Compare commits

...

8 Commits

Author SHA1 Message Date
Bob Farrell
72d6eebab8 WIP 2025-02-08 16:16:58 +00:00
Bob Farrell
960840895e WIP 2025-02-08 15:17:30 +00:00
Bob Farrell
136aed3d54 WIP 2025-02-08 15:16:40 +00:00
Bob Farrell
36f9d32ae6 WIP 2025-02-08 15:16:18 +00:00
Bob Farrell
a18ba77faf WIP 2025-02-08 15:16:18 +00:00
Bob Farrell
33358fa5fa WIP 2025-02-08 15:16:18 +00:00
Bob Farrell
342924b8c0 WIP 2025-02-08 15:16:16 +00:00
Bob Farrell
7b43e87399 WIP 2025-02-08 15:15:39 +00:00
15 changed files with 200 additions and 87 deletions

View File

@ -36,6 +36,7 @@ pub fn build(b: *std.Build) !void {
.{
.target = target,
.optimize = optimize,
.use_llvm = b.option(bool, "use_llvm", "Use LLVM") orelse false,
.zmpl_templates_paths = templates_paths,
.zmpl_auto_build = false,
.zmpl_markdown_fragments = try generateMarkdownFragments(b),
@ -46,6 +47,11 @@ pub fn build(b: *std.Build) !void {
},
);
const zmpl_steps = zmpl_dep.builder.top_level_steps;
const zmpl_compile_step = zmpl_steps.get("compile").?;
const compile_step = b.step("compile", "Compile Zmpl templates");
compile_step.dependOn(&zmpl_compile_step.step);
const zmpl_module = zmpl_dep.module("zmpl");
const jetkv_dep = b.dependency("jetkv", .{ .target = target, .optimize = optimize });
@ -132,6 +138,8 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
const target = exe.root_module.resolved_target orelse @panic("Unable to detect compile target.");
const optimize = exe.root_module.optimize orelse .Debug;
exe.use_llvm = exe.use_llvm orelse (optimize != .Debug);
if (optimize != .Debug) exe.linkLibC();
const environment = b.option(
@ -197,22 +205,25 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
}
}
const root_path = b.build_root.path orelse try std.fs.cwd().realpathAlloc(b.allocator, ".");
const root_path = if (b.build_root.path) |build_root_path|
try std.fs.path.join(b.allocator, &.{ build_root_path, "src" })
else
try std.fs.cwd().realpathAlloc(b.allocator, "src");
const templates_path: []const u8 = try std.fs.path.join(
b.allocator,
&[_][]const u8{ root_path, "src", "app" },
&[_][]const u8{ root_path, "app" },
);
const views_path: []const u8 = try std.fs.path.join(
b.allocator,
&[_][]const u8{ root_path, "src", "app", "views" },
&[_][]const u8{ root_path, "app", "views" },
);
const jobs_path = try std.fs.path.join(
b.allocator,
&[_][]const u8{ root_path, "src", "app", "jobs" },
&[_][]const u8{ root_path, "app", "jobs" },
);
const mailers_path = try std.fs.path.join(
b.allocator,
&[_][]const u8{ root_path, "src", "app", "mailers" },
&[_][]const u8{ root_path, "app", "mailers" },
);
const exe_routes_file = b.addExecutable(.{
@ -220,6 +231,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
.root_source_file = jetzig_dep.path("src/routes_file.zig"),
.target = target,
.optimize = optimize,
.use_llvm = exe.use_llvm,
});
exe_routes_file.root_module.addImport("jetzig", jetzig_module);
@ -228,7 +240,11 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
exe_routes_file.root_module.addImport("zmpl", zmpl_module);
const run_routes_file_cmd = b.addRunArtifact(exe_routes_file);
run_routes_file_cmd.has_side_effects = true;
const source_files = b.addUpdateSourceFiles();
const routes_file_path = run_routes_file_cmd.addOutputFileArg("routes.zig");
source_files.addCopyFileToSource(routes_file_path, "src/routes.zig");
run_routes_file_cmd.addArgs(&.{
root_path,
b.pathFromRoot("src"),
@ -238,15 +254,17 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
mailers_path,
});
const routes_module = b.createModule(.{ .root_source_file = routes_file_path });
const routes_module = b.createModule(.{ .root_source_file = b.path("src/routes.zig") });
routes_module.addImport("jetzig", jetzig_module);
exe.root_module.addImport("routes", routes_module);
exe.step.dependOn(&run_routes_file_cmd.step);
const exe_static_routes = b.addExecutable(.{
.name = "static",
.root_source_file = jetzig_dep.path("src/compile_static_routes.zig"),
.target = target,
.optimize = optimize,
.use_llvm = exe.use_llvm,
});
const main_module = b.createModule(.{ .root_source_file = b.path("src/main.zig") });
@ -311,6 +329,12 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
run_tests_file_cmd.addFileArg(.{ .src_path = .{ .owner = b, .sub_path = sub_path } });
}
const jetzig_steps = jetzig_dep.builder.top_level_steps;
const jetzig_compile_step = jetzig_steps.get("compile").?;
const compile_step = b.step("compile", "Compile Zmpl templates");
compile_step.dependOn(&jetzig_compile_step.step);
jetzig_compile_step.step.dependOn(&source_files.step);
const exe_unit_tests = b.addTest(.{
.root_source_file = tests_file_path,
.target = target,
@ -355,6 +379,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
.root_source_file = jetzig_dep.path("src/commands/routes.zig"),
.target = target,
.optimize = optimize,
.use_llvm = exe.use_llvm,
});
const auth_user_create_step = b.step("jetzig:auth:user:create", "List all routes in your app");
@ -363,6 +388,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
.root_source_file = jetzig_dep.path("src/commands/auth.zig"),
.target = target,
.optimize = optimize,
.use_llvm = exe.use_llvm,
});
exe_auth.root_module.addImport("jetquery", jetquery_module);
exe_auth.root_module.addImport("jetzig", jetzig_module);
@ -384,6 +410,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
.root_source_file = jetzig_dep.path("src/commands/database.zig"),
.target = target,
.optimize = optimize,
.use_llvm = exe.use_llvm,
});
exe_database.root_module.addImport("jetquery", jetquery_module);
exe_database.root_module.addImport("jetzig", jetzig_module);

