X-Git-Url: https://gitweb.ps.run/chirp/blobdiff_plain/c634d6b7851c4bc7901b19b1a6416eb20a367926..1f167a59278b568b88fd70b3f96c7570541638cb:/src/main.zig diff --git a/src/main.zig b/src/main.zig index 259f3da..24bdc51 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,18 +6,34 @@ const http = @import("http"); // db {{{ const Db = struct { - fn users(txn: lmdb.Txn) !db.Db(UserId, User) { - return try db.Db(UserId, User).init(txn, "users"); - } - fn user_ids(txn: lmdb.Txn) !db.Db(Username, UserId) { - return try db.Db(Username, UserId).init(txn, "user_ids"); - } - fn sessions(txn: lmdb.Txn) !db.Db(SessionToken, UserId) { - return try db.Db(SessionToken, UserId).init(txn, "sessions"); - } - fn posts(txn: lmdb.Txn) !db.Db(PostId, Post) { - return try db.Db(PostId, Post).init(txn, "posts"); + pub fn init(env: lmdb.Env) !void { + const txn = try env.txn(); + const dbi = try txn.dbi(null); + if (try dbi.has(1001)) { + users = try dbi.get(1001, @TypeOf(users)); + } else { + users = try @TypeOf(users).init(txn); + } + if (try dbi.has(1002)) { + user_ids = try dbi.get(1002, @TypeOf(user_ids)); + } else { + user_ids = try @TypeOf(user_ids).init(txn); + } + if (try dbi.has(1003)) { + sessions = try dbi.get(1003, @TypeOf(sessions)); + } else { + sessions = try @TypeOf(sessions).init(txn); + } + if (try dbi.has(1004)) { + posts = try dbi.get(1004, @TypeOf(posts)); + } else { + posts = try @TypeOf(posts).init(txn); + } } + var users: UserList = undefined; + var user_ids: UsernameList = undefined; + var sessions: SessionList = undefined; + var posts: PostList = undefined; }; // }}} @@ -32,10 +48,10 @@ const User = struct { description: UserDescription, password_hash: PasswordHash, - posts: PostList, + posts: PostSet, - following: UserList, - followers: UserList, + following: UserSet, + followers: UserSet, post_lists: PostListList, feeds: UserListList, @@ -52,19 +68,19 @@ const Post = struct { upvotes: u64 = 0, downvotes: u64 = 0, votes: VoteList, - comments: PostList, - quotes: PostList, + comments: PostSet, + quotes: PostSet, text: PostText, }; const SavedPostList = struct { name: Name, - list: PostList, + list: PostSet, }; const SavedUserList = struct { name: Name, - list: UserList, + list: UserSet, }; const Vote = struct { @@ -90,8 +106,12 @@ const PasswordHash = std.BoundedArray(u8, 128); const SessionToken = u64; const CookieValue = std.BoundedArray(u8, 128); const PostText = std.BoundedArray(u8, 1024); -const PostList = db.Set(PostId); -const UserList = db.Set(UserId); +const PostSet = db.Set(PostId); +const UserSet = db.Set(UserId); +const PostList = db.SetList(PostId, Post); +const UserList = db.SetList(UserId, User); +const UsernameList = db.SetList(Username, UserId); +const SessionList = db.SetList(SessionToken, UserId); const VoteList = db.SetList(UserId, Vote); const PostListList = db.List(SavedPostList); const UserListList = db.List(SavedUserList); @@ -204,28 +224,28 @@ const Chirp = struct { std.debug.print("error registering user: {}\n", .{err}); }; - const users = try Db.users(txn); - const user_ids = try Db.user_ids(txn); + var users = try Db.users.open(txn); + var user_ids = try Db.user_ids.open(txn); if (try user_ids.has(username_array)) { return false; } else { - const user_id = try db.Prng.gen(users.dbi, UserId); + const user_id = try db.Prng.gen(users.base.dbi, UserId); - try users.put(user_id, User{ + try users.append(user_id, User{ .id = user_id, .name = username_array, .display_name = display_name, .description = try UserDescription.init(0), .password_hash = try hash_password(password), - .posts = try PostList.init(txn), - .following = try UserList.init(txn), - .followers = try UserList.init(txn), + .posts = try PostSet.init(txn), + .following = try UserSet.init(txn), + .followers = try UserSet.init(txn), .post_lists = try PostListList.init(txn), .feeds = try UserListList.init(txn), }); - try user_ids.put(username_array, user_id); + try user_ids.append(username_array, user_id); return true; } @@ -241,17 +261,17 @@ const Chirp = struct { const txn = try env.txn(); defer txn.commit() catch {}; - const user_ids = try Db.user_ids(txn); + const user_ids = try Db.user_ids.open(txn); const user_id = try user_ids.get(username_array); std.debug.print("user logging in, id: {}\n", .{user_id}); - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); if (verify_password(password, user.password_hash)) { - const sessions = try Db.sessions(txn); - const session_token = try db.Prng.gen(sessions.dbi, SessionToken); - try sessions.put(session_token, user_id); + var sessions = try Db.sessions.open(txn); + const session_token = try db.Prng.gen(sessions.base.dbi, SessionToken); + try sessions.append(session_token, user_id); return session_token; } else { return error.IncorrectPassword; @@ -262,11 +282,11 @@ const Chirp = struct { const txn = try env.txn(); defer txn.commit() catch {}; - const sessions = try Db.sessions(txn); + var sessions = try Db.sessions.open(txn); try sessions.del(session_token); } - fn append_post(env: lmdb.Env, user_id: UserId, post_list: PostList, parent_id: ?PostId, quote_id: ?PostId, text: []const u8) !PostId { + fn append_post(env: lmdb.Env, user_id: UserId, post_list: PostSet, parent_id: ?PostId, quote_id: ?PostId, text: []const u8) !PostId { var post_id: PostId = undefined; // TODO: do this in one commit @@ -277,7 +297,7 @@ const Chirp = struct { txn = try env.txn(); defer txn.commit() catch {}; - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); post_id = try db.Prng.gen(posts.dbi, PostId); const decoded_text = try reencode(PostText, text); @@ -288,8 +308,8 @@ const Chirp = struct { .user_id = user_id, .time = std.time.timestamp(), .votes = try VoteList.init(txn), - .comments = try PostList.init(txn), - .quotes = try PostList.init(txn), + .comments = try PostSet.init(txn), + .quotes = try PostSet.init(txn), .text = decoded_text, }); } @@ -307,7 +327,7 @@ const Chirp = struct { txn = try env.txn(); defer txn.commit() catch {}; - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); const quote_post = try posts.get(quote_id.?); var quotes = try quote_post.quotes.open(txn); try quotes.append(post_id); @@ -318,7 +338,7 @@ const Chirp = struct { fn post(env: lmdb.Env, user_id: UserId, text: []const u8) !void { var txn = try env.txn(); - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); txn.abort(); @@ -328,10 +348,10 @@ const Chirp = struct { fn comment(env: lmdb.Env, user_id: UserId, parent_post_id: PostId, text: []const u8) !void { var txn = try env.txn(); - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); const parent_post = try posts.get(parent_post_id); txn.abort(); @@ -345,7 +365,7 @@ const Chirp = struct { fn quote(env: lmdb.Env, user_id: UserId, quote_post_id: PostId, text: []const u8) !void { var txn = try env.txn(); - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); txn.abort(); @@ -357,7 +377,7 @@ const Chirp = struct { const txn = try env.txn(); defer txn.commit() catch {}; - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); var p = try posts.get(post_id); var votes_view = try p.votes.open(txn); @@ -397,7 +417,7 @@ const Chirp = struct { const txn = try env.txn(); defer txn.commit() catch {}; - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); const user_to_follow = try users.get(user_id_to_follow); @@ -420,7 +440,7 @@ const Chirp = struct { const txn = try env.txn(); defer txn.abort(); - const sessions = try Db.sessions(txn); + const sessions = try Db.sessions.open(txn); return try sessions.get(session_token); } @@ -429,7 +449,7 @@ const Chirp = struct { const txn = try env.txn(); defer txn.abort(); - const users = try Db.users(txn); + const users = try Db.users.open(txn); return try users.get(user_id); } }; @@ -612,12 +632,12 @@ fn write_end(res: *http.Response) !void { try res.write("", .{}); } fn write_post(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_id: PostId, options: struct { recurse: u8 = 0, show_comment_field: bool = false }) !void { - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); const post = posts.get(post_id) catch { res.redirect("/") catch {}; return; }; - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(post.user_id); try res.write( @@ -829,17 +849,17 @@ fn write_profile(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user: Us try res.write("
", .{}); } -fn write_posts(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_list: PostList, options: struct { +fn write_posts(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_list: PostSet, options: struct { show_posts: bool, show_quotes: bool, show_comments: bool, }) !void { const posts_view = try post_list.open(txn); - var paginate = try Paginate(PostList).init(res, posts_view, Chirp.PostsPerPage); + var paginate = try Paginate(PostSet).init(res, posts_view, Chirp.PostsPerPage); while (paginate.next()) |post_id| { - const posts = try Db.posts(txn); + const posts = try Db.posts.open(txn); const post = try posts.get(post_id.key); if ((options.show_posts and (post.parent_id == null and post.quote_id == null)) or (options.show_quotes and (post.quote_id != null)) or @@ -852,9 +872,10 @@ fn write_posts(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_list: try paginate.write_navigation(); } -fn write_timeline(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user_list: UserList) !void { - const users = try Db.users(txn); - const posts = try Db.posts(txn); +fn write_timeline(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user_list: UserSet) !void { + // TODO: paginate + const users = try Db.users.open(txn); + const posts = try Db.posts.open(txn); var newest_post_ids = try std.BoundedArray(PostId, 10).init(0); // TODO: TimelinePostsCount var prev_newest_post: ?Post = null; @@ -900,8 +921,21 @@ fn write_timeline(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user_li try res.write("
", .{}); } } +fn write_frontpage(res: *http.Response, txn: lmdb.Txn) !void { + const posts = try Db.posts.open(txn); + var counter: u64 = 0; + var it = try posts.reverse_iterator(); + while (it.next()) |p| { + if (p.val.parent_id == null and p.val.quote_id == null) { + try write_post(res, txn, null, p.key, .{ .recurse = 1 }); + counter += 1; + } + + if (counter >= 10) break; + } +} fn write_user(res: *http.Response, txn: lmdb.Txn, user_id: UserId) !void { - const users = try Db.users(txn); + const users = try Db.users.open(txn); const user = try users.get(user_id); try res.write( \\{s} @@ -941,7 +975,7 @@ fn check_login(env: lmdb.Env, req: http.Request, res: *http.Response) !?Login { if (Chirp.get_session_user_id(env, session_token) catch null) |user_id| { const txn = try env.txn(); defer txn.abort(); - const users = try Db.users(txn); + const users = try Db.users.open(txn); result = .{ .user = try users.get(user_id), @@ -1016,9 +1050,9 @@ const GET = struct { } pub fn @"/user/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); try write_profile(self.res, self.txn, self.logged_in, user); @@ -1035,9 +1069,9 @@ const GET = struct { } } pub fn @"/comments/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); try write_profile(self.res, self.txn, self.logged_in, user); @@ -1054,9 +1088,9 @@ const GET = struct { } } pub fn @"/quotes/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); try write_profile(self.res, self.txn, self.logged_in, user); @@ -1073,9 +1107,9 @@ const GET = struct { } } pub fn @"/all/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); try write_profile(self.res, self.txn, self.logged_in, user); @@ -1092,14 +1126,14 @@ const GET = struct { } } pub fn @"/following/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); const following_view = try user.following.open(self.txn); - var paginate = try Paginate(UserList).init(self.res, following_view, Chirp.UsersPerPage); + var paginate = try Paginate(UserSet).init(self.res, following_view, Chirp.UsersPerPage); try self.res.write( \\

{s} follows:

@@ -1121,13 +1155,13 @@ const GET = struct { } } pub fn @"/followers/"(self: Self, args: struct { username: []const u8 }) !void { - const user_ids = try Db.user_ids(self.txn); + const user_ids = try Db.user_ids.open(self.txn); if (user_ids.get(try Username.fromSlice(args.username))) |user_id| { - const users = try Db.users(self.txn); + const users = try Db.users.open(self.txn); const user = try users.get(user_id); const followers_view = try user.followers.open(self.txn); - var paginate = try Paginate(UserList).init(self.res, followers_view, Chirp.UsersPerPage); + var paginate = try Paginate(UserSet).init(self.res, followers_view, Chirp.UsersPerPage); try self.res.write( \\

{s} followers:

@@ -1155,13 +1189,13 @@ const GET = struct { }); } pub fn @"/upvotes/"(self: Self, args: struct { post_id: PostId }) !void { - const posts = try Db.posts(self.txn); + const posts = try Db.posts.open(self.txn); const post = try posts.get(args.post_id); try self.res.write("{} upvotes:
", .{post.upvotes}); try write_votes(self.res, self.txn, post.votes, .{}); } pub fn @"/quoted/"(self: Self, args: struct { post_id: PostId }) !void { - const posts = try Db.posts(self.txn); + const posts = try Db.posts.open(self.txn); const post = try posts.get(args.post_id); const referer = if (self.req.get_header("Referer")) |ref| ref else self.req.target; @@ -1183,8 +1217,8 @@ const GET = struct { .show_comments = false, }); } - pub fn @"/list/"(self: Self, args: struct { list_id: PostList.Base.Index }) !void { - try write_posts(self.res, self.txn, self.logged_in, PostList{ .base = .{ .idx = args.list_id } }, .{ + pub fn @"/list/"(self: Self, args: struct { list_id: PostSet.Base.Index }) !void { + try write_posts(self.res, self.txn, self.logged_in, PostSet{ .base = .{ .idx = args.list_id } }, .{ .show_posts = true, .show_quotes = true, .show_comments = true, @@ -1218,8 +1252,8 @@ const GET = struct { try self.res.write("not logged in", .{}); } } - pub fn @"/feed/"(self: Self, args: struct { feed_id: UserList.Base.Index }) !void { - try write_timeline(self.res, self.txn, self.logged_in, UserList{ .base = .{ .idx = args.feed_id } }); + pub fn @"/feed/"(self: Self, args: struct { feed_id: UserSet.Base.Index }) !void { + try write_timeline(self.res, self.txn, self.logged_in, UserSet{ .base = .{ .idx = args.feed_id } }); } pub fn @"/feeds"(self: Self) !void { if (self.logged_in) |login| { @@ -1295,6 +1329,7 @@ const GET = struct { } else { // TODO: generic home try self.res.write("Homepage", .{}); + // try write_frontpage(self.res, self.txn); } } }; @@ -1379,13 +1414,13 @@ const POST = struct { const txn = try self.env.txn(); defer txn.commit() catch {}; - const user_ids = try Db.user_ids(txn); + var user_ids = try Db.user_ids.open(txn); if (!try user_ids.has(username)) { try user_ids.del(login.user.name); try user_ids.put(username, login.user.id); - const users = try Db.users(txn); + const users = try Db.users.open(txn); var user = login.user; user.name = username; try users.put(login.user.id, user); @@ -1398,7 +1433,7 @@ const POST = struct { const txn = try self.env.txn(); defer txn.commit() catch {}; - const users = try Db.users(txn); + const users = try Db.users.open(txn); var user = login.user; user.display_name = display_name; try users.put(login.user.id, user); @@ -1410,7 +1445,7 @@ const POST = struct { const txn = try self.env.txn(); defer txn.commit() catch {}; - const users = try Db.users(txn); + const users = try Db.users.open(txn); var user = login.user; user.description = description; try users.put(login.user.id, user); @@ -1421,7 +1456,7 @@ const POST = struct { const txn = try self.env.txn(); defer txn.commit() catch {}; - const users = try Db.users(txn); + const users = try Db.users.open(txn); var user = login.user; user.password_hash = try Chirp.hash_password(args.password); try users.put(login.user.id, user); @@ -1472,7 +1507,7 @@ const POST = struct { // TODO: decode name var txn = try self.env.txn(); - const postlist = try PostList.init(txn); + const postlist = try PostSet.init(txn); try txn.commit(); txn = try self.env.txn(); @@ -1481,9 +1516,9 @@ const POST = struct { try txn.commit(); } } - pub fn @"/delete_list"(self: Self, args: struct { list_id: PostList.Base.Index }) !void { + pub fn @"/delete_list"(self: Self, args: struct { list_id: PostSet.Base.Index }) !void { if (self.logged_in) |login| { - var post_list: ?PostList = null; + var post_list: ?PostSet = null; { const txn = try self.env.txn(); defer txn.commit() catch {}; @@ -1499,14 +1534,14 @@ const POST = struct { } } } - pub fn @"/list_add"(self: Self, args: struct { list_id: PostList.Base.Index, post_id: PostId }) !void { + pub fn @"/list_add"(self: Self, args: struct { list_id: PostSet.Base.Index, post_id: PostId }) !void { if (self.logged_in) |login| { _ = login; const txn = try self.env.txn(); defer txn.commit() catch {}; - const post_list = PostList{ .base = .{ .idx = args.list_id } }; + const post_list = PostSet{ .base = .{ .idx = args.list_id } }; var post_list_view = try post_list.open(txn); if (try post_list_view.has(args.post_id)) { try post_list_view.del(args.post_id); @@ -1521,7 +1556,7 @@ const POST = struct { const name = try Name.fromSlice(name_str); var txn = try self.env.txn(); - const userlist = try UserList.init(txn); + const userlist = try UserSet.init(txn); try txn.commit(); txn = try self.env.txn(); @@ -1530,9 +1565,9 @@ const POST = struct { try txn.commit(); } } - pub fn @"/delete_feed"(self: Self, args: struct { list_id: UserList.Base.Index }) !void { + pub fn @"/delete_feed"(self: Self, args: struct { list_id: UserSet.Base.Index }) !void { if (self.logged_in) |login| { - var user_list: ?UserList = null; + var user_list: ?UserSet = null; { const txn = try self.env.txn(); @@ -1549,14 +1584,14 @@ const POST = struct { } } } - pub fn @"/feed_add"(self: Self, args: struct { feed_id: UserList.Base.Index, user_id: UserId }) !void { + pub fn @"/feed_add"(self: Self, args: struct { feed_id: UserSet.Base.Index, user_id: UserId }) !void { if (self.logged_in) |login| { _ = login; const txn = try self.env.txn(); defer txn.commit() catch {}; - const user_list = UserList{ .base = .{ .idx = args.feed_id } }; + const user_list = UserSet{ .base = .{ .idx = args.feed_id } }; var user_list_view = try user_list.open(txn); if (try user_list_view.has(args.user_id)) { try user_list_view.del(args.user_id); @@ -1605,8 +1640,8 @@ fn list_users(env: lmdb.Env) !void { const txn = try env.txn(); defer txn.abort(); - const users = try Db.users(txn); - var it = try users.iterator(); + const users = try Db.users.open(txn); + var it = users.iterator(); while (it.next()) |kv| { const key = kv.key; @@ -1618,8 +1653,8 @@ fn list_user_ids(env: lmdb.Env) !void { const txn = try env.txn(); defer txn.abort(); - const user_ids = try Db.user_ids(txn); - var it = try user_ids.iterator(); + const user_ids = try Db.user_ids.open(txn); + var it = user_ids.iterator(); while (it.next()) |kv| { const key = kv.key; @@ -1632,8 +1667,8 @@ fn list_sessions(env: lmdb.Env) !void { const txn = try env.txn(); defer txn.abort(); - const sessions = try Db.sessions(txn); - var it = try sessions.iterator(); + const sessions = try Db.sessions.open(txn); + var it = sessions.iterator(); while (it.next()) |kv| { const key = kv.key; @@ -1646,8 +1681,8 @@ fn list_posts(env: lmdb.Env) !void { const txn = try env.txn(); defer txn.abort(); - const posts = try Db.posts(txn); - var it = try posts.iterator(); + const posts = try Db.posts.open(txn); + var it = posts.iterator(); while (it.next()) |kv| { const key = kv.key; @@ -1674,6 +1709,8 @@ pub fn main() !void { var env = try lmdb.Env.open("db", 1024 * 1024 * 10); defer env.close(); + try Db.init(env); + std.debug.print("Users:\n", .{}); try list_users(env); std.debug.print("User IDs:\n", .{});