diff --git a/build.zig.zon b/build.zig.zon index e809431..0ac5933 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .hash = "1220482f07f2bbaef335f20d6890c15a1e14739950b784232bc69182423520e058a5", }, .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/4457eba50bd2eff3743601aa00adbffebcd4207a.tar.gz", - .hash = "122001d661e534ef59fc20936330bb0c3068c8aaf1b7c60f9dde9e58d9a536918754", + .url = "https://github.com/jetzig-framework/zmpl/archive/c14683521ca48c1de0a9b2d61dfb145e1bc4dac1.tar.gz", + .hash = "122093b741ef4aff151e916fc6005cb0c2aed747a34b77c0d4b45099ea2b561df9c7", }, .jetkv = .{ .url = "https://github.com/jetzig-framework/jetkv/archive/edfda9108c857dd5a04f87ae48667d3ed57612d9.tar.gz", diff --git a/cli/commands/bundle.zig b/cli/commands/bundle.zig index 7447bf6..87448ae 100644 --- a/cli/commands/bundle.zig +++ b/cli/commands/bundle.zig @@ -8,7 +8,7 @@ const util = @import("../util.zig"); /// Command line options for the `bundle` command. pub const Options = struct { optimize: enum { Debug, ReleaseFast, ReleaseSmall } = .ReleaseFast, - arch: enum { x86_64, aarch64, default } = .default, + arch: enum { x86, x86_64, aarch64, default } = .default, os: enum { linux, macos, windows, default } = .default, pub const meta = .{ @@ -190,6 +190,7 @@ fn zig_build_install(allocator: std.mem.Allocator, path: []const u8, options: Op try target_buf.append("-Dtarget="); switch (options.arch) { + .x86 => try target_buf.append("x86"), .x86_64 => try target_buf.append("x86_64"), .aarch64 => try target_buf.append("aarch64"), .default => try target_buf.append(@tagName(builtin.cpu.arch)), diff --git a/cli/commands/generate/job.zig b/cli/commands/generate/job.zig index a58bcbe..5272109 100644 --- a/cli/commands/generate/job.zig +++ b/cli/commands/generate/job.zig @@ -51,7 +51,7 @@ pub fn run(allocator: std.mem.Allocator, cwd: std.fs.Dir, args: [][]const u8, he \\// * env: Provides the following fields: \\// - logger: Logger attached to the same stream as the Jetzig server. \\// - environment: Enum of `{ production, development }`. - \\pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, logger: jetzig.Logger) !void { + \\pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv) !void { \\ _ = allocator; \\ _ = params; \\ // Job execution code goes here. Add any code that you would like to run in the background. diff --git a/cli/commands/generate/mailer.zig b/cli/commands/generate/mailer.zig index aa963aa..2d50e00 100644 --- a/cli/commands/generate/mailer.zig +++ b/cli/commands/generate/mailer.zig @@ -39,45 +39,34 @@ pub fn run(allocator: std.mem.Allocator, cwd: std.fs.Dir, args: [][]const u8, he }; try mailer_file.writeAll( - \\const std = @import("std"); - \\const jetzig = @import("jetzig"); - \\ - \\// Default values for this mailer. - \\pub const defaults: jetzig.mail.DefaultMailParams = .{ - \\ .from = "no-reply@example.com", - \\ .subject = "Default subject", - \\}; - \\ \\// The `deliver` function is invoked every time this mailer is used to send an email. - \\// Use this function to modify mail parameters before the mail is delivered, or simply - \\// to log all uses of this mailer. + \\// Use this function to set default mail params (e.g. a default `from` address or + \\// `subject`) before the mail is delivered. \\// - \\// To use this mailer from a request: - \\// ``` - \\// const mail = request.mail("", .{ .to = &.{"user@example.com"} }); - \\// try mail.deliver(.background, .{}); - \\// ``` \\// A mailer can provide two Zmpl templates for rendering email content: \\// * `src/app/mailers//html.zmpl \\// * `src/app/mailers//text.zmpl \\// \\// Arguments: \\// * allocator: Arena allocator for use during the mail delivery process. - \\// * mail: Mail parameters. Inspect or override any values assigned when the mail was created. - \\// * params: Params assigned to a mail (from a request, any values added to `data`). Params - \\// can be modified before email delivery. + \\// * mail: Mail parameters (from, to, subject, etc.). Inspect or override any values + \\// assigned when the mail was created. + \\// * data: Provides `data.string()` etc. for generating Jetzig Values. + \\// * params: Template data for `text.zmpl` and `html.zmpl`. Inherits all response data + \\// assigned in a view function and can be modified for email-specific content. \\// * env: Provides the following fields: \\// - logger: Logger attached to the same stream as the Jetzig server. \\// - environment: Enum of `{ production, development }`. \\pub fn deliver( \\ allocator: std.mem.Allocator, \\ mail: *jetzig.mail.MailParams, + \\ data: *jetzig.data.Data, \\ params: *jetzig.data.Value, \\ env: jetzig.jobs.JobEnv, \\) !void { \\ _ = allocator; + \\ _ = data; \\ _ = params; - \\ \\ try env.logger.INFO("Delivering email with subject: '{?s}'", .{mail.get(.subject)}); \\} \\ diff --git a/demo/src/app/mailers/welcome.zig b/demo/src/app/mailers/welcome.zig index e4dc1d0..87e0d83 100644 --- a/demo/src/app/mailers/welcome.zig +++ b/demo/src/app/mailers/welcome.zig @@ -17,20 +17,23 @@ pub const defaults: jetzig.mail.DefaultMailParams = .{ // // Arguments: // * allocator: Arena allocator for use during the mail delivery process. -// * mail: Mail parameters. Inspect or override any values assigned when the mail was created. -// * params: Params assigned to a mail (from a request, any values added to `data`). Params -// can be modified before email delivery. +// * mail: Mail parameters (from, to, subject, etc.). Inspect or override any values +// assigned when the mail was created. +// * data: Provides `data.string()` etc. for generating Jetzig Values. +// * params: Template data for `text.zmpl` and `html.zmpl`. Inherits all response data +// assigned in a view function and can be modified for email-specific content. // * env: Provides the following fields: // - logger: Logger attached to the same stream as the Jetzig server. // - environment: Enum of `{ production, development }`. pub fn deliver( allocator: std.mem.Allocator, mail: *jetzig.mail.MailParams, + data: *jetzig.data.Data, params: *jetzig.data.Value, env: jetzig.jobs.JobEnv, ) !void { _ = allocator; - _ = params; + try params.put("email_message", data.string("Custom email message")); try env.logger.INFO("Delivering email with subject: '{?s}'", .{mail.get(.subject)}); } diff --git a/demo/src/app/mailers/welcome/html.zmpl b/demo/src/app/mailers/welcome/html.zmpl index dc4dd40..671b21d 100644 --- a/demo/src/app/mailers/welcome/html.zmpl +++ b/demo/src/app/mailers/welcome/html.zmpl @@ -1 +1,2 @@
{{.message}}
+
{{.email_message}}
diff --git a/demo/src/app/mailers/welcome/text.zmpl b/demo/src/app/mailers/welcome/text.zmpl index f9f2e47..02af048 100644 --- a/demo/src/app/mailers/welcome/text.zmpl +++ b/demo/src/app/mailers/welcome/text.zmpl @@ -1 +1,3 @@ {{.message}} + +{{.email_message}} diff --git a/demo/src/app/views/background_jobs.zig b/demo/src/app/views/background_jobs.zig index 9707dc3..d73a53e 100644 --- a/demo/src/app/views/background_jobs.zig +++ b/demo/src/app/views/background_jobs.zig @@ -3,18 +3,14 @@ const jetzig = @import("jetzig"); /// This example demonstrates usage of Jetzig's background jobs. pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - - // Create a new job using `src/app/jobs/example_job.zig`. + // Prepare a job using `src/app/jobs/example.zig`. var job = try request.job("example"); // Add a param `foo` to the job. try job.params.put("foo", data.string("bar")); try job.params.put("id", data.integer(std.crypto.random.int(u32))); - // Schedule the job for background processing. The job is added to the queue. When the job is - // processed a new instance of `example_job` is created and its `run` function is invoked. - // All params are added above are available to the job by calling `job.params()` inside the - // `run` function. + // Schedule the job for background processing. try job.schedule(); return request.render(.ok); diff --git a/demo/src/app/views/cache.zig b/demo/src/app/views/cache.zig index 5041502..c966b17 100644 --- a/demo/src/app/views/cache.zig +++ b/demo/src/app/views/cache.zig @@ -3,7 +3,7 @@ const jetzig = @import("jetzig"); pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { var root = try data.object(); - try root.put("cached_value", try request.cache.get("example")); + try root.put("message", try request.cache.get("message")); return request.render(.ok); } diff --git a/demo/src/app/views/cache/index.zmpl b/demo/src/app/views/cache/index.zmpl index a74c6ac..4a0aae4 100644 --- a/demo/src/app/views/cache/index.zmpl +++ b/demo/src/app/views/cache/index.zmpl @@ -1,3 +1,3 @@
- Cached value: {{.cached_value}} + Cached value: {{.message}}
diff --git a/demo/src/app/views/kvstore.zig b/demo/src/app/views/kvstore.zig index b86a024..cf9d6cd 100644 --- a/demo/src/app/views/kvstore.zig +++ b/demo/src/app/views/kvstore.zig @@ -6,23 +6,25 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { // Fetch a string from the KV store. If it exists, store it in the root data object, // otherwise store a string value to be picked up by the next request. - if (try request.store.fetchRemove("example-key")) |capture| { + if (try request.store.get("example-key")) |capture| { try root.put("stored_string", capture); } else { try root.put("stored_string", null); try request.store.put("example-key", data.string("example-value")); } - // Left-pop an item from the array and store it in the root data object. This will empty the + // Left-pop an item from an array and store it in the root data object. This will empty the // array after multiple requests. + // If the array is empty or not found, append some new values to the array. if (try request.store.popFirst("example-array")) |value| { try root.put("popped", value); } else { - try root.put("popped", null); // Store some values in an array in the KV store. try request.store.append("example-array", data.string("hello")); try request.store.append("example-array", data.string("goodbye")); try request.store.append("example-array", data.string("hello again")); + + try root.put("popped", null); } return request.render(.ok); diff --git a/demo/src/app/views/mail/index.zmpl b/demo/src/app/views/mail/index.zmpl index 9483c0e..a25202b 100644 --- a/demo/src/app/views/mail/index.zmpl +++ b/demo/src/app/views/mail/index.zmpl @@ -1,3 +1,4 @@
Your email has been sent! +
{{.message}}
diff --git a/demo/src/app/views/mailers/welcome.html.zmpl b/demo/src/app/views/mailers/welcome.html.zmpl deleted file mode 100644 index 0ca781b..0000000 --- a/demo/src/app/views/mailers/welcome.html.zmpl +++ /dev/null @@ -1 +0,0 @@ -
{{.data.test}}
diff --git a/demo/src/app/views/mailers/welcome.text.zmpl b/demo/src/app/views/mailers/welcome.text.zmpl deleted file mode 100644 index d0bbaba..0000000 --- a/demo/src/app/views/mailers/welcome.text.zmpl +++ /dev/null @@ -1 +0,0 @@ -{{.data.test}} diff --git a/demo/src/app/views/welcome.html.zmpl b/demo/src/app/views/welcome.html.zmpl deleted file mode 100644 index 0fd9beb..0000000 --- a/demo/src/app/views/welcome.html.zmpl +++ /dev/null @@ -1 +0,0 @@ -
Hello
diff --git a/src/jetzig/http/Request.zig b/src/jetzig/http/Request.zig index a0f7b77..e260a06 100644 --- a/src/jetzig/http/Request.zig +++ b/src/jetzig/http/Request.zig @@ -31,55 +31,55 @@ redirected: bool = false, rendered_multiple: bool = false, rendered_view: ?jetzig.views.View = null, start_time: i128, -store: ArenaStore, -cache: ArenaStore, +store: RequestStore, +cache: RequestStore, /// Wrapper for KV store that uses the request's arena allocator for fetching values. -pub const ArenaStore = struct { +pub const RequestStore = struct { allocator: std.mem.Allocator, store: *jetzig.kv.Store, /// Put a String or into the key-value store. - pub fn get(self: ArenaStore, key: []const u8) !?*jetzig.data.Value { + pub fn get(self: RequestStore, key: []const u8) !?*jetzig.data.Value { return try self.store.get(try self.data(), key); } /// Get a String from the store. - pub fn put(self: ArenaStore, key: []const u8, value: *jetzig.data.Value) !void { + pub fn put(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void { try self.store.put(key, value); } /// Remove a String to from the key-value store and return it if found. - pub fn fetchRemove(self: ArenaStore, key: []const u8) !?*jetzig.data.Value { + pub fn fetchRemove(self: RequestStore, key: []const u8) !?*jetzig.data.Value { return try self.store.fetchRemove(try self.data(), key); } /// Remove a String to from the key-value store. - pub fn remove(self: ArenaStore, key: []const u8) !void { + pub fn remove(self: RequestStore, key: []const u8) !void { try self.store.remove(key); } /// Append a Value to the end of an Array in the key-value store. - pub fn append(self: ArenaStore, key: []const u8, value: *jetzig.data.Value) !void { + pub fn append(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void { try self.store.append(key, value); } /// Prepend a Value to the start of an Array in the key-value store. - pub fn prepend(self: ArenaStore, key: []const u8, value: *jetzig.data.Value) !void { + pub fn prepend(self: RequestStore, key: []const u8, value: *jetzig.data.Value) !void { try self.store.prepend(key, value); } /// Pop a String from an Array in the key-value store. - pub fn pop(self: ArenaStore, key: []const u8) !?*jetzig.data.Value { + pub fn pop(self: RequestStore, key: []const u8) !?*jetzig.data.Value { return try self.store.pop(try self.data(), key); } /// Left-pop a String from an Array in the key-value store. - pub fn popFirst(self: ArenaStore, key: []const u8) !?*jetzig.data.Value { + pub fn popFirst(self: RequestStore, key: []const u8) !?*jetzig.data.Value { return try self.store.popFirst(try self.data(), key); } - fn data(self: ArenaStore) !*jetzig.data.Data { + fn data(self: RequestStore) !*jetzig.data.Data { const arena_data = try self.allocator.create(jetzig.data.Data); arena_data.* = jetzig.data.Data.init(self.allocator); return arena_data; @@ -390,7 +390,10 @@ const RequestMail = struct { const text = if (self.mail_params.text) |text| mail_job.data.string(text) else null; try mail_job.params.put("text", text); - if (self.request.response_data.value) |value| try mail_job.params.put("params", value); + if (self.request.response_data.value) |value| try mail_job.params.put( + "params", + if (strategy == .now) try value.clone(self.request.allocator) else value, + ); switch (strategy) { .background => try mail_job.schedule(), diff --git a/src/jetzig/jobs/Job.zig b/src/jetzig/jobs/Job.zig index f6f9b08..1c0e80a 100644 --- a/src/jetzig/jobs/Job.zig +++ b/src/jetzig/jobs/Job.zig @@ -7,14 +7,24 @@ pub const JobDefinition = struct { runFn: *const fn (std.mem.Allocator, *jetzig.data.Value, JobEnv) anyerror!void, }; +/// Environment passed to all jobs. pub const JobEnv = struct { + /// The Jetzig server logger logger: jetzig.loggers.Logger, + /// The current server environment, `enum { development, production }` environment: jetzig.Environment.EnvironmentName, + /// All routes detected by Jetzig on startup routes: []*const jetzig.Route, + /// All mailers detected by Jetzig on startup mailers: []const jetzig.MailerDefinition, + /// All jobs detected by Jetzig on startup jobs: []const jetzig.JobDefinition, + /// Global key-value store store: *jetzig.kv.Store, + /// Global cache cache: *jetzig.kv.Store, + /// Global mutex - use with caution if it is necessary to guarantee thread safety/consistency + /// between concurrent job workers mutex: *std.Thread.Mutex, }; diff --git a/src/jetzig/mail/Job.zig b/src/jetzig/mail/Job.zig index 820b26b..88c03fa 100644 --- a/src/jetzig/mail/Job.zig +++ b/src/jetzig/mail/Job.zig @@ -34,7 +34,9 @@ pub fn run(allocator: std.mem.Allocator, params: *jetzig.data.Value, env: jetzig .defaults = mailer.defaults, }; - try mailer.deliverFn(allocator, &mail_params, params, env); + var data = jetzig.data.Data.init(allocator); + + try mailer.deliverFn(allocator, &mail_params, &data, params.get("params").?, env); const mail = jetzig.mail.Mail.init(allocator, .{ .subject = mail_params.get(.subject) orelse "(No subject)", diff --git a/src/jetzig/mail/MailerDefinition.zig b/src/jetzig/mail/MailerDefinition.zig index 2331428..923d997 100644 --- a/src/jetzig/mail/MailerDefinition.zig +++ b/src/jetzig/mail/MailerDefinition.zig @@ -4,6 +4,7 @@ const jetzig = @import("../../jetzig.zig"); pub const DeliverFn = *const fn ( std.mem.Allocator, *jetzig.mail.MailParams, + *jetzig.data.Data, *jetzig.data.Value, jetzig.jobs.JobEnv, ) anyerror!void;