mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
WIP
This commit is contained in:
parent
f3bcff6387
commit
6f8de03f07
10
cli/cli.zig
10
cli/cli.zig
@ -9,6 +9,7 @@ pub const routes = @import("commands/routes.zig");
|
|||||||
pub const bundle = @import("commands/bundle.zig");
|
pub const bundle = @import("commands/bundle.zig");
|
||||||
pub const tests = @import("commands/tests.zig");
|
pub const tests = @import("commands/tests.zig");
|
||||||
pub const database = @import("commands/database.zig");
|
pub const database = @import("commands/database.zig");
|
||||||
|
pub const auth = @import("commands/auth.zig");
|
||||||
|
|
||||||
pub const Environment = enum { development, testing, production };
|
pub const Environment = enum { development, testing, production };
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ const Verb = union(enum) {
|
|||||||
bundle: bundle.Options,
|
bundle: bundle.Options,
|
||||||
@"test": tests.Options,
|
@"test": tests.Options,
|
||||||
database: database.Options,
|
database: database.Options,
|
||||||
|
auth: auth.Options,
|
||||||
g: generate.Options,
|
g: generate.Options,
|
||||||
s: server.Options,
|
s: server.Options,
|
||||||
r: routes.Options,
|
r: routes.Options,
|
||||||
@ -87,6 +89,7 @@ pub fn main() !void {
|
|||||||
\\ routes List all routes in your app.
|
\\ routes List all routes in your app.
|
||||||
\\ bundle Create a deployment bundle.
|
\\ bundle Create a deployment bundle.
|
||||||
\\ database Manage the application's database.
|
\\ database Manage the application's database.
|
||||||
|
\\ auth Utilities for Jetzig authentication.
|
||||||
\\ test Run app tests.
|
\\ test Run app tests.
|
||||||
\\
|
\\
|
||||||
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
||||||
@ -156,6 +159,13 @@ fn run(allocator: std.mem.Allocator, options: args.ParseArgsResult(Options, Verb
|
|||||||
OptionsType,
|
OptionsType,
|
||||||
options,
|
options,
|
||||||
),
|
),
|
||||||
|
.auth => |opts| auth.run(
|
||||||
|
allocator,
|
||||||
|
opts,
|
||||||
|
writer,
|
||||||
|
OptionsType,
|
||||||
|
options,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
cli/commands/auth.zig
Normal file
79
cli/commands/auth.zig
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const args = @import("args");
|
||||||
|
|
||||||
|
/// Command line options for the `update` command.
|
||||||
|
pub const Options = struct {
|
||||||
|
pub const meta = .{
|
||||||
|
.usage_summary = "[password]",
|
||||||
|
.full_text =
|
||||||
|
\\Generates a password
|
||||||
|
\\Example:
|
||||||
|
\\
|
||||||
|
\\ jetzig update
|
||||||
|
\\ jetzig update web
|
||||||
|
,
|
||||||
|
.option_docs = .{
|
||||||
|
.path = "Set the output path relative to the current directory (default: current directory)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Run the `jetzig database` command.
|
||||||
|
pub fn run(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
options: Options,
|
||||||
|
writer: anytype,
|
||||||
|
T: type,
|
||||||
|
main_options: T,
|
||||||
|
) !void {
|
||||||
|
_ = options;
|
||||||
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
const Action = enum { password };
|
||||||
|
const map = std.StaticStringMap(Action).initComptime(.{
|
||||||
|
.{ "password", .password },
|
||||||
|
});
|
||||||
|
|
||||||
|
const action = if (main_options.positionals.len > 0)
|
||||||
|
map.get(main_options.positionals[0])
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
const sub_args: []const []const u8 = if (main_options.positionals.len > 1)
|
||||||
|
main_options.positionals[1..]
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
|
|
||||||
|
return if (main_options.options.help and action == null) blk: {
|
||||||
|
try args.printHelp(Options, "jetzig database", writer);
|
||||||
|
break :blk {};
|
||||||
|
} else if (action == null) blk: {
|
||||||
|
const available_help = try std.mem.join(alloc, "|", map.keys());
|
||||||
|
std.debug.print("Missing sub-command. Expected: [{s}]\n", .{available_help});
|
||||||
|
break :blk error.JetzigCommandError;
|
||||||
|
} else if (action) |capture|
|
||||||
|
switch (capture) {
|
||||||
|
.password => blk: {
|
||||||
|
if (sub_args.len < 1) {
|
||||||
|
std.debug.print("Missing argument. Expected a password paramater.\n", .{});
|
||||||
|
break :blk error.JetzigCommandError;
|
||||||
|
} else {
|
||||||
|
const hash = try hashPassword(alloc, sub_args[0]);
|
||||||
|
try std.io.getStdOut().writer().print("Password hash: {s}\n", .{hash});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hashPassword(allocator: std.mem.Allocator, password: []const u8) ![]const u8 {
|
||||||
|
const buf = try allocator.alloc(u8, 128);
|
||||||
|
return try std.crypto.pwhash.argon2.strHash(
|
||||||
|
password,
|
||||||
|
.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.params = .{ .t = 3, .m = 32, .p = 4 },
|
||||||
|
},
|
||||||
|
buf,
|
||||||
|
);
|
||||||
|
}
|
@ -28,7 +28,7 @@ pub const Options = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Run the `jetzig generate` command.
|
/// Run the `jetzig database` command.
|
||||||
pub fn run(
|
pub fn run(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
@ -78,25 +78,32 @@ const middleware_content =
|
|||||||
\\ return middleware;
|
\\ return middleware;
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
\\/// Invoked immediately after the request head has been processed, before relevant view function
|
\\/// Invoked immediately after the request is received but before it has started processing.
|
||||||
\\/// is processed. This gives you access to request headers but not the request body.
|
\\/// Any calls to `request.render` or `request.redirect` will prevent further processing of the
|
||||||
\\pub fn beforeRequest(self: *Self, request: *jetzig.http.Request) !void {
|
\\/// request, including any other middleware in the chain.
|
||||||
|
\\pub fn afterRequest(self: *Self, request: *jetzig.http.Request) !void {
|
||||||
\\ request.server.logger.debug("[middleware] my_custom_value: {s}", .{self.my_custom_value});
|
\\ request.server.logger.debug("[middleware] my_custom_value: {s}", .{self.my_custom_value});
|
||||||
\\ self.my_custom_value = @tagName(request.method);
|
\\ self.my_custom_value = @tagName(request.method);
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
\\/// Invoked immediately after the request has finished responding. Provides full access to the
|
\\/// Invoked immediately before the response renders to the client.
|
||||||
\\/// response as well as the request.
|
\\/// The response can be modified here if needed.
|
||||||
\\pub fn afterRequest(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
|
\\pub fn beforeResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
|
||||||
\\ request.server.logger.debug(
|
\\ request.server.logger.debug(
|
||||||
\\ "[middleware] my_custom_value: {s}, response status: {s}",
|
\\ "[middleware] my_custom_value: {s}, response status: {s}",
|
||||||
\\ .{ self.my_custom_value, @tagName(response.status_code) },
|
\\ .{ self.my_custom_value, @tagName(response.status_code) },
|
||||||
\\ );
|
\\ );
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
\\/// Invoked after `afterRequest` is called, use this function to do any clean-up.
|
\\/// Invoked immediately after the response has been finalized and sent to the client.
|
||||||
|
\\/// Response data can be accessed for logging, but any modifications will have no impact.
|
||||||
|
\\pub fn afterResponse(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) void {
|
||||||
|
\\ request.allocator.destroy(self);
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\/// Invoked after `afterResponse` is called. Use this function to do any clean-up.
|
||||||
\\/// Note that `request.allocator` is an arena allocator, so any allocations are automatically
|
\\/// Note that `request.allocator` is an arena allocator, so any allocations are automatically
|
||||||
\\/// done before the next request starts processing.
|
\\/// freed before the next request starts processing.
|
||||||
\\pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
|
\\pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
|
||||||
\\ request.allocator.destroy(self);
|
\\ request.allocator.destroy(self);
|
||||||
\\}
|
\\}
|
||||||
|
@ -14,11 +14,12 @@ pub fn run(allocator: std.mem.Allocator, cwd: std.fs.Dir, args: [][]const u8, he
|
|||||||
_ = args;
|
_ = args;
|
||||||
_ = cwd;
|
_ = cwd;
|
||||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
var secret: [128]u8 = undefined;
|
const len = 128;
|
||||||
|
var secret: [len]u8 = undefined;
|
||||||
|
|
||||||
for (0..128) |index| {
|
for (0..len) |index| {
|
||||||
secret[index] = chars[std.crypto.random.intRangeAtMost(u8, 0, chars.len)];
|
secret[index] = chars[std.crypto.random.intRangeAtMost(u8, 0, chars.len - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.print("{s}\n", .{secret});
|
try std.io.getStdOut().writer().print("{s}\n", .{secret});
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ pub const database_lazy_connect = true;
|
|||||||
|
|
||||||
pub const jetzig_options = struct {
|
pub const jetzig_options = struct {
|
||||||
pub const Schema = @import("Schema");
|
pub const Schema = @import("Schema");
|
||||||
|
pub const middleware: []const type = if (@hasDecl(@import("main").jetzig_options, "middleware"))
|
||||||
|
@import("main").jetzig_options.middleware
|
||||||
|
else
|
||||||
|
&.{};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
@ -21,6 +21,7 @@ pub const kv = @import("jetzig/kv.zig");
|
|||||||
pub const database = @import("jetzig/database.zig");
|
pub const database = @import("jetzig/database.zig");
|
||||||
pub const testing = @import("jetzig/testing.zig");
|
pub const testing = @import("jetzig/testing.zig");
|
||||||
pub const config = @import("jetzig/config.zig");
|
pub const config = @import("jetzig/config.zig");
|
||||||
|
pub const auth = @import("jetzig/auth.zig");
|
||||||
|
|
||||||
pub const DateTime = jetcommon.types.DateTime;
|
pub const DateTime = jetcommon.types.DateTime;
|
||||||
pub const Time = jetcommon.types.Time;
|
pub const Time = jetcommon.types.Time;
|
||||||
|
50
src/jetzig/auth.zig
Normal file
50
src/jetzig/auth.zig
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jetzig = @import("../jetzig.zig");
|
||||||
|
|
||||||
|
pub const IdType = enum { string, integer };
|
||||||
|
|
||||||
|
pub fn getUserId(comptime id_type: IdType, request: *jetzig.Request) !?switch (id_type) {
|
||||||
|
.integer => i128,
|
||||||
|
.string => []const u8,
|
||||||
|
} {
|
||||||
|
const session = try request.session();
|
||||||
|
|
||||||
|
return session.getT(std.enums.nameCast(jetzig.data.ValueType, id_type), "_jetzig_user_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signIn(request: *jetzig.Request, user_id: anytype) !void {
|
||||||
|
var session = try request.session();
|
||||||
|
try session.put("_jetzig_user_id", user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verifyPassword(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
hash: []const u8,
|
||||||
|
password: []const u8,
|
||||||
|
) !bool {
|
||||||
|
const verify_error = std.crypto.pwhash.argon2.strVerify(
|
||||||
|
hash,
|
||||||
|
password,
|
||||||
|
.{ .allocator = allocator },
|
||||||
|
);
|
||||||
|
|
||||||
|
return if (verify_error)
|
||||||
|
true
|
||||||
|
else |err| switch (err) {
|
||||||
|
error.AuthenticationFailed, error.PasswordVerificationFailed => false,
|
||||||
|
else => err,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hashPassword(allocator: std.mem.Allocator, password: []const u8) ![]const u8 {
|
||||||
|
const buf = try allocator.alloc(u8, 128);
|
||||||
|
return try std.crypto.pwhash.argon2.strHash(
|
||||||
|
password,
|
||||||
|
.{
|
||||||
|
.allocator = allocator,
|
||||||
|
.params = .{ .t = 3, .m = 32, .p = 4 },
|
||||||
|
},
|
||||||
|
buf,
|
||||||
|
);
|
||||||
|
}
|
@ -12,3 +12,4 @@ pub const Boolean = zmpl.Data.Boolean;
|
|||||||
pub const String = zmpl.Data.String;
|
pub const String = zmpl.Data.String;
|
||||||
pub const Object = zmpl.Data.Object;
|
pub const Object = zmpl.Data.Object;
|
||||||
pub const Array = zmpl.Data.Array;
|
pub const Array = zmpl.Data.Array;
|
||||||
|
pub const ValueType = zmpl.Data.ValueType;
|
||||||
|
@ -35,9 +35,11 @@ layout: ?[]const u8 = null,
|
|||||||
layout_disabled: bool = false,
|
layout_disabled: bool = false,
|
||||||
rendered: bool = false,
|
rendered: bool = false,
|
||||||
redirected: bool = false,
|
redirected: bool = false,
|
||||||
|
failed: bool = false,
|
||||||
redirect_state: ?RedirectState = null,
|
redirect_state: ?RedirectState = null,
|
||||||
middleware_rendered: ?struct { name: []const u8, action: []const u8 } = null,
|
middleware_rendered: ?struct { name: []const u8, action: []const u8 } = null,
|
||||||
middleware_rendered_during_response: bool = false,
|
middleware_rendered_during_response: bool = false,
|
||||||
|
middleware_data: jetzig.http.middleware.MiddlewareData = undefined,
|
||||||
rendered_multiple: bool = false,
|
rendered_multiple: bool = false,
|
||||||
rendered_view: ?jetzig.views.View = null,
|
rendered_view: ?jetzig.views.View = null,
|
||||||
start_time: i128,
|
start_time: i128,
|
||||||
@ -173,7 +175,7 @@ pub fn respond(self: *Request) !void {
|
|||||||
/// Render a response. This function can only be called once per request (repeat calls will
|
/// Render a response. This function can only be called once per request (repeat calls will
|
||||||
/// trigger an error).
|
/// trigger an error).
|
||||||
pub fn render(self: *Request, status_code: jetzig.http.status_codes.StatusCode) jetzig.views.View {
|
pub fn render(self: *Request, status_code: jetzig.http.status_codes.StatusCode) jetzig.views.View {
|
||||||
if (self.rendered) self.rendered_multiple = true;
|
if (self.rendered or self.failed) self.rendered_multiple = true;
|
||||||
|
|
||||||
self.rendered = true;
|
self.rendered = true;
|
||||||
if (self.response_started) self.middleware_rendered_during_response = true;
|
if (self.response_started) self.middleware_rendered_during_response = true;
|
||||||
@ -181,6 +183,18 @@ pub fn render(self: *Request, status_code: jetzig.http.status_codes.StatusCode)
|
|||||||
return self.rendered_view.?;
|
return self.rendered_view.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Render an error. This function can only be called once per request (repeat calls will
|
||||||
|
/// trigger an error).
|
||||||
|
pub fn fail(self: *Request, status_code: jetzig.http.status_codes.StatusCode) jetzig.views.View {
|
||||||
|
if (self.rendered or self.redirected) self.rendered_multiple = true;
|
||||||
|
|
||||||
|
self.rendered = true;
|
||||||
|
self.failed = true;
|
||||||
|
if (self.response_started) self.middleware_rendered_during_response = true;
|
||||||
|
self.rendered_view = .{ .data = self.response_data, .status_code = status_code };
|
||||||
|
return self.rendered_view.?;
|
||||||
|
}
|
||||||
|
|
||||||
/// Issue a redirect to a new location.
|
/// Issue a redirect to a new location.
|
||||||
/// ```zig
|
/// ```zig
|
||||||
/// return request.redirect("https://www.example.com/", .moved_permanently);
|
/// return request.redirect("https://www.example.com/", .moved_permanently);
|
||||||
@ -194,7 +208,7 @@ pub fn redirect(
|
|||||||
location: []const u8,
|
location: []const u8,
|
||||||
redirect_status: enum { moved_permanently, found },
|
redirect_status: enum { moved_permanently, found },
|
||||||
) jetzig.views.View {
|
) jetzig.views.View {
|
||||||
if (self.rendered) self.rendered_multiple = true;
|
if (self.rendered or self.failed) self.rendered_multiple = true;
|
||||||
|
|
||||||
self.rendered = true;
|
self.rendered = true;
|
||||||
self.redirected = true;
|
self.redirected = true;
|
||||||
@ -209,6 +223,19 @@ pub fn redirect(
|
|||||||
return .{ .data = self.response_data, .status_code = status_code };
|
return .{ .data = self.response_data, .status_code = status_code };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn middleware(
|
||||||
|
self: *const Request,
|
||||||
|
comptime name: jetzig.http.middleware.Enum,
|
||||||
|
) jetzig.http.middleware.Type(name) {
|
||||||
|
inline for (jetzig.http.middleware.middlewares, 0..) |T, index| {
|
||||||
|
if (@hasDecl(T, "middleware_name") and std.mem.eql(u8, @tagName(name), T.middleware_name)) {
|
||||||
|
const middleware_data = self.middleware_data.get(index);
|
||||||
|
return @as(*jetzig.http.middleware.Type(name), @ptrCast(@alignCast(middleware_data))).*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
const RedirectState = struct { location: []const u8, status_code: jetzig.http.status_codes.StatusCode };
|
const RedirectState = struct { location: []const u8, status_code: jetzig.http.status_codes.StatusCode };
|
||||||
|
|
||||||
pub fn renderRedirect(self: *Request, state: RedirectState) !void {
|
pub fn renderRedirect(self: *Request, state: RedirectState) !void {
|
||||||
|
@ -153,10 +153,10 @@ pub fn processNextRequest(
|
|||||||
try self.renderResponse(&request);
|
try self.renderResponse(&request);
|
||||||
try request.response.headers.append("Content-Type", response.content_type);
|
try request.response.headers.append("Content-Type", response.content_type);
|
||||||
try jetzig.http.middleware.beforeResponse(&middleware_data, &request);
|
try jetzig.http.middleware.beforeResponse(&middleware_data, &request);
|
||||||
jetzig.http.middleware.deinit(&middleware_data, &request);
|
|
||||||
|
|
||||||
try jetzig.http.middleware.afterResponse(&middleware_data, &request);
|
|
||||||
try request.respond();
|
try request.respond();
|
||||||
|
try jetzig.http.middleware.afterResponse(&middleware_data, &request);
|
||||||
|
jetzig.http.middleware.deinit(&middleware_data, &request);
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.logger.logRequest(&request);
|
try self.logger.logRequest(&request);
|
||||||
@ -233,7 +233,7 @@ fn renderHTML(
|
|||||||
return request.setResponse(rendered_error, .{});
|
return request.setResponse(rendered_error, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
return if (request.redirected or request.dynamic_assigned_template != null)
|
return if (request.redirected or request.failed or request.dynamic_assigned_template != null)
|
||||||
request.setResponse(rendered, .{})
|
request.setResponse(rendered, .{})
|
||||||
else
|
else
|
||||||
request.setResponse(try self.renderNotFound(request), .{});
|
request.setResponse(try self.renderNotFound(request), .{});
|
||||||
@ -302,6 +302,14 @@ fn renderView(
|
|||||||
return try self.renderInternalServerError(request, err);
|
return try self.renderInternalServerError(request, err);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (request.failed) {
|
||||||
|
const view: jetzig.views.View = request.rendered_view orelse .{
|
||||||
|
.data = request.response_data,
|
||||||
|
.status_code = .internal_server_error,
|
||||||
|
};
|
||||||
|
return try self.renderError(request, view.status_code);
|
||||||
|
}
|
||||||
|
|
||||||
const template: ?zmpl.Template = if (request.dynamic_assigned_template) |request_template|
|
const template: ?zmpl.Template = if (request.dynamic_assigned_template) |request_template|
|
||||||
zmpl.findPrefixed("views", request_template) orelse maybe_template
|
zmpl.findPrefixed("views", request_template) orelse maybe_template
|
||||||
else
|
else
|
||||||
|
@ -53,8 +53,8 @@ pub fn deinit(self: *Self) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a value from the session.
|
/// Get a value from the session.
|
||||||
pub fn get(self: *Self, key: []const u8) !?*jetzig.data.Value {
|
pub fn get(self: *Self, key: []const u8) ?*jetzig.data.Value {
|
||||||
if (self.state != .parsed) return error.UnparsedSessionCookie;
|
std.debug.assert(self.state == .parsed);
|
||||||
|
|
||||||
return switch (self.data.value.?.*) {
|
return switch (self.data.value.?.*) {
|
||||||
.object => self.data.value.?.object.get(key),
|
.object => self.data.value.?.object.get(key),
|
||||||
@ -62,8 +62,22 @@ pub fn get(self: *Self, key: []const u8) !?*jetzig.data.Value {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a typed value from the session.
|
||||||
|
pub fn getT(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: jetzig.data.ValueType,
|
||||||
|
key: []const u8,
|
||||||
|
) @TypeOf(self.data.value.?.object.getT(T, key)) {
|
||||||
|
std.debug.assert(self.state == .parsed);
|
||||||
|
|
||||||
|
return switch (self.data.value.?.*) {
|
||||||
|
.object => self.data.value.?.object.getT(T, key),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Put a value into the session.
|
/// Put a value into the session.
|
||||||
pub fn put(self: *Self, key: []const u8, value: *jetzig.data.Value) !void {
|
pub fn put(self: *Self, key: []const u8, value: anytype) !void {
|
||||||
if (self.state != .parsed) return error.UnparsedSessionCookie;
|
if (self.state != .parsed) return error.UnparsedSessionCookie;
|
||||||
|
|
||||||
switch (self.data.value.?.*) {
|
switch (self.data.value.?.*) {
|
||||||
|
@ -1,9 +1,52 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const jetzig = @import("../../jetzig.zig");
|
const jetzig = @import("../../jetzig.zig");
|
||||||
|
|
||||||
const middlewares: []const type = jetzig.config.get([]const type, "middleware");
|
pub const middlewares: []const type = jetzig.config.get([]const type, "middleware");
|
||||||
|
pub const MiddlewareData = std.BoundedArray(?*anyopaque, middlewares.len);
|
||||||
|
pub const Enum = MiddlewareEnum();
|
||||||
|
|
||||||
const MiddlewareData = std.BoundedArray(?*anyopaque, middlewares.len);
|
fn MiddlewareEnum() type {
|
||||||
|
comptime {
|
||||||
|
var size: usize = 0;
|
||||||
|
for (middlewares) |middleware_type| {
|
||||||
|
if (@hasDecl(middleware_type, "middleware_name")) size += 1;
|
||||||
|
}
|
||||||
|
var fields: [size]std.builtin.Type.EnumField = undefined;
|
||||||
|
var index: usize = 0;
|
||||||
|
for (middlewares) |middleware_type| {
|
||||||
|
if (@hasDecl(middleware_type, "middleware_name")) {
|
||||||
|
fields[index] = .{ .name = middleware_type.middleware_name, .value = index };
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @Type(.{
|
||||||
|
.@"enum" = .{
|
||||||
|
.tag_type = std.math.IntFittingRange(0, if (size == 0) 0 else size - 1),
|
||||||
|
.fields = &fields,
|
||||||
|
.decls = &.{},
|
||||||
|
.is_exhaustive = true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Type(comptime name: MiddlewareEnum()) type {
|
||||||
|
comptime {
|
||||||
|
for (middlewares) |middleware_type| {
|
||||||
|
if (@hasDecl(
|
||||||
|
middleware_type,
|
||||||
|
"middleware_name",
|
||||||
|
) and std.mem.eql(
|
||||||
|
u8,
|
||||||
|
middleware_type.middleware_name,
|
||||||
|
@tagName(name),
|
||||||
|
)) {
|
||||||
|
return middleware_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn afterRequest(request: *jetzig.http.Request) !MiddlewareData {
|
pub fn afterRequest(request: *jetzig.http.Request) !MiddlewareData {
|
||||||
var middleware_data = MiddlewareData.init(0) catch unreachable;
|
var middleware_data = MiddlewareData.init(0) catch unreachable;
|
||||||
@ -35,6 +78,7 @@ pub fn afterRequest(request: *jetzig.http.Request) !MiddlewareData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request.middleware_data = middleware_data;
|
||||||
return middleware_data;
|
return middleware_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ stdout_colorized: bool,
|
|||||||
stderr_colorized: bool,
|
stderr_colorized: bool,
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
log_queue: *jetzig.loggers.LogQueue,
|
log_queue: *jetzig.loggers.LogQueue,
|
||||||
|
mutex: *std.Thread.Mutex,
|
||||||
|
|
||||||
/// Initialize a new Development Logger.
|
/// Initialize a new Development Logger.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
@ -19,12 +20,14 @@ pub fn init(
|
|||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
log_queue: *jetzig.loggers.LogQueue,
|
log_queue: *jetzig.loggers.LogQueue,
|
||||||
) DevelopmentLogger {
|
) DevelopmentLogger {
|
||||||
|
const mutex = allocator.create(std.Thread.Mutex) catch unreachable;
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.level = level,
|
.level = level,
|
||||||
.log_queue = log_queue,
|
.log_queue = log_queue,
|
||||||
.stdout_colorized = log_queue.stdout_is_tty,
|
.stdout_colorized = log_queue.stdout_is_tty,
|
||||||
.stderr_colorized = log_queue.stderr_is_tty,
|
.stderr_colorized = log_queue.stderr_is_tty,
|
||||||
|
.mutex = mutex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +108,9 @@ pub fn logRequest(self: DevelopmentLogger, request: *const jetzig.http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn logSql(self: *const DevelopmentLogger, event: jetzig.jetquery.events.Event) !void {
|
pub fn logSql(self: *const DevelopmentLogger, event: jetzig.jetquery.events.Event) !void {
|
||||||
|
self.mutex.lock();
|
||||||
|
defer self.mutex.unlock();
|
||||||
|
|
||||||
// XXX: This function does not make any effort to prevent log messages clobbering each other
|
// XXX: This function does not make any effort to prevent log messages clobbering each other
|
||||||
// from multiple threads. JSON logger etc. write in one call and the logger's mutex prevents
|
// from multiple threads. JSON logger etc. write in one call and the logger's mutex prevents
|
||||||
// clobbering, but this is not the case here.
|
// clobbering, but this is not the case here.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user