Extend auth + demo to show sign out

Add Jetzig module to migrations/seeders to make it available for import.
This commit is contained in:
Bob Farrell 2025-05-14 18:59:26 +01:00
parent 7be1d137fc
commit 988cd9c3d8
9 changed files with 61 additions and 32 deletions

View File

@ -82,6 +82,9 @@ pub fn build(b: *std.Build) !void {
b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory"); b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory");
b.modules.put("jetquery_seeder", jetquery_dep.module("jetquery_seeder")) catch @panic("Out of memory"); b.modules.put("jetquery_seeder", jetquery_dep.module("jetquery_seeder")) catch @panic("Out of memory");
jetquery_dep.module("jetquery_seeder").addImport("jetzig", jetzig_module);
jetquery_dep.module("jetquery_migrate").addImport("jetzig", jetzig_module);
const smtp_client_dep = b.dependency("smtp_client", .{ const smtp_client_dep = b.dependency("smtp_client", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
@ -192,6 +195,9 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
const jetquery_seeder_module = jetzig_dep.module("jetquery_seeder"); const jetquery_seeder_module = jetzig_dep.module("jetquery_seeder");
const jetquery_reflect_module = jetquery_dep.module("jetquery_reflect"); const jetquery_reflect_module = jetquery_dep.module("jetquery_reflect");
jetquery_dep.module("jetquery_seeders").addImport("jetzig", jetzig_module);
jetquery_dep.module("jetquery_migrations").addImport("jetzig", jetzig_module);
const build_options = b.addOptions(); const build_options = b.addOptions();
build_options.addOption(Environment, "environment", environment); build_options.addOption(Environment, "environment", environment);
build_options.addOption(bool, "build_static", build_static); build_options.addOption(bool, "build_static", build_static);

View File

@ -33,8 +33,8 @@
.hash = "httpz-0.0.0-PNVzrEK4BgBpHQGA2m0RPqPGEjnTdDXHodBwzjYDrmps", .hash = "httpz-0.0.0-PNVzrEK4BgBpHQGA2m0RPqPGEjnTdDXHodBwzjYDrmps",
}, },
.jetquery = .{ .jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/ed6b42e196573365057d8e5d17ff85b456b11e68.tar.gz", .url = "https://github.com/jetzig-framework/jetquery/archive/8d3dd2ae107767d9c72161a1e4adbb602a16e619.tar.gz",
.hash = "jetquery-0.0.0-TNf3zs24BgCYXZZvMKUbEMb6hRfrMmYHJgHTNGWZM5B1", .hash = "jetquery-0.0.0-TNf3ziK5BgBGDf4BI6tNfTTQ3TpHIGjuonWrlvs8meVg",
}, },
}, },

View File

