const std = @import("std"); const zqlite = @import("zqlite"); const bcrypt = std.crypto.pwhash.bcrypt; const rand = std.crypto.random; const token_length: usize = 20; pub const User = struct { email: []const u8, name: []const u8, }; pub fn get_user(allocator: std.mem.Allocator, conn: zqlite.Conn, login_token: []const u8) !?User { const query = \\SELECT email, name \\FROM users \\WHERE login_token = ? ; if (try conn.row(query, .{login_token})) |row| { defer row.deinit(); return User{ .email = try allocator.dupe(u8, row.text(0)), .name = try allocator.dupe(u8, row.text(1)), }; } else { return null; } } pub fn check_password(allocator: std.mem.Allocator, conn: zqlite.Conn, email: []const u8, password: []const u8) !?User { const query = \\SELECT password_hash, email, name \\FROM users \\WHERE email = ? ; if (try conn.row(query, .{email})) |row| { defer row.deinit(); const hash = row.text(0); const verify_options = bcrypt.VerifyOptions{ .silently_truncate_password = false }; std.time.sleep(100000000 + rand.intRangeAtMost(u32, 0, 100000000)); bcrypt.strVerify(hash, password, verify_options) catch { return null; }; return User{ .email = try allocator.dupe(u8, row.text(1)), .name = try allocator.dupe(u8, row.text(2)), }; } else { std.time.sleep(500000000 + rand.intRangeAtMost(u32, 0, 500000000)); return null; } } pub fn generate_login_token(allocator: std.mem.Allocator, conn: zqlite.Conn, email: []const u8) ![]const u8 { const query = \\UPDATE users \\SET login_token = ?, updated_at = datetime() \\WHERE email = ? ; const token = try random_token(allocator); try conn.exec(query, .{ token, email }); return token; } fn random_token(allocator: std.mem.Allocator) ![]const u8 { // Generate random bytes var bytes: [token_length]u8 = undefined; rand.bytes(&bytes); // Encode as base64 const Encoder = std.base64.standard.Encoder; const encoded_length = Encoder.calcSize(token_length); const token = try allocator.alloc(u8, encoded_length); _ = Encoder.encode(token, &bytes); return token; } pub fn remove_login_token(conn: zqlite.Conn, email: []const u8) !void { const query = \\ UPDATE users \\ SET login_token = NULL, updated_at = datetime() \\ WHERE email = ? ; try conn.exec(query, .{email}); }