From 3f18f02d07802d1fc705a500e5978a9b3cb2e751 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Fri, 14 Nov 2025 21:55:59 +0100 Subject: implement login --- src/routes/handler-info.zig | 113 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/routes/handler-info.zig (limited to 'src/routes/handler-info.zig') diff --git a/src/routes/handler-info.zig b/src/routes/handler-info.zig new file mode 100644 index 0000000..bac7612 --- /dev/null +++ b/src/routes/handler-info.zig @@ -0,0 +1,113 @@ +const std = @import("std"); +const Context = @import("context.zig"); +const Storage = @import("../storage/root.zig"); + +const log = std.log.scoped(.handler_info); + +handler: *const fn (*Context) anyerror![]const u8, +needs_auth: bool, +method: std.http.Method, + +pub fn handle( + self: *const @This(), + request: *std.http.Server.Request, + storage: *Storage, + allocator: std.mem.Allocator, +) !void { + if (request.head.method != self.method) { + return request.respond( + "{ \"error\": \"Bad Request\" }", + .{ .status = .bad_request } + ); + } + + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + var context: Context = .{ + .request = request, + .storage = storage, + .allocator = arena.allocator(), + }; + + const response = self.handler(&context) catch |err| { + const response, const status_code: std.http.Status = switch (err) { + error.BadRequest => .{ "{ \"error\": \"Bad Request\" }", .bad_request }, + error.Unauthorized => .{ "{ \"error\": \"Unauthorized\" }", .unauthorized }, + error.Forbidden => .{ "{ \"error\": \"Forbidden\" }", .forbidden }, + error.NotFound => .{ "{ \"error\": \"Not Found\" }", .not_found }, + else => blk: { + log.err("handler for '{s}' returned {}", .{request.head.target, err}); + break :blk .{ "{ \"error\": \"Internal Server Error\" }", .internal_server_error }; + }, + }; + + return request.respond(response, .{ .status = status_code }); + }; + + try request.respond(response, .{ + .extra_headers = &.{ + .{ + .name = "Content-Type", + .value = context.response.headers.content_type + }, + }, + }); +} + +pub fn from_type(T: type) @This() { + const info = @typeInfo(@TypeOf(T.handler)); + const return_type = info.@"fn".return_type orelse void; + const payload_type = @typeInfo(return_type).error_union.payload; + + const Handler = type: { + break :type struct { + pub fn call(ctx: *Context) anyerror![]const u8 { + const args = args: { + const tuple = std.meta.fields(std.meta.ArgsTuple(@TypeOf(T.handler))); + + if (tuple.len == 1) { + break :args .{ ctx }; + } else if (tuple.len == 2) { + const Body = tuple[1].@"type"; + + var writer = std.Io.Writer.Allocating.init(ctx.allocator); + const interface = &writer.writer; + + var buffer: [1024]u8 = undefined; + const reader = try ctx.request.readerExpectContinue(&buffer); + + try reader.streamExact64(interface, ctx.request.head.content_length orelse 0); + + const body = std.json.parseFromSliceLeaky( + Body, + ctx.allocator, + writer.written(), + .{} + ) catch return error.BadRequest; + break :args .{ ctx, body }; + } else { + @compileError("invalid amount of arguments for request function"); + } + }; + + if (payload_type == []const u8) { + return @call(.auto, T.handler, args); + } else if (payload_type == void) { + try @call(.auto, T.handler, args); + return ""; + } else { + var writer = std.Io.Writer.Allocating.init(ctx.allocator); + var stringify = std.json.Stringify { .writer = &writer.writer }; + try stringify.write(try @call(.auto, T.handler, args)); + return writer.written(); + } + } + }; + }; + + return @This() { + .handler = Handler.call, + .needs_auth = T.needs_auth, + .method = T.method, + }; +} -- cgit v1.2.3-70-g09d2