Skip to content

Commit

Permalink
Merge pull request #47 from jetzig-framework/auto-routing-markdown
Browse files Browse the repository at this point in the history
Auto routing markdown
  • Loading branch information
bobf authored Mar 29, 2024
2 parents c2cc9c5 + ec045cc commit 800b72e
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 100 deletions.
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
.hash = "1220bfc5c29bc930b5a524c210712ef65c6cde6770450899bef01164a3089e6707fa",
},
.zmpl = .{
.url = "https://github.com/jetzig-framework/zmpl/archive/9e2df115c9f17e92fb60a4d09bf55ea48d0388b0.tar.gz",
.hash = "1220820b7f5f3e01b7dc976d32cf9ff65d44dee2642533f4b8104e19a824e802d7e1",
.url = "https://github.com/jetzig-framework/zmpl/archive/ffdbd3767da28d5ab07c1a786ea778152d2f79f6.tar.gz",
.hash = "12202cf05fd4ba2482a9b4b89c632b435310a76ac501b7a3d87dfd41006748dd138d",
},
.args = .{
.url = "https://github.com/MasterQ32/zig-args/archive/01d72b9a0128c474aeeb9019edd48605fa6d95f7.tar.gz",
Expand Down
4 changes: 2 additions & 2 deletions cli/commands/generate/secret.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ pub fn run(allocator: std.mem.Allocator, cwd: std.fs.Dir, args: [][]const u8) !v
_ = args;
_ = cwd;
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var secret: [44]u8 = undefined;
var secret: [128]u8 = undefined;

for (0..44) |index| {
for (0..128) |index| {
secret[index] = chars[std.crypto.random.intRangeAtMost(u8, 0, chars.len)];
}

Expand Down
2 changes: 1 addition & 1 deletion src/GenerateRoutes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ fn generateRoutesForView(self: *Self, views_dir: std.fs.Dir, path: []const u8) !
var json_buf = std.ArrayList(u8).init(self.allocator);
defer json_buf.deinit();
const json_writer = json_buf.writer();
try item.toJson(json_writer);
try item.toJson(json_writer, false, 0);
var encoded_buf = std.ArrayList(u8).init(self.allocator);
defer encoded_buf.deinit();
const writer = encoded_buf.writer();
Expand Down
4 changes: 3 additions & 1 deletion src/compile_static_routes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ fn renderMarkdown(
jetzig_options.markdown_fragments
else
null;
const content = try jetzig.markdown.render(allocator, &route, fragments) orelse return null;
const path = try std.mem.join(allocator, "/", &[_][]const u8{ route.uri_path, @tagName(route.action) });
defer allocator.free(path);
const content = try jetzig.markdown.render(allocator, path, fragments) orelse return null;

if (route.layout) |layout_name| {
try view.data.addConst("jetzig_view", view.data.string(route.name));
Expand Down
29 changes: 19 additions & 10 deletions src/jetzig/Environment.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ const Environment = @This();

allocator: std.mem.Allocator,

pub const EnvironmentName = enum { development, production };

const Options = struct {
help: bool = false,
bind: []const u8 = "127.0.0.1",
port: u16 = 8080,
// TODO:
// environment: []const u8 = "development",
environment: EnvironmentName = .development,
log: []const u8 = "-",
@"log-error": []const u8 = "-",
@"log-level": jetzig.loggers.LogLevel = .INFO,
Expand All @@ -24,8 +25,7 @@ const Options = struct {
.h = "help",
.b = "bind",
.p = "port",
// TODO:
// .e = "environment",
.e = "environment",
.d = "detach",
};

Expand All @@ -35,8 +35,7 @@ const Options = struct {
.option_docs = .{
.bind = "IP address/hostname to bind to (default: 127.0.0.1)",
.port = "Port to listen on (default: 8080)",
// TODO:
// .environment = "Load an environment configuration from src/app/environments/<environment>.zig",
.environment = "Set the server environment. Must be one of: { development, production } (default: development)",
.log = "Path to log file. Use '-' for stdout (default: '-')",
.@"log-error" =
\\Optional path to separate error log file. Use '-' for stderr. If omitted, errors are logged to the location specified by the `log` option (or stderr if `log` is '-').
Expand Down Expand Up @@ -94,9 +93,11 @@ pub fn getServerOptions(self: Environment) !jetzig.http.Server.ServerOptions {
std.process.exit(1);
}

const environment = options.options.environment;

// TODO: Generate nonce per session - do research to confirm correct best practice.
const secret_len = jetzig.http.Session.Cipher.key_length + jetzig.http.Session.Cipher.nonce_length;
const secret = try self.getSecret(&logger, secret_len);
const secret = (try self.getSecret(&logger, secret_len, environment))[0..secret_len];

if (secret.len != secret_len) {
try logger.ERROR("Expected secret length: {}, found: {}.", .{ secret_len, secret.len });
Expand All @@ -110,6 +111,7 @@ pub fn getServerOptions(self: Environment) !jetzig.http.Server.ServerOptions {
.bind = try self.allocator.dupe(u8, options.options.bind),
.port = options.options.port,
.detach = options.options.detach,
.environment = environment,
};
}

Expand All @@ -132,11 +134,18 @@ fn getLogFile(stream: enum { stdout, stderr }, options: Options) !std.fs.File {
return file;
}

fn getSecret(self: Environment, logger: *jetzig.loggers.Logger, comptime len: u10) ![]const u8 {
return std.process.getEnvVarOwned(self.allocator, "JETZIG_SECRET") catch |err| {
fn getSecret(self: Environment, logger: *jetzig.loggers.Logger, comptime len: u10, environment: EnvironmentName) ![]const u8 {
const env_var = "JETZIG_SECRET";

return std.process.getEnvVarOwned(self.allocator, env_var) catch |err| {
switch (err) {
error.EnvironmentVariableNotFound => {
// TODO: Make this a failure when running in non-development mode.
if (environment != .development) {
try logger.ERROR("Environment variable `{s}` must be defined in production mode.", .{env_var});
try logger.ERROR("Run `jetzig generate secret` to generate an appropriate value.", .{});
std.process.exit(1);
}

const secret = try jetzig.util.generateSecret(self.allocator, len);
try logger.WARN(
"Running in development mode, using auto-generated cookie encryption key: {s}",
Expand Down
1 change: 1 addition & 0 deletions src/jetzig/http.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ pub const Headers = @import("http/Headers.zig");
pub const Query = @import("http/Query.zig");
pub const Path = @import("http/Path.zig");
pub const status_codes = @import("http/status_codes.zig");
pub const StatusCode = status_codes.StatusCode;
pub const middleware = @import("http/middleware.zig");
pub const mime = @import("http/mime.zig");
28 changes: 28 additions & 0 deletions src/jetzig/http/Request.zig
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,34 @@ pub fn fmtMethod(self: *const Self, colorized: bool) []const u8 {
};
}

/// Format a status code appropriately for the current request format.
/// e.g. `.HTML` => `404 Not Found`
/// `.JSON` => `{ "message": "Not Found", "status": "404" }`
pub fn formatStatus(self: *Self, status_code: jetzig.http.StatusCode) ![]const u8 {
const status = jetzig.http.status_codes.get(status_code);

return switch (self.requestFormat()) {
.JSON => try std.json.stringifyAlloc(self.allocator, .{
.message = status.getMessage(),
.status = status.getCode(),
}, .{}),
.HTML, .UNKNOWN => status.getFormatted(.{ .linebreak = true }),
};
}

pub fn setResponse(
self: *Self,
rendered_view: jetzig.http.Server.RenderedView,
options: struct { content_type: ?[]const u8 = null },
) void {
self.response.content = rendered_view.content;
self.response.status_code = rendered_view.view.status_code;
self.response.content_type = options.content_type orelse switch (self.requestFormat()) {
.HTML, .UNKNOWN => "text/html",
.JSON => "application/json",
};
}

// Determine if a given route matches the current request.
pub fn match(self: *Self, route: jetzig.views.Route) !bool {
return switch (self.method) {
Expand Down
Loading

0 comments on commit 800b72e

Please sign in to comment.