View File

@ -14,13 +14,19 @@
.url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz",
.hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248",
},
.zmpl = .{
.url = "https://github.com/jetzig-framework/zmpl/archive/a249c7a34328d16a6e52f24d8c551f29b4f05ec0.tar.gz",
.hash = "1220454ad7214ff46f6893f95854e29721bdd66a2ec590ff0a214ce35d57131a9d28",
},
.jetquery = .{
.path = "../jetquery",
},
.jetcommon = .{
.url = "https://github.com/jetzig-framework/jetcommon/archive/5be57d534b3d469f5570cd4b373b8d61032b1b8b.tar.gz",
.hash = "122079c6ceb28fa93163c2f95e2f175bb8f93f3075fa34af63045671ab7dd824e756",
},
.pg = .{
.url = "https://github.com/karlseguin/pg.zig/archive/0110cfdf387403a5a326115b5184861c4604d711.tar.gz",
.hash = "12205019ce2bc2e08c76352ea37a14600d412e5e0ecdd7ddd27b4e83a62f37d8ba94",
.path = "../pg.zig",
},
.httpz = .{
.url = "https://github.com/karlseguin/http.zig/archive/a691d731047e9a5a79d71ac594cb8f5fad1d0705.tar.gz",
@ -30,14 +36,14 @@
.url = "https://github.com/karlseguin/smtp_client.zig/archive/5163c66cc42cdd93176a6b1cad45f3db3a291a6a.tar.gz",
.hash = "1220a7807b5161550cb0cba772689c4872bfeee8305a26c3cd0e12a8ccde1d546910",
},
.smtp_client = .{
.url = "https://github.com/karlseguin/smtp_client.zig/archive/5163c66cc42cdd93176a6b1cad45f3db3a291a6a.tar.gz",
.hash = "1220a7807b5161550cb0cba772689c4872bfeee8305a26c3cd0e12a8ccde1d546910",
},
.jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/d4010cfd9ced2e7deb0f3a6cc64e2d32b8db95ba.tar.gz",
.hash = "122069eeb0d43931e49f93419bdb5930ac3a6bc35d1e977738fe872ecaac8ff32aec",
},
.zmpl = .{
.url = "https://github.com/jetzig-framework/zmpl/archive/b1dfca8eec73520af5b029016c5b5914da659b6d.tar.gz",
.hash = "1220e70c218c89de219d4f9506a4ad69bd1b5257cd8c7cdc2ea823830e1d8b9dc4df",
},
},
.paths = .{

View File

@ -9,8 +9,7 @@
.hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248",
},
.jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/d4010cfd9ced2e7deb0f3a6cc64e2d32b8db95ba.tar.gz",
.hash = "122069eeb0d43931e49f93419bdb5930ac3a6bc35d1e977738fe872ecaac8ff32aec",
.path = "../../jetquery",
},
},
.paths = .{

View File

@ -149,7 +149,7 @@ pub fn run(
const tmpdir_real_path = try tmpdir.realpathAlloc(allocator, ".");
defer allocator.free(tmpdir_real_path);
try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path });
try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path }, .{});
switch (builtin.os.tag) {
.windows => {},
@ -215,7 +215,7 @@ fn zig_build_install(allocator: std.mem.Allocator, path: []const u8, options: Op
defer project_dir.close();
project_dir.makePath(".bundle") catch {};
try util.runCommandInDir(allocator, install_argv.items, .{ .path = path });
try util.runCommandInDir(allocator, install_argv.items, .{ .path = path }, .{});
const install_bin_path = try std.fs.path.join(allocator, &[_][]const u8{ ".bundle", "bin" });
defer allocator.free(install_bin_path);

View File

@ -202,6 +202,7 @@ pub fn run(
github_url,
},
.{ .dir = install_dir },
.{},
);
// TODO: Use arg or interactive prompt to do Git setup in net project, default to no.
@ -333,6 +334,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
".",
},
.{ .path = install_dir },
.{},
);
try util.runCommandInDir(
@ -343,6 +345,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
".",
},
.{ .path = install_dir },
.{},
);
try util.runCommandInDir(
@ -354,5 +357,6 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
"Initialize Jetzig project",
},
.{ .path = install_dir },
.{},
);
}

