mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
Refactor Server into generic type
We need to have routes available within the server if we are going to do any kind of dynamic dispatch for Channel Actions.
This commit is contained in:
parent
d3b3ae63cf
commit
8c2d6806b5
@ -16,7 +16,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
|
pub fn edit(id: []const u8, request: *jetzig.Request) !jetzig.View {
|
||||||
try request.server.logger.INFO("id: {s}", .{id});
|
try request.logger.INFO("id: {s}", .{id});
|
||||||
return request.render(.ok);
|
return request.render(.ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const jetzig = @import("jetzig");
|
const jetzig = @import("jetzig");
|
||||||
|
|
||||||
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
pub fn index(request: *jetzig.Request) !jetzig.View {
|
||||||
_ = data;
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|
||||||
_ = data;
|
|
||||||
_ = id;
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn post(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|
||||||
_ = data;
|
|
||||||
return request.render(.created);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|
||||||
_ = data;
|
|
||||||
_ = id;
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn patch(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|
||||||
_ = data;
|
|
||||||
_ = id;
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
|
||||||
_ = data;
|
|
||||||
_ = id;
|
|
||||||
return request.render(.ok);
|
return request.render(.ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +60,13 @@ pub const Channel = struct {
|
|||||||
try message.channel.sync();
|
try message.channel.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Actions = struct {
|
||||||
|
pub fn reset(channel: jetzig.channels.Channel) !void {
|
||||||
|
_ = channel;
|
||||||
|
std.debug.print("here\n", .{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn resetGame(channel: jetzig.channels.Channel) !void {
|
fn resetGame(channel: jetzig.channels.Channel) !void {
|
||||||
try channel.put("winner", null);
|
try channel.put("winner", null);
|
||||||
var cells = try channel.put("cells", .array);
|
var cells = try channel.put("cells", .array);
|
||||||
@ -172,51 +149,3 @@ const Game = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test "index" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.GET, "/websockets", .{});
|
|
||||||
try response.expectStatus(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "get" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.GET, "/websockets/example-id", .{});
|
|
||||||
try response.expectStatus(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "post" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.POST, "/websockets", .{});
|
|
||||||
try response.expectStatus(.created);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "put" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.PUT, "/websockets/example-id", .{});
|
|
||||||
try response.expectStatus(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "patch" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.PATCH, "/websockets/example-id", .{});
|
|
||||||
try response.expectStatus(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "delete" {
|
|
||||||
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
const response = try app.request(.DELETE, "/websockets/example-id", .{});
|
|
||||||
try response.expectStatus(.ok);
|
|
||||||
}
|
|
||||||
|
@ -57,15 +57,15 @@
|
|||||||
<div id="party-container"></div>
|
<div id="party-container"></div>
|
||||||
|
|
||||||
<div class="board" id="board">
|
<div class="board" id="board">
|
||||||
<div class="cell" id="tic-tac-toe-cell-0" data-cell="0"></div>
|
<div class="cell" jetzig-connect="$.cells.0" id="tic-tac-toe-cell-0" data-cell="0"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-1" data-cell="1"></div>
|
<div class="cell" jetzig-connect="$.cells.1" id="tic-tac-toe-cell-1" data-cell="1"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-2" data-cell="2"></div>
|
<div class="cell" jetzig-connect="$.cells.2" id="tic-tac-toe-cell-2" data-cell="2"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-3" data-cell="3"></div>
|
<div class="cell" jetzig-connect="$.cells.3" id="tic-tac-toe-cell-3" data-cell="3"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-4" data-cell="4"></div>
|
<div class="cell" jetzig-connect="$.cells.4" id="tic-tac-toe-cell-4" data-cell="4"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-5" data-cell="5"></div>
|
<div class="cell" jetzig-connect="$.cells.5" id="tic-tac-toe-cell-5" data-cell="5"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-6" data-cell="6"></div>
|
<div class="cell" jetzig-connect="$.cells.6" id="tic-tac-toe-cell-6" data-cell="6"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-7" data-cell="7"></div>
|
<div class="cell" jetzig-connect="$.cells.7" id="tic-tac-toe-cell-7" data-cell="7"></div>
|
||||||
<div class="cell" id="tic-tac-toe-cell-8" data-cell="8"></div>
|
<div class="cell" jetzig-connect="$.cells.8" id="tic-tac-toe-cell-8" data-cell="8"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="reset-button">Reset Game</button>
|
<button id="reset-button">Reset Game</button>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
|
|
||||||
Object.entries(state.cells).forEach(([cell, state]) => {
|
Object.entries(state.cells).forEach(([cell, state]) => {
|
||||||
const element = document.querySelector(`#tic-tac-toe-cell-${cell}`);
|
const element = document.querySelector(`#tic-tac-toe-cell-${cell}`);
|
||||||
element.innerHTML = { player: "✈", cpu: "🦎" }[state] || "";
|
element.innerHTML = { player: "✈️", cpu: "🦎" }[state] || "";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ buffer: std.ArrayList(u8),
|
|||||||
dynamic_routes: std.ArrayList(Function),
|
dynamic_routes: std.ArrayList(Function),
|
||||||
static_routes: std.ArrayList(Function),
|
static_routes: std.ArrayList(Function),
|
||||||
channel_routes: std.ArrayList([]const u8),
|
channel_routes: std.ArrayList([]const u8),
|
||||||
module_paths: std.ArrayList([]const u8),
|
module_paths: std.StringHashMap(void),
|
||||||
data: *jetzig.data.Data,
|
data: *jetzig.data.Data,
|
||||||
|
|
||||||
const receive_message = "receiveMessage";
|
const receive_message = "receiveMessage";
|
||||||
@ -124,7 +124,7 @@ pub fn init(
|
|||||||
.static_routes = std.ArrayList(Function).init(allocator),
|
.static_routes = std.ArrayList(Function).init(allocator),
|
||||||
.dynamic_routes = std.ArrayList(Function).init(allocator),
|
.dynamic_routes = std.ArrayList(Function).init(allocator),
|
||||||
.channel_routes = std.ArrayList([]const u8).init(allocator),
|
.channel_routes = std.ArrayList([]const u8).init(allocator),
|
||||||
.module_paths = std.ArrayList([]const u8).init(allocator),
|
.module_paths = std.StringHashMap(void).init(allocator),
|
||||||
.data = data,
|
.data = data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -186,16 +186,29 @@ pub fn generateRoutes(self: *Routes) ![]const u8 {
|
|||||||
\\
|
\\
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try writer.writeAll(
|
||||||
|
\\
|
||||||
|
\\pub const View = struct { name: []const u8, module: type };
|
||||||
|
\\pub const views = [_]View{
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
try self.writeViewsArray(writer);
|
||||||
|
try writer.writeAll(
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
try writer.writeAll(
|
try writer.writeAll(
|
||||||
\\test {
|
\\test {
|
||||||
\\
|
\\
|
||||||
);
|
);
|
||||||
|
|
||||||
for (self.module_paths.items) |module_path| {
|
var it = self.module_paths.keyIterator();
|
||||||
|
while (it.next()) |module_path| {
|
||||||
try writer.print(
|
try writer.print(
|
||||||
\\ _ = @import("{s}");
|
\\ _ = @import("{s}");
|
||||||
\\
|
\\
|
||||||
, .{module_path});
|
, .{module_path.*});
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll(
|
try writer.writeAll(
|
||||||
@ -362,7 +375,7 @@ fn writeRoute(self: *Routes, writer: std.ArrayList(u8).Writer, route: Function)
|
|||||||
unreachable;
|
unreachable;
|
||||||
|
|
||||||
std.mem.replaceScalar(u8, module_path, '\\', '/');
|
std.mem.replaceScalar(u8, module_path, '\\', '/');
|
||||||
try self.module_paths.append(try self.allocator.dupe(u8, module_path));
|
try self.module_paths.put(try self.allocator.dupe(u8, module_path), {});
|
||||||
|
|
||||||
var buf: [32]u8 = undefined;
|
var buf: [32]u8 = undefined;
|
||||||
const id = jetzig.util.generateVariableName(&buf);
|
const id = jetzig.util.generateVariableName(&buf);
|
||||||
@ -879,3 +892,15 @@ fn writeJobs(self: Routes, writer: anytype) !void {
|
|||||||
|
|
||||||
std.debug.print("[jetzig] Imported {} job(s)\n", .{count});
|
std.debug.print("[jetzig] Imported {} job(s)\n", .{count});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writeViewsArray(self: Routes, writer: anytype) !void {
|
||||||
|
var it = self.module_paths.keyIterator();
|
||||||
|
while (it.next()) |path| {
|
||||||
|
try writer.print(
|
||||||
|
\\.{{ .name = "{s}", .module = @import("{s}") }},
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{ chompExtension(try self.relativePathFrom(.views, path.*, .posix)), path.* },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ env: jetzig.Environment,
|
|||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
custom_routes: std.ArrayList(jetzig.views.Route),
|
custom_routes: std.ArrayList(jetzig.views.Route),
|
||||||
initHook: ?*const fn (*App) anyerror!void,
|
initHook: ?*const fn (*App) anyerror!void,
|
||||||
server: *jetzig.http.Server = undefined,
|
server: *anyopaque = undefined,
|
||||||
|
|
||||||
pub fn deinit(self: *const App) void {
|
pub fn deinit(self: *const App) void {
|
||||||
@constCast(self).custom_routes.deinit();
|
@constCast(self).custom_routes.deinit();
|
||||||
@ -28,6 +28,10 @@ const AppOptions = struct {
|
|||||||
pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
||||||
defer self.env.deinit();
|
defer self.env.deinit();
|
||||||
|
|
||||||
|
const action_router = comptime jetzig.channels.ActionRouter.initComptime(routes_module);
|
||||||
|
_ = action_router;
|
||||||
|
// inline for (action_router.actions) |action| std.debug.print("{s}\n", .{@typeName(action.params[0].type.?)});
|
||||||
|
|
||||||
if (self.initHook) |hook| try hook(@constCast(self));
|
if (self.initHook) |hook| try hook(@constCast(self));
|
||||||
|
|
||||||
var mime_map = jetzig.http.mime.MimeMap.init(self.allocator);
|
var mime_map = jetzig.http.mime.MimeMap.init(self.allocator);
|
||||||
@ -93,7 +97,7 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void {
|
|||||||
std.process.exit(0);
|
std.process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var server = jetzig.http.Server.init(
|
var server = jetzig.http.Server.RoutedServer(routes_module).init(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
self.env,
|
self.env,
|
||||||
routes,
|
routes,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub const Channel = @import("channels/Channel.zig");
|
pub const Channel = @import("channels/Channel.zig");
|
||||||
pub const Message = @import("channels/Message.zig");
|
pub const Message = @import("channels/Message.zig");
|
||||||
pub const Route = @import("channels/Route.zig");
|
pub const Route = @import("channels/Route.zig");
|
||||||
|
pub const ActionRouter = @import("channels/ActionRouter.zig");
|
||||||
|
45
src/jetzig/channels/ActionRouter.zig
Normal file
45
src/jetzig/channels/ActionRouter.zig
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn initComptime(T: type) ActionRouter {
|
||||||
|
comptime {
|
||||||
|
var len: usize = 0;
|
||||||
|
for (T.views) |view| {
|
||||||
|
if (!@hasDecl(view.module, "Channel")) continue;
|
||||||
|
if (!@hasDecl(view.module.Channel, "Actions")) continue;
|
||||||
|
|
||||||
|
const actions = view.module.Channel.Actions;
|
||||||
|
for (std.meta.declarations(actions)) |_| {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var actions: [len]Action = undefined;
|
||||||
|
var index: usize = 0;
|
||||||
|
for (T.views) |view| {
|
||||||
|
if (!@hasDecl(view.module, "Channel")) continue;
|
||||||
|
if (!@hasDecl(view.module.Channel, "Actions")) continue;
|
||||||
|
|
||||||
|
const channel_actions = view.module.Channel.Actions;
|
||||||
|
for (std.meta.declarations(channel_actions)) |decl| {
|
||||||
|
actions[index] = .{
|
||||||
|
.view = view.name,
|
||||||
|
.name = decl.name,
|
||||||
|
.params = &.{}, //@typeInfo(@TypeOf(@field(view.module.Channel.Actions, decl.name))).@"fn".params,
|
||||||
|
};
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = actions;
|
||||||
|
return .{ .actions = &result };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Action = struct {
|
||||||
|
view: []const u8,
|
||||||
|
name: []const u8,
|
||||||
|
params: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ActionRouter = struct {
|
||||||
|
actions: []const Action,
|
||||||
|
};
|
@ -30,7 +30,12 @@ pub fn value(message: Message) !*jetzig.data.Value {
|
|||||||
test "message with payload" {
|
test "message with payload" {
|
||||||
const message = Message.init(
|
const message = Message.init(
|
||||||
std.testing.allocator,
|
std.testing.allocator,
|
||||||
Channel{ .websocket = undefined, .state = undefined },
|
Channel{
|
||||||
|
.websocket = undefined,
|
||||||
|
.state = undefined,
|
||||||
|
.allocator = undefined,
|
||||||
|
.data = undefined,
|
||||||
|
},
|
||||||
"foo",
|
"foo",
|
||||||
);
|
);
|
||||||
try std.testing.expectEqualStrings(message.payload, "foo");
|
try std.testing.expectEqualStrings(message.payload, "foo");
|
||||||
|
@ -39,9 +39,10 @@ pub fn repo(allocator: std.mem.Allocator, app: anytype) !Repo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn eventCallback(event: jetzig.jetquery.events.Event, app: anytype) !void {
|
fn eventCallback(event: jetzig.jetquery.events.Event, app: anytype) !void {
|
||||||
try app.server.logger.logSql(event);
|
var server: *jetzig.http.Server.RoutedServer(@import("root").routes) = @ptrCast(@alignCast(app.server));
|
||||||
|
try server.logger.logSql(event);
|
||||||
if (event.err) |err| {
|
if (event.err) |err| {
|
||||||
try app.server.logger.ERROR("[database] {?s}", .{err.message});
|
try server.logger.ERROR("[database] {?s}", .{err.message});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ path: jetzig.http.Path,
|
|||||||
method: Method,
|
method: Method,
|
||||||
headers: jetzig.http.Headers,
|
headers: jetzig.http.Headers,
|
||||||
host: []const u8,
|
host: []const u8,
|
||||||
server: *jetzig.http.Server,
|
|
||||||
httpz_request: *httpz.Request,
|
httpz_request: *httpz.Request,
|
||||||
httpz_response: *httpz.Response,
|
httpz_response: *httpz.Response,
|
||||||
response: *jetzig.http.Response,
|
response: *jetzig.http.Response,
|
||||||
@ -53,8 +52,14 @@ rendered_view: ?jetzig.views.View = null,
|
|||||||
start_time: i128,
|
start_time: i128,
|
||||||
store: RequestStore(jetzig.kv.Store.GeneralStore),
|
store: RequestStore(jetzig.kv.Store.GeneralStore),
|
||||||
cache: RequestStore(jetzig.kv.Store.CacheStore),
|
cache: RequestStore(jetzig.kv.Store.CacheStore),
|
||||||
|
job_queue: RequestStore(jetzig.kv.Store.JobQueueStore),
|
||||||
|
job_definitions: []const jetzig.JobDefinition,
|
||||||
|
mailer_definitions: []const jetzig.MailerDefinition,
|
||||||
repo: *jetzig.database.Repo,
|
repo: *jetzig.database.Repo,
|
||||||
global: *jetzig.Global,
|
global: *jetzig.Global,
|
||||||
|
env: jetzig.Environment,
|
||||||
|
routes: []const *const jetzig.views.Route,
|
||||||
|
logger: jetzig.loggers.Logger,
|
||||||
|
|
||||||
/// Wrapper for KV store that uses the request's arena allocator for fetching values.
|
/// Wrapper for KV store that uses the request's arena allocator for fetching values.
|
||||||
pub fn RequestStore(T: type) type {
|
pub fn RequestStore(T: type) type {
|
||||||
@ -123,12 +128,20 @@ pub fn RequestStore(T: type) type {
|
|||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
server: *jetzig.http.Server,
|
|
||||||
start_time: i128,
|
start_time: i128,
|
||||||
httpz_request: *httpz.Request,
|
httpz_request: *httpz.Request,
|
||||||
httpz_response: *httpz.Response,
|
httpz_response: *httpz.Response,
|
||||||
response: *jetzig.http.Response,
|
response: *jetzig.http.Response,
|
||||||
repo: *jetzig.database.Repo,
|
repo: *jetzig.database.Repo,
|
||||||
|
env: jetzig.Environment,
|
||||||
|
routes: []const *const jetzig.views.Route,
|
||||||
|
logger: jetzig.loggers.Logger,
|
||||||
|
store: *jetzig.kv.Store.GeneralStore,
|
||||||
|
cache: *jetzig.kv.Store.CacheStore,
|
||||||
|
job_queue: *jetzig.kv.Store.JobQueueStore,
|
||||||
|
job_definitions: []const jetzig.JobDefinition,
|
||||||
|
mailer_definitions: []const jetzig.MailerDefinition,
|
||||||
|
global: *anyopaque,
|
||||||
) !Request {
|
) !Request {
|
||||||
const path = jetzig.http.Path.init(httpz_request.url.raw);
|
const path = jetzig.http.Path.init(httpz_request.url.raw);
|
||||||
|
|
||||||
@ -156,19 +169,24 @@ pub fn init(
|
|||||||
.method = method,
|
.method = method,
|
||||||
.headers = headers,
|
.headers = headers,
|
||||||
.host = host,
|
.host = host,
|
||||||
.server = server,
|
|
||||||
.response = response,
|
.response = response,
|
||||||
.response_data = response_data,
|
.response_data = response_data,
|
||||||
.httpz_request = httpz_request,
|
.httpz_request = httpz_request,
|
||||||
.httpz_response = httpz_response,
|
.httpz_response = httpz_response,
|
||||||
.start_time = start_time,
|
.start_time = start_time,
|
||||||
.store = .{ .store = server.store, .allocator = allocator },
|
.store = .{ .store = store, .allocator = allocator },
|
||||||
.cache = .{ .store = server.cache, .allocator = allocator },
|
.cache = .{ .store = cache, .allocator = allocator },
|
||||||
|
.job_queue = .{ .store = job_queue, .allocator = allocator },
|
||||||
|
.job_definitions = job_definitions,
|
||||||
|
.mailer_definitions = mailer_definitions,
|
||||||
|
.env = env,
|
||||||
|
.routes = routes,
|
||||||
|
.logger = logger,
|
||||||
.repo = repo,
|
.repo = repo,
|
||||||
.global = if (@hasField(jetzig.Global, "__jetzig_default"))
|
.global = if (@hasField(jetzig.Global, "__jetzig_default"))
|
||||||
undefined
|
undefined
|
||||||
else
|
else
|
||||||
@ptrCast(@alignCast(server.global)),
|
@ptrCast(@alignCast(global)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,19 +519,19 @@ pub fn cookies(self: *Request) !*jetzig.http.Cookies {
|
|||||||
/// `jetzig.http.Session`.
|
/// `jetzig.http.Session`.
|
||||||
pub fn session(self: *Request) !*jetzig.http.Session {
|
pub fn session(self: *Request) !*jetzig.http.Session {
|
||||||
if (self._session) |capture| return capture;
|
if (self._session) |capture| return capture;
|
||||||
const cookie_name = self.server.env.vars.get("JETZIG_SESSION_COOKIE") orelse
|
const cookie_name = self.env.vars.get("JETZIG_SESSION_COOKIE") orelse
|
||||||
jetzig.http.Session.default_cookie_name;
|
jetzig.http.Session.default_cookie_name;
|
||||||
const local_session = try self.allocator.create(jetzig.http.Session);
|
const local_session = try self.allocator.create(jetzig.http.Session);
|
||||||
local_session.* = jetzig.http.Session.init(
|
local_session.* = jetzig.http.Session.init(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
try self.cookies(),
|
try self.cookies(),
|
||||||
self.server.env.secret,
|
self.env.secret,
|
||||||
.{ .cookie_name = cookie_name },
|
.{ .cookie_name = cookie_name },
|
||||||
);
|
);
|
||||||
local_session.parse() catch |err| {
|
local_session.parse() catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
error.JetzigInvalidSessionCookie => {
|
error.JetzigInvalidSessionCookie => {
|
||||||
try self.server.logger.DEBUG("Invalid session cookie detected. Resetting session.", .{});
|
try self.logger.DEBUG("Invalid session cookie detected. Resetting session.", .{});
|
||||||
try local_session.reset();
|
try local_session.reset();
|
||||||
},
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
@ -565,11 +583,11 @@ pub fn job(self: *Request, job_name: []const u8) !*jetzig.Job {
|
|||||||
const background_job = try self.allocator.create(jetzig.Job);
|
const background_job = try self.allocator.create(jetzig.Job);
|
||||||
background_job.* = jetzig.Job.init(
|
background_job.* = jetzig.Job.init(
|
||||||
self.allocator,
|
self.allocator,
|
||||||
self.server.store,
|
self.store.store,
|
||||||
self.server.job_queue,
|
self.job_queue.store,
|
||||||
self.server.cache,
|
self.cache.store,
|
||||||
self.server.logger,
|
self.logger,
|
||||||
self.server.job_definitions,
|
self.job_definitions,
|
||||||
job_name,
|
job_name,
|
||||||
);
|
);
|
||||||
return background_job;
|
return background_job;
|
||||||
@ -611,14 +629,14 @@ const RequestMail = struct {
|
|||||||
self.request.allocator,
|
self.request.allocator,
|
||||||
mail_job.params,
|
mail_job.params,
|
||||||
jetzig.jobs.JobEnv{
|
jetzig.jobs.JobEnv{
|
||||||
.vars = self.request.server.env.vars,
|
.vars = self.request.env.vars,
|
||||||
.environment = self.request.server.env.environment,
|
.environment = self.request.env.environment,
|
||||||
.logger = self.request.server.logger,
|
.logger = self.request.logger,
|
||||||
.routes = self.request.server.routes,
|
.routes = self.request.routes,
|
||||||
.mailers = self.request.server.mailer_definitions,
|
.mailers = self.request.mailer_definitions,
|
||||||
.jobs = self.request.server.job_definitions,
|
.jobs = self.request.job_definitions,
|
||||||
.store = self.request.server.store,
|
.store = self.request.store.store,
|
||||||
.cache = self.request.server.cache,
|
.cache = self.request.cache.store,
|
||||||
.mutex = undefined,
|
.mutex = undefined,
|
||||||
.repo = self.request.repo,
|
.repo = self.request.repo,
|
||||||
},
|
},
|
||||||
|
@ -6,28 +6,33 @@ const zmpl = @import("zmpl");
|
|||||||
const zmd = @import("zmd");
|
const zmd = @import("zmd");
|
||||||
const httpz = @import("httpz");
|
const httpz = @import("httpz");
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
pub const RenderedView = struct { view: jetzig.views.View, content: []const u8 };
|
||||||
logger: jetzig.loggers.Logger,
|
|
||||||
env: jetzig.Environment,
|
|
||||||
routes: []const *const jetzig.views.Route,
|
|
||||||
channel_routes: std.StaticStringMap(jetzig.channels.Route),
|
|
||||||
custom_routes: []const jetzig.views.Route,
|
|
||||||
job_definitions: []const jetzig.JobDefinition,
|
|
||||||
mailer_definitions: []const jetzig.MailerDefinition,
|
|
||||||
mime_map: *jetzig.http.mime.MimeMap,
|
|
||||||
initialized: bool = false,
|
|
||||||
store: *jetzig.kv.Store.GeneralStore,
|
|
||||||
job_queue: *jetzig.kv.Store.JobQueueStore,
|
|
||||||
cache: *jetzig.kv.Store.CacheStore,
|
|
||||||
channels: *jetzig.kv.Store.ChannelStore,
|
|
||||||
repo: *jetzig.database.Repo,
|
|
||||||
global: *anyopaque,
|
|
||||||
decoded_static_route_params: []const *jetzig.data.Value = &.{},
|
|
||||||
debug_mutex: std.Thread.Mutex = .{},
|
|
||||||
|
|
||||||
const Server = @This();
|
pub fn RoutedServer(Routes: type) type {
|
||||||
|
_ = Routes;
|
||||||
|
return struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
logger: jetzig.loggers.Logger,
|
||||||
|
env: jetzig.Environment,
|
||||||
|
routes: []const *const jetzig.views.Route,
|
||||||
|
channel_routes: std.StaticStringMap(jetzig.channels.Route),
|
||||||
|
custom_routes: []const jetzig.views.Route,
|
||||||
|
job_definitions: []const jetzig.JobDefinition,
|
||||||
|
mailer_definitions: []const jetzig.MailerDefinition,
|
||||||
|
mime_map: *jetzig.http.mime.MimeMap,
|
||||||
|
initialized: bool = false,
|
||||||
|
store: *jetzig.kv.Store.GeneralStore,
|
||||||
|
job_queue: *jetzig.kv.Store.JobQueueStore,
|
||||||
|
cache: *jetzig.kv.Store.CacheStore,
|
||||||
|
channels: *jetzig.kv.Store.ChannelStore,
|
||||||
|
repo: *jetzig.database.Repo,
|
||||||
|
global: *anyopaque,
|
||||||
|
decoded_static_route_params: []const *jetzig.data.Value = &.{},
|
||||||
|
debug_mutex: std.Thread.Mutex = .{},
|
||||||
|
|
||||||
pub fn init(
|
const Server = @This();
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
env: jetzig.Environment,
|
env: jetzig.Environment,
|
||||||
routes: []const *const jetzig.views.Route,
|
routes: []const *const jetzig.views.Route,
|
||||||
@ -42,7 +47,7 @@ pub fn init(
|
|||||||
channels: *jetzig.kv.Store.ChannelStore,
|
channels: *jetzig.kv.Store.ChannelStore,
|
||||||
repo: *jetzig.database.Repo,
|
repo: *jetzig.database.Repo,
|
||||||
global: *anyopaque,
|
global: *anyopaque,
|
||||||
) Server {
|
) Server {
|
||||||
return .{
|
return .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.logger = env.logger,
|
.logger = env.logger,
|
||||||
@ -60,14 +65,14 @@ pub fn init(
|
|||||||
.repo = repo,
|
.repo = repo,
|
||||||
.global = global,
|
.global = global,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Server) void {
|
pub fn deinit(self: *Server) void {
|
||||||
self.allocator.free(self.env.secret);
|
self.allocator.free(self.env.secret);
|
||||||
self.allocator.free(self.env.bind);
|
self.allocator.free(self.env.bind);
|
||||||
}
|
}
|
||||||
|
|
||||||
const HttpzHandler = struct {
|
const HttpzHandler = struct {
|
||||||
server: *Server,
|
server: *Server,
|
||||||
|
|
||||||
pub const WebsocketHandler = jetzig.http.Websocket;
|
pub const WebsocketHandler = jetzig.http.Websocket;
|
||||||
@ -77,9 +82,9 @@ const HttpzHandler = struct {
|
|||||||
self.server.errorHandlerFn(request, response, err) catch {};
|
self.server.errorHandlerFn(request, response, err) catch {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn listen(self: *Server) !void {
|
pub fn listen(self: *Server) !void {
|
||||||
try self.decodeStaticParams();
|
try self.decodeStaticParams();
|
||||||
|
|
||||||
const worker_count = jetzig.config.get(u16, "worker_count");
|
const worker_count = jetzig.config.get(u16, "worker_count");
|
||||||
@ -121,9 +126,9 @@ pub fn listen(self: *Server) !void {
|
|||||||
try jetzig.http.middleware.afterLaunch(self);
|
try jetzig.http.middleware.afterLaunch(self);
|
||||||
|
|
||||||
return try httpz_server.listen();
|
return try httpz_server.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errorHandlerFn(self: *Server, request: *httpz.Request, response: *httpz.Response, err: anyerror) !void {
|
pub fn errorHandlerFn(self: *Server, request: *httpz.Request, response: *httpz.Response, err: anyerror) !void {
|
||||||
if (isBadHttpError(err)) return;
|
if (isBadHttpError(err)) return;
|
||||||
|
|
||||||
self.logger.ERROR("Encountered error: {s} {s}", .{ @errorName(err), request.url.raw }) catch {};
|
self.logger.ERROR("Encountered error: {s} {s}", .{ @errorName(err), request.url.raw }) catch {};
|
||||||
@ -135,13 +140,13 @@ pub fn errorHandlerFn(self: *Server, request: *httpz.Request, response: *httpz.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
response.body = "500 Internal Server Error";
|
response.body = "500 Internal Server Error";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn processNextRequest(
|
pub fn processNextRequest(
|
||||||
self: *Server,
|
self: *Server,
|
||||||
httpz_request: *httpz.Request,
|
httpz_request: *httpz.Request,
|
||||||
httpz_response: *httpz.Response,
|
httpz_response: *httpz.Response,
|
||||||
) !void {
|
) !void {
|
||||||
const start_time = std.time.nanoTimestamp();
|
const start_time = std.time.nanoTimestamp();
|
||||||
|
|
||||||
var repo = try self.repo.bindConnect(.{ .allocator = httpz_response.arena });
|
var repo = try self.repo.bindConnect(.{ .allocator = httpz_response.arena });
|
||||||
@ -150,12 +155,20 @@ pub fn processNextRequest(
|
|||||||
var response = try jetzig.http.Response.init(httpz_response.arena, httpz_response);
|
var response = try jetzig.http.Response.init(httpz_response.arena, httpz_response);
|
||||||
var request = try jetzig.http.Request.init(
|
var request = try jetzig.http.Request.init(
|
||||||
httpz_response.arena,
|
httpz_response.arena,
|
||||||
self,
|
|
||||||
start_time,
|
start_time,
|
||||||
httpz_request,
|
httpz_request,
|
||||||
httpz_response,
|
httpz_response,
|
||||||
&response,
|
&response,
|
||||||
&repo,
|
&repo,
|
||||||
|
self.env,
|
||||||
|
self.routes,
|
||||||
|
self.logger,
|
||||||
|
self.store,
|
||||||
|
self.cache,
|
||||||
|
self.job_queue,
|
||||||
|
self.job_definitions,
|
||||||
|
self.mailer_definitions,
|
||||||
|
self.global,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (try self.upgradeWebsocket(httpz_request, httpz_response, &request)) {
|
if (try self.upgradeWebsocket(httpz_request, httpz_response, &request)) {
|
||||||
@ -180,19 +193,20 @@ pub fn processNextRequest(
|
|||||||
jetzig.http.middleware.deinit(&middleware_data, &request);
|
jetzig.http.middleware.deinit(&middleware_data, &request);
|
||||||
|
|
||||||
try self.logger.logRequest(&request);
|
try self.logger.logRequest(&request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to match a channel name to a view with a Channel implementation.
|
/// Attempt to match a channel name to a view with a Channel implementation.
|
||||||
pub fn matchChannelRoute(self: *const Server, channel_name: []const u8) ?jetzig.channels.Route {
|
pub fn matchChannelRoute(self: *const Server, channel_name: []const u8) ?jetzig.channels.Route {
|
||||||
|
// TODO: Detect root path correctly.
|
||||||
return self.channel_routes.get(channel_name);
|
return self.channel_routes.get(channel_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgradeWebsocket(
|
fn upgradeWebsocket(
|
||||||
self: *const Server,
|
self: *const Server,
|
||||||
httpz_request: *httpz.Request,
|
httpz_request: *httpz.Request,
|
||||||
httpz_response: *httpz.Response,
|
httpz_response: *httpz.Response,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
) !bool {
|
) !bool {
|
||||||
const route = self.matchChannelRoute(request.path.view_name) orelse return false;
|
const route = self.matchChannelRoute(request.path.view_name) orelse return false;
|
||||||
const session = try request.session();
|
const session = try request.session();
|
||||||
const session_id = session.getT(.string, "_id") orelse {
|
const session_id = session.getT(.string, "_id") orelse {
|
||||||
@ -208,12 +222,12 @@ fn upgradeWebsocket(
|
|||||||
.allocator = self.allocator,
|
.allocator = self.allocator,
|
||||||
.route = route,
|
.route = route,
|
||||||
.session_id = session_id,
|
.session_id = session_id,
|
||||||
.server = self,
|
.channels = self.channels,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybeMiddlewareRender(request: *jetzig.http.Request, response: *const jetzig.http.Response) !bool {
|
fn maybeMiddlewareRender(request: *jetzig.http.Request, response: *const jetzig.http.Response) !bool {
|
||||||
if (request.middleware_rendered) |_| {
|
if (request.middleware_rendered) |_| {
|
||||||
// Request processing ends when a middleware renders or redirects.
|
// Request processing ends when a middleware renders or redirects.
|
||||||
if (request.redirect_state) |state| {
|
if (request.redirect_state) |state| {
|
||||||
@ -226,9 +240,9 @@ fn maybeMiddlewareRender(request: *jetzig.http.Request, response: *const jetzig.
|
|||||||
try request.respond();
|
try request.respond();
|
||||||
return true;
|
return true;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
||||||
const static_resource = self.matchStaticResource(request) catch |err| {
|
const static_resource = self.matchStaticResource(request) catch |err| {
|
||||||
if (isUnhandledError(err)) return err;
|
if (isUnhandledError(err)) return err;
|
||||||
|
|
||||||
@ -295,20 +309,20 @@ fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.redirect_state) |state| try request.renderRedirect(state);
|
if (request.redirect_state) |state| try request.renderRedirect(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderStatic(resource: StaticResource, request: *jetzig.http.Request) !void {
|
fn renderStatic(resource: StaticResource, request: *jetzig.http.Request) !void {
|
||||||
request.setResponse(
|
request.setResponse(
|
||||||
.{ .view = .{ .data = request.response_data }, .content = resource.content },
|
.{ .view = .{ .data = request.response_data }, .content = resource.content },
|
||||||
.{ .content_type = resource.mime_type },
|
.{ .content_type = resource.mime_type },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderHTML(
|
fn renderHTML(
|
||||||
self: *Server,
|
self: *Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
route: ?jetzig.views.Route,
|
route: ?jetzig.views.Route,
|
||||||
) !void {
|
) !void {
|
||||||
if (route) |matched_route| {
|
if (route) |matched_route| {
|
||||||
if (zmpl.findPrefixed("views", matched_route.template)) |template| {
|
if (zmpl.findPrefixed("views", matched_route.template)) |template| {
|
||||||
const rendered = self.renderView(matched_route, request, template) catch |err| {
|
const rendered = self.renderView(matched_route, request, template) catch |err| {
|
||||||
@ -345,13 +359,13 @@ fn renderHTML(
|
|||||||
return request.setResponse(try self.renderNotFound(request), .{});
|
return request.setResponse(try self.renderNotFound(request), .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderJSON(
|
fn renderJSON(
|
||||||
self: *Server,
|
self: *Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
route: ?jetzig.views.Route,
|
route: ?jetzig.views.Route,
|
||||||
) !void {
|
) !void {
|
||||||
if (route) |matched_route| {
|
if (route) |matched_route| {
|
||||||
var rendered = try self.renderView(matched_route, request, null);
|
var rendered = try self.renderView(matched_route, request, null);
|
||||||
var data = rendered.view.data;
|
var data = rendered.view.data;
|
||||||
@ -367,9 +381,9 @@ fn renderJSON(
|
|||||||
} else {
|
} else {
|
||||||
request.setResponse(try self.renderNotFound(request), .{});
|
request.setResponse(try self.renderNotFound(request), .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderMarkdown(self: *Server, request: *jetzig.http.Request) !?RenderedView {
|
fn renderMarkdown(self: *Server, request: *jetzig.http.Request) !?RenderedView {
|
||||||
_ = self;
|
_ = self;
|
||||||
// No route recognized, but we can still render a static markdown file if it matches the URI:
|
// No route recognized, but we can still render a static markdown file if it matches the URI:
|
||||||
if (request.method != .GET) return null;
|
if (request.method != .GET) return null;
|
||||||
@ -381,16 +395,14 @@ fn renderMarkdown(self: *Server, request: *jetzig.http.Request) !?RenderedView {
|
|||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const RenderedView = struct { view: jetzig.views.View, content: []const u8 };
|
fn renderView(
|
||||||
|
|
||||||
fn renderView(
|
|
||||||
self: *Server,
|
self: *Server,
|
||||||
route: jetzig.views.Route,
|
route: jetzig.views.Route,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
maybe_template: ?zmpl.Template,
|
maybe_template: ?zmpl.Template,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
// View functions return a `View` to encourage users to return from a view function with
|
// View functions return a `View` to encourage users to return from a view function with
|
||||||
// `return request.render(.ok)`, but the actual rendered view is stored in
|
// `return request.render(.ok)`, but the actual rendered view is stored in
|
||||||
// `request.rendered_view`.
|
// `request.rendered_view`.
|
||||||
@ -448,15 +460,15 @@ fn renderView(
|
|||||||
.content = "",
|
.content = "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderTemplateWithLayout(
|
fn renderTemplateWithLayout(
|
||||||
self: *Server,
|
self: *Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
template: zmpl.Template,
|
template: zmpl.Template,
|
||||||
view: jetzig.views.View,
|
view: jetzig.views.View,
|
||||||
route: jetzig.views.Route,
|
route: jetzig.views.Route,
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
try addTemplateConstants(view, route);
|
try addTemplateConstants(view, route);
|
||||||
|
|
||||||
const template_context = jetzig.TemplateContext{ .request = request };
|
const template_context = jetzig.TemplateContext{ .request = request };
|
||||||
@ -492,9 +504,9 @@ fn renderTemplateWithLayout(
|
|||||||
template_context,
|
template_context,
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addTemplateConstants(view: jetzig.views.View, route: jetzig.views.Route) !void {
|
fn addTemplateConstants(view: jetzig.views.View, route: jetzig.views.Route) !void {
|
||||||
const action = switch (route.action) {
|
const action = switch (route.action) {
|
||||||
.custom => route.name,
|
.custom => route.name,
|
||||||
else => |tag| @tagName(tag),
|
else => |tag| @tagName(tag),
|
||||||
@ -502,23 +514,23 @@ fn addTemplateConstants(view: jetzig.views.View, route: jetzig.views.Route) !voi
|
|||||||
|
|
||||||
try view.data.addConst("jetzig_action", view.data.string(action));
|
try view.data.addConst("jetzig_action", view.data.string(action));
|
||||||
try view.data.addConst("jetzig_view", view.data.string(route.view_name));
|
try view.data.addConst("jetzig_view", view.data.string(route.view_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isBadRequest(err: anyerror) bool {
|
fn isBadRequest(err: anyerror) bool {
|
||||||
return switch (err) {
|
return switch (err) {
|
||||||
error.JetzigBodyParseError, error.JetzigQueryParseError => true,
|
error.JetzigBodyParseError, error.JetzigQueryParseError => true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isUnhandledError(err: anyerror) bool {
|
fn isUnhandledError(err: anyerror) bool {
|
||||||
return switch (err) {
|
return switch (err) {
|
||||||
error.OutOfMemory => true,
|
error.OutOfMemory => true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isBadHttpError(err: anyerror) bool {
|
fn isBadHttpError(err: anyerror) bool {
|
||||||
return switch (err) {
|
return switch (err) {
|
||||||
error.JetzigParseHeadError,
|
error.JetzigParseHeadError,
|
||||||
error.UnknownHttpMethod,
|
error.UnknownHttpMethod,
|
||||||
@ -535,63 +547,63 @@ fn isBadHttpError(err: anyerror) bool {
|
|||||||
=> true,
|
=> true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderInternalServerError(
|
fn renderInternalServerError(
|
||||||
self: *Server,
|
self: *Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
stack_trace: ?*std.builtin.StackTrace,
|
stack_trace: ?*std.builtin.StackTrace,
|
||||||
err: anyerror,
|
err: anyerror,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
try self.logger.logError(stack_trace, err);
|
try self.logger.logError(stack_trace, err);
|
||||||
|
|
||||||
const status = jetzig.http.StatusCode.internal_server_error;
|
const status = jetzig.http.StatusCode.internal_server_error;
|
||||||
|
|
||||||
return try self.renderError(request, status, .{ .stack_trace = stack_trace, .err = err });
|
return try self.renderError(request, status, .{ .stack_trace = stack_trace, .err = err });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderNotFound(self: *Server, request: *jetzig.http.Request) !RenderedView {
|
fn renderNotFound(self: *Server, request: *jetzig.http.Request) !RenderedView {
|
||||||
request.response_data.reset();
|
request.response_data.reset();
|
||||||
|
|
||||||
const status: jetzig.http.StatusCode = .not_found;
|
const status: jetzig.http.StatusCode = .not_found;
|
||||||
return try self.renderError(request, status, .{});
|
return try self.renderError(request, status, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderBadRequest(self: *Server, request: *jetzig.http.Request) !RenderedView {
|
fn renderBadRequest(self: *Server, request: *jetzig.http.Request) !RenderedView {
|
||||||
request.response_data.reset();
|
request.response_data.reset();
|
||||||
|
|
||||||
const status: jetzig.http.StatusCode = .bad_request;
|
const status: jetzig.http.StatusCode = .bad_request;
|
||||||
return try self.renderError(request, status, .{});
|
return try self.renderError(request, status, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderError(
|
fn renderError(
|
||||||
self: Server,
|
self: Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
status_code: jetzig.http.StatusCode,
|
status_code: jetzig.http.StatusCode,
|
||||||
error_info: jetzig.debug.ErrorInfo,
|
error_info: jetzig.debug.ErrorInfo,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
if (comptime jetzig.build_options.debug_console) {
|
if (comptime jetzig.build_options.debug_console) {
|
||||||
return try self.renderDebugConsole(request, status_code, error_info);
|
return try self.renderDebugConsole(request, status_code, error_info);
|
||||||
} else return try self.renderGeneralError(request, status_code);
|
} else return try self.renderGeneralError(request, status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderGeneralError(
|
fn renderGeneralError(
|
||||||
self: Server,
|
self: Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
status_code: jetzig.http.StatusCode,
|
status_code: jetzig.http.StatusCode,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
if (try self.renderErrorView(request, status_code)) |view| return view;
|
if (try self.renderErrorView(request, status_code)) |view| return view;
|
||||||
if (try renderStaticErrorPage(request, status_code)) |view| return view;
|
if (try renderStaticErrorPage(request, status_code)) |view| return view;
|
||||||
|
|
||||||
return try renderDefaultError(request, status_code);
|
return try renderDefaultError(request, status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderDebugConsole(
|
fn renderDebugConsole(
|
||||||
self: Server,
|
self: Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
status_code: jetzig.http.StatusCode,
|
status_code: jetzig.http.StatusCode,
|
||||||
error_info: jetzig.debug.ErrorInfo,
|
error_info: jetzig.debug.ErrorInfo,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
if (comptime jetzig.build_options.debug_console) {
|
if (comptime jetzig.build_options.debug_console) {
|
||||||
var buf = std.ArrayList(u8).init(request.allocator);
|
var buf = std.ArrayList(u8).init(request.allocator);
|
||||||
const writer = buf.writer();
|
const writer = buf.writer();
|
||||||
@ -620,13 +632,13 @@ fn renderDebugConsole(
|
|||||||
.content = if (content.len == 0) "" else content,
|
.content = if (content.len == 0) "" else content,
|
||||||
};
|
};
|
||||||
} else unreachable;
|
} else unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderErrorView(
|
fn renderErrorView(
|
||||||
self: Server,
|
self: Server,
|
||||||
request: *jetzig.http.Request,
|
request: *jetzig.http.Request,
|
||||||
status_code: jetzig.http.StatusCode,
|
status_code: jetzig.http.StatusCode,
|
||||||
) !?RenderedView {
|
) !?RenderedView {
|
||||||
for (self.routes) |route| {
|
for (self.routes) |route| {
|
||||||
if (std.mem.eql(u8, route.view_name, "errors") and route.action == .index) {
|
if (std.mem.eql(u8, route.view_name, "errors") and route.action == .index) {
|
||||||
request.response_data.reset();
|
request.response_data.reset();
|
||||||
@ -665,9 +677,9 @@ fn renderErrorView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderStaticErrorPage(request: *jetzig.http.Request, status_code: jetzig.http.StatusCode) !?RenderedView {
|
fn renderStaticErrorPage(request: *jetzig.http.Request, status_code: jetzig.http.StatusCode) !?RenderedView {
|
||||||
if (request.requestFormat() == .JSON) return null;
|
if (request.requestFormat() == .JSON) return null;
|
||||||
|
|
||||||
var dir = std.fs.cwd().openDir(
|
var dir = std.fs.cwd().openDir(
|
||||||
@ -697,40 +709,40 @@ fn renderStaticErrorPage(request: *jetzig.http.Request, status_code: jetzig.http
|
|||||||
.view = jetzig.views.View{ .data = request.response_data, .status_code = status_code },
|
.view = jetzig.views.View{ .data = request.response_data, .status_code = status_code },
|
||||||
.content = content,
|
.content = content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderDefaultError(
|
fn renderDefaultError(
|
||||||
request: *const jetzig.http.Request,
|
request: *const jetzig.http.Request,
|
||||||
status_code: jetzig.http.StatusCode,
|
status_code: jetzig.http.StatusCode,
|
||||||
) !RenderedView {
|
) !RenderedView {
|
||||||
const content = try request.formatStatus(status_code);
|
const content = try request.formatStatus(status_code);
|
||||||
return .{
|
return .{
|
||||||
.view = jetzig.views.View{ .data = request.response_data, .status_code = status_code },
|
.view = jetzig.views.View{ .data = request.response_data, .status_code = status_code },
|
||||||
.content = content,
|
.content = content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logStackTrace(
|
fn logStackTrace(
|
||||||
self: Server,
|
self: Server,
|
||||||
stack: *std.builtin.StackTrace,
|
stack: *std.builtin.StackTrace,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
) !void {
|
) !void {
|
||||||
var buf = std.ArrayList(u8).init(allocator);
|
var buf = std.ArrayList(u8).init(allocator);
|
||||||
defer buf.deinit();
|
defer buf.deinit();
|
||||||
const writer = buf.writer();
|
const writer = buf.writer();
|
||||||
try stack.format("", .{}, writer);
|
try stack.format("", .{}, writer);
|
||||||
if (buf.items.len > 0) try self.logger.ERROR("{s}\n", .{buf.items});
|
if (buf.items.len > 0) try self.logger.ERROR("{s}\n", .{buf.items});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchCustomRoute(self: Server, request: *const jetzig.http.Request) ?jetzig.views.Route {
|
fn matchCustomRoute(self: Server, request: *const jetzig.http.Request) ?jetzig.views.Route {
|
||||||
for (self.custom_routes) |custom_route| {
|
for (self.custom_routes) |custom_route| {
|
||||||
if (custom_route.match(request)) return custom_route;
|
if (custom_route.match(request)) return custom_route;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchMiddlewareRoute(request: *const jetzig.http.Request) ?jetzig.middleware.MiddlewareRoute {
|
fn matchMiddlewareRoute(request: *const jetzig.http.Request) ?jetzig.middleware.MiddlewareRoute {
|
||||||
const middlewares = jetzig.config.get([]const type, "middleware");
|
const middlewares = jetzig.config.get([]const type, "middleware");
|
||||||
|
|
||||||
inline for (middlewares) |middleware| {
|
inline for (middlewares) |middleware| {
|
||||||
@ -742,9 +754,9 @@ fn matchMiddlewareRoute(request: *const jetzig.http.Request) ?jetzig.middleware.
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchRoute(self: *Server, request: *jetzig.http.Request, static: bool) !?jetzig.views.Route {
|
fn matchRoute(self: *Server, request: *jetzig.http.Request, static: bool) !?jetzig.views.Route {
|
||||||
for (self.routes) |route| {
|
for (self.routes) |route| {
|
||||||
// .index routes always take precedence.
|
// .index routes always take precedence.
|
||||||
if (route.action == .index and try request.match(route.*)) {
|
if (route.action == .index and try request.match(route.*)) {
|
||||||
@ -761,14 +773,14 @@ fn matchRoute(self: *Server, request: *jetzig.http.Request, static: bool) !?jetz
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StaticResource = struct {
|
const StaticResource = struct {
|
||||||
content: []const u8,
|
content: []const u8,
|
||||||
mime_type: []const u8 = "application/octet-stream",
|
mime_type: []const u8 = "application/octet-stream",
|
||||||
};
|
};
|
||||||
|
|
||||||
fn matchStaticResource(self: *Server, request: *jetzig.http.Request) !?StaticResource {
|
fn matchStaticResource(self: *Server, request: *jetzig.http.Request) !?StaticResource {
|
||||||
if (comptime jetzig.build_options.debug_console) {
|
if (comptime jetzig.build_options.debug_console) {
|
||||||
if (std.mem.eql(u8, request.path.path, "/_jetzig_debug.js")) return .{
|
if (std.mem.eql(u8, request.path.path, "/_jetzig_debug.js")) return .{
|
||||||
.content = @embedFile("../../assets/debug.js"),
|
.content = @embedFile("../../assets/debug.js"),
|
||||||
@ -791,9 +803,9 @@ fn matchStaticResource(self: *Server, request: *jetzig.http.Request) !?StaticRes
|
|||||||
};
|
};
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchPublicContent(self: *Server, request: *jetzig.http.Request) !?StaticResource {
|
fn matchPublicContent(self: *Server, request: *jetzig.http.Request) !?StaticResource {
|
||||||
if (request.path.file_path.len <= 1) return null;
|
if (request.path.file_path.len <= 1) return null;
|
||||||
if (request.method != .GET) return null;
|
if (request.method != .GET) return null;
|
||||||
|
|
||||||
@ -833,9 +845,9 @@ fn matchPublicContent(self: *Server, request: *jetzig.http.Request) !?StaticReso
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchStaticContent(self: *Server, request: *jetzig.http.Request) !?[]const u8 {
|
fn matchStaticContent(self: *Server, request: *jetzig.http.Request) !?[]const u8 {
|
||||||
const request_format = request.requestFormat();
|
const request_format = request.requestFormat();
|
||||||
const matched_route = try self.matchRoute(request, true);
|
const matched_route = try self.matchRoute(request, true);
|
||||||
|
|
||||||
@ -867,9 +879,9 @@ fn matchStaticContent(self: *Server, request: *jetzig.http.Request) !?[]const u8
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decodeStaticParams(self: *Server) !void {
|
pub fn decodeStaticParams(self: *Server) !void {
|
||||||
if (comptime !@hasDecl(jetzig.root, "static")) return;
|
if (comptime !@hasDecl(jetzig.root, "static")) return;
|
||||||
|
|
||||||
// Store decoded static params (i.e. declared in views) for faster comparison at request time.
|
// Store decoded static params (i.e. declared in views) for faster comparison at request time.
|
||||||
@ -882,15 +894,15 @@ pub fn decodeStaticParams(self: *Server) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.decoded_static_route_params = try decoded.toOwnedSlice();
|
self.decoded_static_route_params = try decoded.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matchStaticOutput(
|
fn matchStaticOutput(
|
||||||
maybe_expected_id: ?[]const u8,
|
maybe_expected_id: ?[]const u8,
|
||||||
maybe_expected_params: ?*jetzig.data.Value,
|
maybe_expected_params: ?*jetzig.data.Value,
|
||||||
route: jetzig.views.Route,
|
route: jetzig.views.Route,
|
||||||
request: *const jetzig.http.Request,
|
request: *const jetzig.http.Request,
|
||||||
params: jetzig.data.Value,
|
params: jetzig.data.Value,
|
||||||
) bool {
|
) bool {
|
||||||
return if (maybe_expected_params) |expected_params| blk: {
|
return if (maybe_expected_params) |expected_params| blk: {
|
||||||
const params_match = expected_params.count() == 0 or expected_params.eql(params);
|
const params_match = expected_params.count() == 0 or expected_params.eql(params);
|
||||||
break :blk switch (route.action) {
|
break :blk switch (route.action) {
|
||||||
@ -904,4 +916,6 @@ fn matchStaticOutput(
|
|||||||
std.mem.eql(u8, id, request.path.resource_id)
|
std.mem.eql(u8, id, request.path.resource_id)
|
||||||
else
|
else
|
||||||
true; // We reached a params filter (possibly the default catch-all) with no params set.
|
true; // We reached a params filter (possibly the default catch-all) with no params set.
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ pub const Context = struct {
|
|||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
route: jetzig.channels.Route,
|
route: jetzig.channels.Route,
|
||||||
session_id: []const u8,
|
session_id: []const u8,
|
||||||
server: *const jetzig.http.Server,
|
channels: *jetzig.kv.Store.ChannelStore,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Websocket = @This();
|
const Websocket = @This();
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
connection: *httpz.websocket.Conn,
|
connection: *httpz.websocket.Conn,
|
||||||
server: *const jetzig.http.Server,
|
channels: *jetzig.kv.Store.ChannelStore,
|
||||||
route: jetzig.channels.Route,
|
route: jetzig.channels.Route,
|
||||||
data: *jetzig.Data,
|
data: *jetzig.Data,
|
||||||
session_id: []const u8,
|
session_id: []const u8,
|
||||||
@ -29,7 +29,7 @@ pub fn init(connection: *httpz.websocket.Conn, context: Context) !Websocket {
|
|||||||
.connection = connection,
|
.connection = connection,
|
||||||
.route = context.route,
|
.route = context.route,
|
||||||
.session_id = context.session_id,
|
.session_id = context.session_id,
|
||||||
.server = context.server,
|
.channels = context.channels,
|
||||||
.data = data,
|
.data = data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -70,15 +70,15 @@ pub fn syncState(websocket: *Websocket, channel: jetzig.channels.Channel) !void
|
|||||||
const writer = write_buffer.writer();
|
const writer = write_buffer.writer();
|
||||||
|
|
||||||
// TODO: Make this really fast.
|
// TODO: Make this really fast.
|
||||||
try websocket.server.channels.put(websocket.session_id, channel.state);
|
try websocket.channels.put(websocket.session_id, channel.state);
|
||||||
try writer.print("__jetzig_channel_state__:{s}", .{try websocket.data.toJson()});
|
try writer.print("__jetzig_channel_state__:{s}", .{try websocket.data.toJson()});
|
||||||
try write_buffer.flush();
|
try write_buffer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getState(websocket: *Websocket) !*jetzig.data.Value {
|
pub fn getState(websocket: *Websocket) !*jetzig.data.Value {
|
||||||
return try websocket.server.channels.get(websocket.data, websocket.session_id) orelse blk: {
|
return try websocket.channels.get(websocket.data, websocket.session_id) orelse blk: {
|
||||||
const root = try websocket.data.root(.object);
|
const root = try websocket.data.root(.object);
|
||||||
try websocket.server.channels.put(websocket.session_id, root);
|
try websocket.channels.put(websocket.session_id, root);
|
||||||
break :blk try websocket.server.channels.get(websocket.data, websocket.session_id) orelse error.JetzigInvalidChannel;
|
break :blk try websocket.channels.get(websocket.data, websocket.session_id) orelse error.JetzigInvalidChannel;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ pub fn Type(comptime name: MiddlewareEnum()) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn afterLaunch(server: *jetzig.http.Server) !void {
|
pub fn afterLaunch(server: *jetzig.http.Server.RoutedServer(@import("root").routes)) !void {
|
||||||
inline for (middlewares) |middleware| {
|
inline for (middlewares) |middleware| {
|
||||||
if (comptime @hasDecl(middleware, "afterLaunch")) {
|
if (comptime @hasDecl(middleware, "afterLaunch")) {
|
||||||
try middleware.afterLaunch(server);
|
try middleware.afterLaunch(server);
|
||||||
|
@ -29,7 +29,7 @@ pub fn beforeRender(request: *jetzig.http.Request, route: jetzig.views.Route) !v
|
|||||||
|
|
||||||
fn logFailure(request: *jetzig.http.Request) !void {
|
fn logFailure(request: *jetzig.http.Request) !void {
|
||||||
_ = request.fail(.forbidden);
|
_ = request.fail(.forbidden);
|
||||||
try request.server.logger.DEBUG("Anti-CSRF token validation failed. Request aborted.", .{});
|
try request.logger.DEBUG("Anti-CSRF token validation failed. Request aborted.", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verifyCsrfToken(request: *jetzig.http.Request) !void {
|
fn verifyCsrfToken(request: *jetzig.http.Request) !void {
|
||||||
|
@ -15,7 +15,7 @@ channels: *MemoryStore,
|
|||||||
job_queue: *MemoryStore,
|
job_queue: *MemoryStore,
|
||||||
multipart_boundary: ?[]const u8 = null,
|
multipart_boundary: ?[]const u8 = null,
|
||||||
logger: jetzig.loggers.Logger,
|
logger: jetzig.loggers.Logger,
|
||||||
server: Server,
|
server: *jetzig.http.Server.RoutedServer(@import("root").routes),
|
||||||
repo: *jetzig.database.Repo,
|
repo: *jetzig.database.Repo,
|
||||||
cookies: *jetzig.http.Cookies,
|
cookies: *jetzig.http.Cookies,
|
||||||
session: *jetzig.http.Session,
|
session: *jetzig.http.Session,
|
||||||
@ -58,6 +58,28 @@ pub fn init(allocator: std.mem.Allocator, routes_module: type) !App {
|
|||||||
const session = try alloc.create(jetzig.http.Session);
|
const session = try alloc.create(jetzig.http.Session);
|
||||||
session.* = jetzig.http.Session.init(alloc, cookies, jetzig.testing.secret, .{});
|
session.* = jetzig.http.Session.init(alloc, cookies, jetzig.testing.secret, .{});
|
||||||
|
|
||||||
|
const server = try alloc.create(jetzig.http.Server.RoutedServer(@import("root").routes));
|
||||||
|
// This server is only used by database logging - we create a more lifelike server when we
|
||||||
|
// process the actual test request.
|
||||||
|
server.* = .{
|
||||||
|
.logger = logger,
|
||||||
|
.allocator = undefined,
|
||||||
|
.env = undefined,
|
||||||
|
.routes = undefined,
|
||||||
|
.channel_routes = undefined,
|
||||||
|
.custom_routes = undefined,
|
||||||
|
.job_definitions = undefined,
|
||||||
|
.mailer_definitions = undefined,
|
||||||
|
.mime_map = undefined,
|
||||||
|
.initialized = undefined,
|
||||||
|
.store = undefined,
|
||||||
|
.job_queue = undefined,
|
||||||
|
.cache = undefined,
|
||||||
|
.channels = undefined,
|
||||||
|
.repo = undefined,
|
||||||
|
.global = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
app.* = App{
|
app.* = App{
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
@ -67,7 +89,7 @@ pub fn init(allocator: std.mem.Allocator, routes_module: type) !App {
|
|||||||
.channels = try createStore(arena.allocator(), logger, .channels),
|
.channels = try createStore(arena.allocator(), logger, .channels),
|
||||||
.job_queue = try createStore(arena.allocator(), logger, .jobs),
|
.job_queue = try createStore(arena.allocator(), logger, .jobs),
|
||||||
.logger = logger,
|
.logger = logger,
|
||||||
.server = .{ .logger = logger },
|
.server = server,
|
||||||
.repo = repo,
|
.repo = repo,
|
||||||
.cookies = cookies,
|
.cookies = cookies,
|
||||||
.session = session,
|
.session = session,
|
||||||
@ -133,7 +155,7 @@ pub fn request(
|
|||||||
.env_map = std.process.EnvMap.init(allocator),
|
.env_map = std.process.EnvMap.init(allocator),
|
||||||
.env_file = null,
|
.env_file = null,
|
||||||
};
|
};
|
||||||
var server = jetzig.http.Server{
|
var server = jetzig.http.Server.RoutedServer(@import("root").routes){
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.logger = self.logger,
|
.logger = self.logger,
|
||||||
.env = .{
|
.env = .{
|
||||||
@ -150,6 +172,7 @@ pub fn request(
|
|||||||
.secret = jetzig.testing.secret,
|
.secret = jetzig.testing.secret,
|
||||||
},
|
},
|
||||||
.routes = routes,
|
.routes = routes,
|
||||||
|
.channel_routes = std.StaticStringMap(jetzig.channels.Route).initComptime(.{}),
|
||||||
.custom_routes = &.{},
|
.custom_routes = &.{},
|
||||||
.mailer_definitions = &.{},
|
.mailer_definitions = &.{},
|
||||||
.job_definitions = &.{},
|
.job_definitions = &.{},
|
||||||
|
@ -8,6 +8,7 @@ pub const std_options = std.Options{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const jetzig_options = @import("main").jetzig_options;
|
pub const jetzig_options = @import("main").jetzig_options;
|
||||||
|
pub const routes = @import("main").routes;
|
||||||
|
|
||||||
pub fn log(
|
pub fn log(
|
||||||
comptime message_level: std.log.Level,
|
comptime message_level: std.log.Level,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user