const std = @import("std"); const util = @import("util"); const controllers = @import("../controllers.zig"); pub const routes = .{ controllers.apiEndpoint(index), controllers.apiEndpoint(about), controllers.apiEndpoint(login), controllers.apiEndpoint(global_timeline), controllers.apiEndpoint(cluster.overview), controllers.apiEndpoint(media), controllers.apiEndpoint(static), }; const static = struct { pub const path = "/static/:path*"; pub const method = .GET; pub const Args = struct { path: []const u8, }; pub fn handler(req: anytype, res: anytype, _: anytype) !void { if (std.mem.indexOf(u8, req.args.path, "..") != null) return error.NotFound; std.log.debug("opening {s}", .{req.args.path}); var dir = try std.fs.cwd().openDir("static", .{}); defer dir.close(); var file = dir.openFile(req.args.path, .{}) catch |err| switch (err) { error.FileNotFound => return error.NotFound, else => |e| return e, }; defer file.close(); var stream = try res.open(.ok); defer stream.close(); var buf: [1 << 16]u8 = undefined; while (true) { const count = try file.reader().readAll(&buf); if (count == 0) break; std.log.debug("writing {} bytes from {s}", .{ count, req.args.path }); try stream.writer().writeAll(buf[0..count]); } try stream.finish(); } }; const index = struct { pub const path = "/"; pub const method = .GET; pub fn handler(_: anytype, res: anytype, srv: anytype) !void { if (srv.user_id == null) { try res.headers.put("Location", about.path); return res.status(.see_other); } try res.template(.ok, srv, "Hello", .{}); } }; const about = struct { pub const path = "/about"; pub const method = .GET; pub fn handler(_: anytype, res: anytype, srv: anytype) !void { try res.template(.ok, srv, tmpl, .{}); } const tmpl = @embedFile("./web/about.tmpl.html"); }; const login = struct { pub const path = "/login"; pub const method = .POST; pub const Body = struct { username: []const u8, password: []const u8, }; pub fn handler(req: anytype, res: anytype, srv: anytype) !void { const token = try srv.login(req.body.username, req.body.password); try res.headers.put("Location", index.path); var buf: [64]u8 = undefined; const cookie_name = try std.fmt.bufPrint(&buf, "token.{s}", .{req.body.username}); try res.headers.setCookie(cookie_name, token.token, .{}); try res.headers.setCookie("active_account", req.body.username, .{ .HttpOnly = false }); try res.status(.see_other); } }; const global_timeline = struct { pub const path = "/timelines/global"; pub const method = .GET; pub fn handler(req: anytype, res: anytype, srv: anytype) !void { _ = req; const timeline = try srv.globalTimeline(.{}); try res.template(.ok, srv, @embedFile("./web/timelines/global.tmpl.html"), .{ .notes = timeline.items, .community = srv.community, }); } }; const cluster = struct { const overview = struct { pub const path = "/cluster/overview"; pub const method = .GET; pub fn handler(_: anytype, res: anytype, srv: anytype) !void { const meta = try srv.getClusterMeta(); try res.template(.ok, srv, @embedFile("./web/cluster/overview.tmpl.html"), .{ .community = srv.community, .meta = meta, }); } }; }; const media = struct { pub const path = "/media/:id"; pub const method = .GET; pub const Args = struct { id: util.Uuid, }; pub fn handler(req: anytype, res: anytype, srv: anytype) !void { const result = try srv.fileDereference(req.args.id); defer util.deepFree(srv.allocator, result); try res.headers.put("Content-Type", result.meta.content_type orelse "application/octet-stream"); var stream = try res.open(.ok); defer stream.close(); try stream.writer().writeAll(result.data); try stream.finish(); } };