View File

@ -4,12 +4,13 @@ const args = @import("args");
const util = @import("../util.zig");
pub const watch_changes_pause_duration = 1 * 1000 * 1000 * 1000;
pub const watch_changes_pause_duration = 100 * std.time.ns_per_ms;
/// Command line options for the `server` command.
pub const Options = struct {
reload: bool = true,
debug: bool = true,
incremental: bool = false,
pub const meta = .{
.full_text =
@ -19,10 +20,12 @@ pub const Options = struct {
\\
\\ jetzig server
\\ jetzig server --reload=false --debug=false
\\ jetzig server --incremental=true
,
.option_docs = .{
.reload = "Enable or disable automatic reload on update (default: true)",
.debug = "Enable or disable the development debug console (default: true)",
.incremental = "Enable or disable the incremental compilation (default: false) [experimental]",
},
};
};
@ -51,8 +54,6 @@ pub fn run(
const realpath = try std.fs.realpathAlloc(allocator, ".");
defer allocator.free(realpath);
var mtime = try totalMtime(allocator, cwd, "src");
std.debug.print(
"Launching development server. [reload:{s}]\n",
.{
@ -66,30 +67,51 @@ pub fn run(
try argv.appendSlice(&.{
"zig",
"build",
"--watch",
});
if (options.incremental) try argv.append("-fincremental");
try argv.appendSlice(&.{
util.environmentBuildOption(main_options.options.environment),
"-Djetzig_runner=true",
});
if (options.debug) try argv.append("-Ddebug_console=true");
try argv.appendSlice(&.{
"install",
"--color",
"on",
});
while (true) {
util.runCommandInDir(
allocator,
argv.items,
.{ .path = realpath },
) catch {
std.debug.print("Build failed, waiting for file change...\n", .{});
try awaitFileChange(allocator, cwd, &mtime);
std.debug.print("Changes detected, restarting server...\n", .{});
continue;
};
_ = cwd.createFile("src/routes.zig", .{}) catch {};
var exe_path = try util.locateExecutable(allocator, cwd, .{});
var mtime = if (exe_path) |path| blk: {
const stat = try std.fs.cwd().statFile(path);
break :blk stat.mtime;
} else 0;
util.runCommandInDir(
allocator,
argv.items,
.{ .path = realpath },
.{ .output = .stream, .wait = false },
) catch {
std.debug.print("Build failed, waiting for file change...\n", .{});
std.process.exit(1);
};
var zmpl_thread = try std.Thread.spawn(
.{ .allocator = allocator },
zmplThread,
.{ allocator, cwd },
);
defer zmpl_thread.join();
while (true) {
exe_path = try util.locateExecutable(allocator, cwd, .{});
const exe_path = try util.locateExecutable(allocator, cwd, .{});
if (exe_path == null) {
std.debug.print("Unable to locate compiled executable. Exiting.\n", .{});
std.process.exit(1);
@ -116,21 +138,36 @@ pub fn run(
std.process.exit(term.Exited);
}
// HACK: This currenly doesn't restart the server when it exits, maybe that
// could be implemented in the future.
try awaitFileChange(allocator, cwd, &mtime);
try awaitFileChange(exe_path.?, &mtime);
std.debug.print("Changes detected, restarting server...\n", .{});
_ = try process.kill();
}
}
fn awaitFileChange(allocator: std.mem.Allocator, cwd: std.fs.Dir, mtime: *i128) !void {
fn awaitFileChange(path: []const u8, mtime: *i128) !void {
while (true) {
std.time.sleep(watch_changes_pause_duration);
const new_mtime = try totalMtime(allocator, cwd, "src");
if (new_mtime > mtime.*) {
mtime.* = new_mtime;
const stat = try std.fs.cwd().statFile(path);
if (stat.mtime > mtime.*) {
mtime.* = stat.mtime;
return;
}
}
}
fn zmplThread(allocator: std.mem.Allocator, cwd: std.fs.Dir) void {
while (true) {
awaitDirChange(allocator, cwd, "src/app/views") catch break;
util.runCommand(allocator, &.{ "zig", "build", "compile" }) catch break;
}
}
fn awaitDirChange(allocator: std.mem.Allocator, dir: std.fs.Dir, sub_path: []const u8) !void {
const mtime = try totalMtime(allocator, dir, sub_path);
while (true) {
std.time.sleep(watch_changes_pause_duration);
const new_mtime = try totalMtime(allocator, dir, sub_path);
if (new_mtime > mtime) {
return;
}
}
@ -147,12 +184,12 @@ fn totalMtime(allocator: std.mem.Allocator, cwd: std.fs.Dir, sub_path: []const u
while (try walker.next()) |entry| {
if (entry.kind != .file) continue;
const extension = std.fs.path.extension(entry.path);
// const extension = std.fs.path.extension(entry.path);
if (std.mem.eql(u8, extension, ".zig") or std.mem.eql(u8, extension, ".zmpl")) {
const stat = try dir.statFile(entry.path);
sum += stat.mtime;
}
// if (std.mem.eql(u8, extension, ".zig") or std.mem.eql(u8, extension, ".zmpl")) {
const stat = try dir.statFile(entry.path);
sum += stat.mtime;
// }
}
return sum;

View File

@ -153,7 +153,7 @@ pub fn runCommandStreaming(allocator: std.mem.Allocator, install_path: []const u
pub fn runCommand(allocator: std.mem.Allocator, argv: []const []const u8) !void {
var dir = try detectJetzigProjectDir();
defer dir.close();
try runCommandInDir(allocator, argv, .{ .dir = dir });
try runCommandInDir(allocator, argv, .{ .dir = dir }, .{});
}
const Dir = union(enum) {
@ -161,34 +161,53 @@ const Dir = union(enum) {
dir: std.fs.Dir,
};
pub const RunOptions = struct {
output: enum { stream, capture } = .capture,
wait: bool = true,
};
/// Runs a command as a child process in the given directory and verifies successful exit code.
pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir) !void {
pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir, options: RunOptions) !void {
const cwd_path = switch (dir) {
.path => |capture| capture,
.dir => |capture| try capture.realpathAlloc(allocator, "."),
};
defer if (dir == .dir) allocator.free(cwd_path);
const result = std.process.Child.run(.{
.allocator = allocator,
.argv = argv,
.cwd = cwd_path,
}) catch |err| {
switch (err) {
error.FileNotFound => {
printFailure();
const cmd_str = try std.mem.join(allocator, " ", argv);
defer allocator.free(cmd_str);
std.debug.print(
\\Error: Could not execute command - executable '{s}' not found
\\Command: {s}
\\Working directory: {s}
\\
, .{ argv[0], cmd_str, cwd_path });
return error.JetzigCommandError;
},
else => return err,
}
const output_behaviour: std.process.Child.StdIo = switch (options.output) {
.stream => .Inherit,
.capture => .Pipe,
};
var child = std.process.Child.init(argv, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = output_behaviour;
child.stderr_behavior = output_behaviour;
if (options.output == .stream) {
child.stdout = std.io.getStdOut();
child.stderr = std.io.getStdErr();
}
child.cwd = cwd_path;
var stdout = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
var stderr = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0);
errdefer {
stdout.deinit(allocator);
stderr.deinit(allocator);
}
try child.spawn();
switch (options.output) {
.capture => try child.collectOutput(allocator, &stdout, &stderr, 50 * 1024),
.stream => {},
}
if (!options.wait) return;
const result = std.process.Child.RunResult{
.term = try child.wait(),
.stdout = try stdout.toOwnedSlice(allocator),
.stderr = try stderr.toOwnedSlice(allocator),
};
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);

1
demo/.gitignore vendored
View File

@ -4,3 +4,4 @@ static/
src/app/views/**/.*.zig
.DS_Store
log/
src/routes.zig

View File

@ -0,0 +1,3 @@
<div>
<span>Content goes here</span>
</div>

View File

@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="/prism.css" />
<meta http-equiv="refresh" content="1" />
</head>
<body>

View File

@ -12,6 +12,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
try root.put("imported_number", importedFunction(100, 200, 300));
try request.response.headers.append("x-example-header", "example header value");
try request.server.logger.INFO("data", .{});
return request.render(.ok);
}

View File

@ -1,10 +1,9 @@
<div class="text-center pt-10 m-auto">
<div><img class="p-3 mx-auto" src="/jetzig.png" /></div>
<!-- Renders `src/app/views/root/_quotes.zmpl`: -->
<div>
@partial root/quotes(message: .message)
@partial root/quotes(message: $.message)
</div>
<div>

View File

@ -24,8 +24,7 @@ const AppOptions = struct {
};
/// Starts an application. `routes` should be `@import("routes").routes`, a generated file
/// automatically created at build time. `templates` should be
/// `@import("src/app/views/zmpl.manifest.zig").templates`, created by Zmpl at compile time.
/// automatically created at build time.
pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
defer self.env.deinit();
@ -35,7 +34,7 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
defer mime_map.deinit();
try mime_map.build();
const routes = try createRoutes(self.allocator, &routes_module.routes);
const routes = try createRoutes(self.allocator, if (@hasDecl(routes_module, "routes")) &routes_module.routes else &.{});
defer {
for (routes) |var_route| {
var_route.deinitParams();
@ -87,8 +86,8 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
self.env,
routes,
self.custom_routes.items,
&routes_module.jobs,
&routes_module.mailers,
if (@hasDecl(routes_module, "jobs")) &routes_module.jobs else &.{},
if (@hasDecl(routes_module, "jobs")) &routes_module.mailers else &.{},
&mime_map,
&store,
&job_queue,
@ -107,8 +106,8 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
.vars = self.env.vars,
.environment = self.env.environment,
.routes = routes,
.jobs = &routes_module.jobs,
.mailers = &routes_module.mailers,
.jobs = if (@hasDecl(routes_module, "jobs")) &routes_module.jobs else &.{},
.mailers = if (@hasDecl(routes_module, "jobs")) &routes_module.mailers else &.{},
.store = &store,
.cache = &cache,
.repo = &repo,
@ -132,7 +131,7 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
return;
},
else => {
try server.logger.ERROR("Encountered error: {}\nExiting.\n", .{err});
try server.logger.ERROR("Encountered error at server launch: {}\nExiting.\n", .{err});
std.process.exit(1);
},
}

View File

@ -110,6 +110,8 @@ pub fn listen(self: *Server) !void {
self.initialized = true;
try jetzig.http.middleware.afterLaunch(self);
return try httpz_server.listen();
}
@ -151,7 +153,23 @@ pub fn processNextRequest(
try request.process();
var middleware_data = try jetzig.http.middleware.afterRequest(&request);
if (try maybeMiddlewareRender(&request, &response)) {
try self.logger.logRequest(&request);
return;
}
try self.renderResponse(&request);
try request.response.headers.append("Content-Type", response.content_type);
try jetzig.http.middleware.beforeResponse(&middleware_data, &request);
try request.respond();
try jetzig.http.middleware.afterResponse(&middleware_data, &request);
jetzig.http.middleware.deinit(&middleware_data, &request);
try self.logger.logRequest(&request);
}
fn maybeMiddlewareRender(request: *jetzig.http.Request, response: *const jetzig.http.Response) !bool {
if (request.middleware_rendered) |_| {
// Request processing ends when a middleware renders or redirects.
if (request.redirect_state) |state| {
@ -162,17 +180,8 @@ pub fn processNextRequest(
}
try request.response.headers.append("Content-Type", response.content_type);
try request.respond();
} else {
try self.renderResponse(&request);
try request.response.headers.append("Content-Type", response.content_type);
try jetzig.http.middleware.beforeResponse(&middleware_data, &request);
try request.respond();
try jetzig.http.middleware.afterResponse(&middleware_data, &request);
jetzig.http.middleware.deinit(&middleware_data, &request);
}
try self.logger.logRequest(&request);
return true;
} else return false;
}
fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {

View File

@ -48,6 +48,14 @@ pub fn Type(comptime name: MiddlewareEnum()) type {
}
}
pub fn afterLaunch(server: *jetzig.http.Server) !void {
inline for (middlewares) |middleware| {
if (comptime @hasDecl(middleware, "afterLaunch")) {
try middleware.afterLaunch(server);
}
}
}
pub fn afterRequest(request: *jetzig.http.Request) !MiddlewareData {
var middleware_data = MiddlewareData.init(0) catch unreachable;