diff --git a/cli/commands/init.zig b/cli/commands/init.zig index 934a3fe..7ca1fa0 100644 --- a/cli/commands/init.zig +++ b/cli/commands/init.zig @@ -114,7 +114,7 @@ pub fn run( try copySourceFile( allocator, install_dir, - "demo/src/main.zig", + "init/src/main.zig", "src/main.zig", null, ); @@ -217,7 +217,13 @@ pub fn run( \\ \\ $ cd {s} \\ - \\ $ zig build run or jetzig server + \\ then + \\ + \\ $ zig build run + \\ + \\ or + \\ + \\ $ jetzig server \\ \\And then browse to http://localhost:8080/ \\ diff --git a/cli/compile.zig b/cli/compile.zig index c89efb8..2a7ec0e 100644 --- a/cli/compile.zig +++ b/cli/compile.zig @@ -20,8 +20,8 @@ pub fn initDataModule(build: *std.Build) !*std.Build.Module { const writer = buf.writer(); const paths = .{ + "init/src/main.zig", "demo/build.zig", - "demo/src/main.zig", "demo/src/app/middleware/DemoMiddleware.zig", "demo/src/app/views/init.zig", "demo/src/app/views/init/index.zmpl", diff --git a/demo/src/app/views/init/_content.zmpl b/demo/src/app/views/init/_content.zmpl index 03da91f..a10ec8a 100644 --- a/demo/src/app/views/init/_content.zmpl +++ b/demo/src/app/views/init/_content.zmpl @@ -1,4 +1,4 @@ -@args message: *ZmplValue +@args message: []const u8

{{message}}

@@ -10,9 +10,18 @@ -
Visit jetzig.dev to get started. -
Join our Discord server and introduce yourself:
+
+
Visit jetzig.dev to get started.
+
Join our Discord server and introduce yourself:
+
https://discord.gg/eufqssz7X6
+
+ Follow the project on + + GitHub + GitHub Repo stars + +
diff --git a/demo/src/app/views/init/index.zmpl b/demo/src/app/views/init/index.zmpl index 100d770..17e3935 100644 --- a/demo/src/app/views/init/index.zmpl +++ b/demo/src/app/views/init/index.zmpl @@ -11,11 +11,11 @@
-

{{.message_param}}

+

{{$.message_param}}

- @partial init/content(message: .welcome_message) + @partial init/content(message: $.welcome_message)
diff --git a/demo/src/main.zig b/demo/src/main.zig index 99a75d3..a1f6b22 100644 --- a/demo/src/main.zig +++ b/demo/src/main.zig @@ -12,11 +12,11 @@ pub const jetzig_options = struct { /// Middleware chain. Add any custom middleware here, or use middleware provided in /// `jetzig.middleware` (e.g. `jetzig.middleware.HtmxMiddleware`). pub const middleware: []const type = &.{ + // jetzig.middleware.AntiCsrfMiddleware, + // jetzig.middleware.CompressionMiddleware, + // @import("app/middleware/DemoMiddleware.zig"), jetzig.middleware.AuthMiddleware, - // jetzig.middleware.AntiCsrfMiddleware, - // jetzig.middleware.HtmxMiddleware, - // jetzig.middleware.CompressionMiddleware, - // @import("app/middleware/DemoMiddleware.zig"), + jetzig.middleware.HtmxMiddleware, }; // Maximum bytes to allow in request body. diff --git a/init/src/main.zig b/init/src/main.zig new file mode 100644 index 0000000..454a95a --- /dev/null +++ b/init/src/main.zig @@ -0,0 +1,238 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const jetzig = @import("jetzig"); +const zmd = @import("zmd"); + +pub const routes = @import("routes"); +pub const static = @import("static"); + +// Override default settings in `jetzig.config` here: +pub const jetzig_options = struct { + /// Middleware chain. Add any custom middleware here, or use middleware provided in + /// `jetzig.middleware` (e.g. `jetzig.middleware.HtmxMiddleware`). + pub const middleware: []const type = &.{ + // jetzig.middleware.AuthMiddleware, + // jetzig.middleware.AntiCsrfMiddleware, + // jetzig.middleware.HtmxMiddleware, + // jetzig.middleware.CompressionMiddleware, + // @import("app/middleware/DemoMiddleware.zig"), + }; + + // Maximum bytes to allow in request body. + pub const max_bytes_request_body: usize = std.math.pow(usize, 2, 16); + + // Maximum filesize for `public/` content. + pub const max_bytes_public_content: usize = std.math.pow(usize, 2, 20); + + // Maximum filesize for `static/` content (applies only to apps using `jetzig.http.StaticRequest`). + pub const max_bytes_static_content: usize = std.math.pow(usize, 2, 18); + + // Maximum length of a header name. There is no limit imposed by the HTTP specification but + // AWS load balancers reference 40 as a limit so we use that as a baseline: + // https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/API_HttpHeaderConditionConfig.html + // This can be increased if needed. + pub const max_bytes_header_name: u16 = 40; + + /// Maximum number of `multipart/form-data`-encoded fields to accept per request. + pub const max_multipart_form_fields: usize = 20; + + // Log message buffer size. Log messages exceeding this size spill to heap with degraded + // performance. Log messages should aim to fit in the message buffer. + pub const log_message_buffer_len: usize = 4096; + + // Maximum log pool size. When a log buffer is no longer required it is returned to a pool + // for recycling. When logging i/o is slow, a high volume of requests will result in this + // pool growing. When the pool size reaches the maximum value defined here, log events are + // freed instead of recycled. + pub const max_log_pool_len: usize = 256; + + // Number of request threads. Defaults to number of detected CPUs. + pub const thread_count: ?u16 = null; + + // Number of response worker threads. + pub const worker_count: u16 = 4; + + // Total number of connections managed by worker threads. + pub const max_connections: u16 = 512; + + // Per-thread stack memory to use before spilling into request arena (possibly with allocations). + pub const buffer_size: usize = 64 * 1024; + + // The size of each item in the available memory pool used by requests for rendering. + // Total retained allocation: `worker_count * max_connections`. + pub const arena_size: usize = 1024 * 1024; + + // Path relative to cwd() to serve public content from. Symlinks are not followed. + pub const public_content_path = "public"; + + /// Request path to map to public directory, e.g. if `public_routing_path` is `"/foo"` then a + /// request to `/foo/bar.png` will serve static content found in `public/bar.png` + pub const public_routing_path = "/"; + + // HTTP buffer. Must be large enough to store all headers. This should typically not be modified. + pub const http_buffer_size: usize = std.math.pow(usize, 2, 16); + + // The number of worker threads to spawn on startup for processing Jobs (NOT the number of + // HTTP server worker threads). + pub const job_worker_threads: usize = 4; + + // Duration before looking for more Jobs when the queue is found to be empty, in + // milliseconds. + pub const job_worker_sleep_interval_ms: usize = 10; + + /// Database Schema. Set to `@import("Schema")` to load `src/app/database/Schema.zig`. + pub const Schema = @import("Schema"); + + /// HTTP cookie configuration + pub const cookies: jetzig.http.Cookies.CookieOptions = switch (jetzig.environment) { + .development, .testing => .{ + .domain = "localhost", + .path = "/", + }, + .production => .{ + .same_site = .lax, + .secure = true, + .http_only = true, + .path = "/", + }, + }; + + /// Key-value store options. + /// Available backends: + /// * memory: Simple, in-memory hashmap-backed store. + /// * file: Rudimentary file-backed store. + /// * valkey: Valkey-backed store with connection pool. + /// + /// When using `.file` or `.valkey` backend, you must also set `.file_options` or + /// `.valkey_options` respectively. + /// + /// ## File backend: + // .backend = .file, + // .file_options = .{ + // .path = "/path/to/jetkv-store.db", + // .truncate = false, // Set to `true` to clear the store on each server launch. + // .address_space_size = jetzig.jetkv.JetKV.FileBackend.addressSpace(4096), + // }, + // + // ## Valkey backend + // .backend = .valkey, + // .valkey_options = .{ + // .host = "localhost", + // .port = 6379, + // .timeout = 1000, // in milliseconds, i.e. 1 second. + // .connect = .lazy, // Connect on first use, or `auto` to connect on server startup. + // .buffer_size = 8192, + // .pool_size = 8, + // }, + /// Available configuration options for `store`, `job_queue`, and `cache` are identical. + /// + /// For production deployment, the `valkey` backend is recommended for all use cases. + /// + /// The general-purpose key-value store is exposed as `request.store` in views and is also + /// available in as `env.store` in all jobs/mailers. + pub const store: jetzig.kv.Store.Options = .{ + .backend = .memory, + }; + + /// Job queue options. Identical to `store` options, but allows using different + /// backends (e.g. `.memory` for key-value store, `.file` for jobs queue. + /// The job queue is managed internally by Jetzig. + pub const job_queue: jetzig.kv.Store.Options = .{ + .backend = .memory, + }; + + /// Cache options. Identical to `store` options, but allows using different + /// backends (e.g. `.memory` for key-value store, `.file` for cache. + pub const cache: jetzig.kv.Store.Options = .{ + .backend = .memory, + }; + + /// SMTP configuration for Jetzig Mail. It is recommended to use a local SMTP relay, + /// e.g.: https://github.com/juanluisbaptiste/docker-postfix + /// + /// Each configuration option can be overridden with environment variables: + /// `JETZIG_SMTP_PORT` + /// `JETZIG_SMTP_ENCRYPTION` + /// `JETZIG_SMTP_HOST` + /// `JETZIG_SMTP_USERNAME` + /// `JETZIG_SMTP_PASSWORD` + // pub const smtp: jetzig.mail.SMTPConfig = .{ + // .port = 25, + // .encryption = .none, // .insecure, .none, .tls, .start_tls + // .host = "localhost", + // .username = null, + // .password = null, + // }; + + /// Force email delivery in development mode (instead of printing email body to logger). + pub const force_development_email_delivery = false; + + // Set custom fragments for rendering markdown templates. Any values will fall back to + // defaults provided by Zmd (https://github.com/jetzig-framework/zmd/blob/main/src/zmd/html.zig). + pub const markdown_fragments = struct { + pub const root = .{ + "
", + "
", + }; + pub const h1 = .{ + "

", + "

", + }; + pub const h2 = .{ + "

", + "

", + }; + pub const h3 = .{ + "

", + "

", + }; + pub const paragraph = .{ + "

", + "

", + }; + pub const code = .{ + "", + "", + }; + + pub const unordered_list = .{ + "", + }; + + pub const ordered_list = .{ + "", + }; + + pub fn block(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 { + return try std.fmt.allocPrint(allocator, + \\
{s}
+ , .{ node.meta, node.content }); + } + + pub fn link(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 { + return try std.fmt.allocPrint(allocator, + \\{1s} + , .{ node.href.?, node.title.? }); + } + }; +}; + +pub fn init(app: *jetzig.App) !void { + _ = app; + // Example custom route: + // app.route(.GET, "/custom/:id/foo/bar", @import("app/views/custom/foo.zig"), .bar); +} + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = if (builtin.mode == .Debug) gpa.allocator() else std.heap.c_allocator; + defer if (builtin.mode == .Debug) std.debug.assert(gpa.deinit() == .ok); + + var app = try jetzig.init(allocator); + defer app.deinit(); + + try app.start(routes, .{}); +}