@ -10,8 +10,8 @@
.hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH",
}, },
.jetquery = .{ .jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/ed6b42e196573365057d8e5d17ff85b456b11e68.tar.gz", .url = "https://github.com/jetzig-framework/jetquery/archive/8d3dd2ae107767d9c72161a1e4adbb602a16e619.tar.gz",
.hash = "jetquery-0.0.0-TNf3zs24BgCYXZZvMKUbEMb6hRfrMmYHJgHTNGWZM5B1", .hash = "jetquery-0.0.0-TNf3ziK5BgBGDf4BI6tNfTTQ3TpHIGjuonWrlvs8meVg",
}, },
}, },
.paths = .{ .paths = .{

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const jetquery = @import("jetquery"); const jetquery = @import("jetquery");
const t = jetquery.schema.table; const t = jetquery.schema.table;
const jetzig = @import("jetzig");
pub fn up(repo: anytype) !void { pub fn up(repo: anytype) !void {
try repo.createTable( try repo.createTable(

View File

@ -1,11 +1,13 @@
const std = @import("std"); const std = @import("std");
const jetzig = @import("jetzig");
pub fn run(repo: anytype) !void { pub fn run(repo: anytype) !void {
try repo.insert( try repo.insert(
.User, .User,
.{ .{
.email = "iguana@jetzig.dev", .email = "iguana@jetzig.dev",
.password_hash = "not_secure", .password_hash = try jetzig.auth.hashPassword(repo.allocator, "password"),
}, },
); );
@ -13,7 +15,7 @@ pub fn run(repo: anytype) !void {
.User, .User,
.{ .{
.email = "admin@jetzig.dev", .email = "admin@jetzig.dev",
.password_hash = "do_not_use", .password_hash = try jetzig.auth.hashPassword(repo.allocator, "admin"),
}, },
); );
} }

View File

@ -3,38 +3,44 @@ const jetzig = @import("jetzig");
const auth = @import("jetzig").auth; const auth = @import("jetzig").auth;
pub fn index(request: *jetzig.Request) !jetzig.View { pub fn index(request: *jetzig.Request) !jetzig.View {
var root = try request.data(.object);
if (request.middleware(.auth).user) |user| {
try root.put("user", .{ .email = user.email });
}
return request.render(.ok); return request.render(.ok);
} }
pub fn post(request: *jetzig.Request) !jetzig.View { pub fn post(request: *jetzig.Request) !jetzig.View {
const Login = struct { const Logout = struct { logout: []const u8 };
email: []const u8, const Login = struct { email: []const u8, password: []const u8 };
password: []const u8,
}; if (try request.expectParams(Logout)) |_| {
try auth.signOut(request);
return request.redirect("/login", .found);
}
const params = try request.expectParams(Login) orelse { const params = try request.expectParams(Login) orelse {
return request.fail(.forbidden); return request.fail(.forbidden);
}; };
// Lookup the user by email // Lookup the user by email
const query = jetzig.database.Query(.User).findBy( const user = try jetzig.database.Query(.User).findBy(
.{ .email = params.email }, .{ .email = params.email },
); ).execute(request.repo) orelse {
const user = try request.repo.execute(query) orelse {
return request.fail(.forbidden); return request.fail(.forbidden);
}; };
// Check that the password matches // Check that the password matches
if (try auth.verifyPassword( if (!try auth.verifyPassword(
request.allocator, request.allocator,
user.password_hash, user.password_hash,
params.password, params.password,
)) { )) return request.fail(.forbidden);
try auth.signIn(request, user.id);
return request.redirect("/", .found); try auth.signIn(request, user.id);
} return request.redirect("/login", .found);
return request.fail(.forbidden);
} }
test "post" { test "post" {

View File

@ -1,7 +1,16 @@
<form method="post" id="login"> @if ($.user) |user|
<input type="email" name="email" placeholder="name@example.com"> <div>Logged in as {{user.email}}</div>
<label for="email">Email address</label>
<input type="password" name="password" placeholder="Password"> <form method="post" id="logout">
<label for="password">Password</label> <input type="hidden" name="logout" value="1" />
<button type="submit" form="login">Sign in</button> <button type="submit" form="logout">Sign Out</button>
</form> </form>
@else
<form method="post" id="login">
<input type="email" name="email" placeholder="name@example.com">
<label for="email">Email address</label>
<input type="password" name="password" placeholder="Password">
<label for="password">Password</label>
<button type="submit" form="login">Sign in</button>
</form>
@end

View File

@ -12,11 +12,11 @@ pub const jetzig_options = struct {
/// Middleware chain. Add any custom middleware here, or use middleware provided in /// Middleware chain. Add any custom middleware here, or use middleware provided in
/// `jetzig.middleware` (e.g. `jetzig.middleware.HtmxMiddleware`). /// `jetzig.middleware` (e.g. `jetzig.middleware.HtmxMiddleware`).
pub const middleware: []const type = &.{ pub const middleware: []const type = &.{
// jetzig.middleware.AuthMiddleware, jetzig.middleware.AuthMiddleware,
// jetzig.middleware.AntiCsrfMiddleware, // jetzig.middleware.AntiCsrfMiddleware,
// jetzig.middleware.HtmxMiddleware, // jetzig.middleware.HtmxMiddleware,
// jetzig.middleware.CompressionMiddleware, // jetzig.middleware.CompressionMiddleware,
// @import("app/middleware/DemoMiddleware.zig"), // @import("app/middleware/DemoMiddleware.zig"),
}; };
// Maximum bytes to allow in request body. // Maximum bytes to allow in request body.

View File

@ -22,6 +22,11 @@ pub fn signIn(request: *jetzig.Request, user_id: anytype) !void {
try session.put("_jetzig_user_id", user_id); try session.put("_jetzig_user_id", user_id);
} }
pub fn signOut(request: *jetzig.Request) !void {
var session = try request.session();
_ = try session.remove("_jetzig_user_id");
}
pub fn verifyPassword( pub fn verifyPassword(
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
hash: []const u8, hash: []const u8,