X-Git-Url: https://gitweb.ps.run/chirp/blobdiff_plain/d8f54dd8ac187349c1194871a55d4675f28e5a43..c634d6b7851c4bc7901b19b1a6416eb20a367926:/src/main.zig diff --git a/src/main.zig b/src/main.zig index 164606f..259f3da 100644 --- a/src/main.zig +++ b/src/main.zig @@ -97,7 +97,7 @@ const PostListList = db.List(SavedPostList); const UserListList = db.List(SavedUserList); fn parse_enum(comptime E: type, buf: []const u8, base: u8) !E { - return @enumFromInt(try std.fmt.parseUnsigned(@typeInfo(E).Enum.tag_type, buf, base)); + return @enumFromInt(try std.fmt.parseUnsigned(@typeInfo(E).@"enum".tag_type, buf, base)); } // https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding @@ -163,19 +163,18 @@ fn decode(text: []const u8) !std.BoundedArray(u8, 1024) { const Chirp = struct { const PostsPerPage = 10; const UsersPerPage = 10; + var HashBuffer = std.mem.zeroes([1024 * 1024 * 50]u8); pub fn hash_password(password: []const u8) !PasswordHash { var hash_buffer = try PasswordHash.init(128); // TODO: choose buffer size - // TODO: dont allocate on stack, maybe zero memory? - var buffer: [1024 * 10]u8 = undefined; - var alloc = std.heap.FixedBufferAllocator.init(&buffer); + var alloc = std.heap.FixedBufferAllocator.init(&HashBuffer); // TODO: choose limits const result = try std.crypto.pwhash.argon2.strHash(password, .{ .allocator = alloc.allocator(), - .params = std.crypto.pwhash.argon2.Params.fromLimits(1000, 1024), + .params = std.crypto.pwhash.argon2.Params.owasp_2id, }, hash_buffer.slice()); try hash_buffer.resize(result.len); @@ -184,8 +183,7 @@ const Chirp = struct { } pub fn verify_password(password: []const u8, hash: PasswordHash) bool { - var buffer: [1024 * 10]u8 = undefined; - var alloc = std.heap.FixedBufferAllocator.init(&buffer); + var alloc = std.heap.FixedBufferAllocator.init(&HashBuffer); if (std.crypto.pwhash.argon2.strVerify(hash.constSlice(), password, .{ .allocator = alloc.allocator(), @@ -467,7 +465,7 @@ pub fn Paginate(comptime T: type) type { .starting_idx = it.idx, }; } - pub fn next(self: *Self) IterateResult { + pub fn next(self: *Self) ?IterateResult { if (self.it.next()) |kv| { if (self.count < self.per_page) { self.count += 1; @@ -509,7 +507,7 @@ fn html_form(res: *http.Response, action: []const u8, inputs: anytype) !void { inline for (inputs) |input| { switch (@typeInfo(@TypeOf(input))) { - .Struct => |s| { + .@"struct" => |s| { if (s.fields.len == 3) { try res.write("<{s} ", .{input[0]}); try res.write(input[1], input[2]); @@ -601,6 +599,10 @@ fn write_start(res: *http.Response) !void { \\ form { \\ display: inline-block; \\ } + \\ body { + \\ margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;color:#444;padding:0 10px; + \\ } + \\ h1,h2,h3{line-height:1.2} \\ \\ \\ @@ -716,7 +718,7 @@ fn write_post(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, post_id: Po try res.write("

