const std = @import("std"); const util = @import("util"); const Uuid = util.Uuid; const DateTime = util.DateTime; pub const Role = enum { user, admin, }; pub const CreateOptions = struct { invite_id: ?Uuid = null, email: ?[]const u8 = null, role: Role = .user, }; /// Creates a local account with the given information pub fn create( db: anytype, for_actor: Uuid, password_hash: []const u8, options: CreateOptions, alloc: std.mem.Allocator, ) !void { const tx = try db.beginOrSavepoint(); errdefer tx.rollback(); tx.insert("account", .{ .id = for_actor, .invite_id = options.invite_id, .email = options.email, .kind = options.role, }, alloc) catch return error.DatabaseFailure; tx.insert("password", .{ .account_id = for_actor, .hash = password_hash, .changed_at = DateTime.now(), }, alloc) catch return error.DatabaseFailure; tx.commitOrRelease() catch return error.DatabaseFailure; } pub const Credentials = struct { account_id: Uuid, password_hash: []const u8, }; pub fn getCredentialsByUsername(db: anytype, username: []const u8, community_id: Uuid, alloc: std.mem.Allocator) !Credentials { return db.queryRow( Credentials, \\SELECT account.id as account_id, password.hash as password_hash \\FROM password \\ JOIN account \\ JOIN actor \\ ON password.account_id = account.id AND account.id = actor.id \\WHERE actor.username = $1 \\ AND actor.community_id = $2 \\LIMIT 1 , .{ username, community_id }, alloc, ) catch |err| return switch (err) { error.NoRows => error.InvalidLogin, else => |e| return e, }; }