", .{}); try html_form(res, "/comment", .{ .{ "type=\"hidden\" value=\"{x}\" name=\"post_id\"", .{@intFromEnum(post.id)} }, - "type=\"text\" name=\"text\" placeholder=\"Text\"", + .{ "textarea", "type=\"text\" name=\"text\" placeholder=\"Text\"", .{} }, "type=\"submit\" value=\"Comment\"", }); try res.write("
", .{}); @@ -858,6 +860,10 @@ fn write_timeline(res: *http.Response, txn: lmdb.Txn, logged_in: ?Login, user_li var prev_newest_post: ?Post = null; const following = try user_list.open(txn); + if (following.len() == 0) { + try res.write("Empty timeline (no users)", .{}); + return; + } while (true) { var newest_post: ?Post = null; @@ -969,20 +975,20 @@ const GET = struct { fn handle(self: Self) !bool { const ti = @typeInfo(Self); - inline for (ti.Struct.decls) |f_decl| { + inline for (ti.@"struct".decls) |f_decl| { const has_arg = f_decl.name.len > 1 and f_decl.name[f_decl.name.len - 1] == '/'; const match = if (has_arg) std.mem.startsWith(u8, self.req.target, f_decl.name) else std.mem.eql(u8, self.req.target, f_decl.name); if (match) { const f = @field(Self, f_decl.name); const fi = @typeInfo(@TypeOf(f)); - if (fi.Fn.params.len == 1) { + if (fi.@"fn".params.len == 1) { try @call(.auto, f, .{self}); } else { - const arg_type = fi.Fn.params[1].type.?; + const arg_type = fi.@"fn".params[1].type.?; const arg_info = @typeInfo(arg_type); var arg: arg_type = undefined; - const field = arg_info.Struct.fields[0]; + const field = arg_info.@"struct".fields[0]; if (self.req.target.len <= f_decl.name.len) { return error.NoArgProvided; } @@ -990,10 +996,10 @@ const GET = struct { const field_ti = @typeInfo(field.type); switch (field_ti) { // TODO: maybe handle BoundedArray? - .Int => { + .int => { @field(arg, field.name) = try std.fmt.parseUnsigned(field.type, str, 16); }, - .Enum => { + .@"enum" => { @field(arg, field.name) = try parse_enum(field.type, str, 16); }, else => { @@ -1164,7 +1170,7 @@ const GET = struct { try html_form(self.res, "/quote", .{ .{ "type=\"hidden\" name=\"referer\" value=\"{s}\"", .{referer} }, .{ "type=\"hidden\" name=\"post_id\" value=\"{x}\"", .{@intFromEnum(post.id)} }, - "type=\"text\" name=\"text\" placeholder=\"Text\" autofocus", + .{ "textarea", "type=\"text\" name=\"text\" placeholder=\"Text\" autofocus", .{} }, "type=\"submit\" value=\"Quote\"", }); try self.res.write("
", .{}); @@ -1250,7 +1256,7 @@ const GET = struct { try html_form(self.res, "/post", .{ .{ "type=\"hidden\" name=\"referer\" value=\"{s}\"", .{referer} }, - "type=\"text\" name=\"text\" placeholder=\"Text\" autofocus", + .{ "textarea", "type=\"text\" name=\"text\" placeholder=\"Text\" autofocus", .{} }, "type=\"submit\" value=\"Post\"", }); } else { @@ -1305,24 +1311,24 @@ const POST = struct { pub fn handle(self: Self) !bool { const ti = @typeInfo(Self); - inline for (ti.Struct.decls) |f_decl| { + inline for (ti.@"struct".decls) |f_decl| { if (std.mem.eql(u8, f_decl.name, self.req.target)) { const f = @field(Self, f_decl.name); const fi = @typeInfo(@TypeOf(f)); - if (fi.Fn.params.len == 1) { + if (fi.@"fn".params.len == 1) { _ = try @call(.auto, f, .{self}); } else { - const args_type = fi.Fn.params[fi.Fn.params.len - 1].type.?; + const args_type = fi.@"fn".params[fi.@"fn".params.len - 1].type.?; const argsi = @typeInfo(args_type); var args: args_type = undefined; - inline for (argsi.Struct.fields) |field| { + inline for (argsi.@"struct".fields) |field| { const str = self.req.get_value(field.name) orelse return error.ArgNotFound; const field_ti = @typeInfo(field.type); switch (field_ti) { - .Int => { + .int => { @field(args, field.name) = try std.fmt.parseUnsigned(field.type, str, 16); }, - .Enum => { + .@"enum" => { @field(args, field.name) = try parse_enum(field.type, str, 16); }, else => {