fediglam/src/main/api/users.zig

129 lines
3.0 KiB
Zig

const std = @import("std");
const util = @import("util");
const auth = @import("./auth.zig");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
const getRandom = @import("../api.zig").getRandom;
const UserAuthInfo = struct {
password: []const u8,
email: []const u8,
invite_used: ?Uuid,
};
pub const CreateError = error{
UsernameTaken,
DbError,
};
const DbUser = struct {
id: Uuid,
username: []const u8,
community_id: ?Uuid,
};
const DbLocalUser = struct {
user_id: Uuid,
invite_id: ?Uuid,
email: ?[]const u8,
};
pub const CreateOptions = struct {
invite_id: ?Uuid = null,
email: ?[]const u8 = null,
};
fn lookupSystemUserByUsername(db: anytype, username: []const u8) !?Uuid {
return if (try db.queryRow(
&.{Uuid},
"SELECT user.id FROM user WHERE community_id IS NULL AND username = $1",
.{username},
null,
)) |result|
result[0]
else
null;
}
fn lookupUserByUsername(db: anytype, username: []const u8, community_id: Uuid) !?Uuid {
return if (try db.queryRow(
&.{Uuid},
"SELECT user.id FROM user WHERE community_id = $1 AND username = $2",
.{ community_id, username },
null,
)) |result|
result[0]
else
null;
}
pub fn lookupByUsername(db: anytype, username: []const u8, community_id: ?Uuid) !?Uuid {
return if (community_id) |id|
lookupUserByUsername(db, username, id) catch return error.DbError
else
lookupSystemUserByUsername(db, username) catch return error.DbError;
}
pub fn create(
db: anytype,
username: []const u8,
password: []const u8,
community_id: ?Uuid,
options: CreateOptions,
password_alloc: std.mem.Allocator,
) CreateError!Uuid {
const id = Uuid.randV4(getRandom());
if ((try lookupByUsername(db, username, community_id)) != null) {
return error.UsernameTaken;
}
db.insert("user", .{
.id = id,
.username = username,
.community_id = community_id,
}) catch return error.DbError;
try auth.passwords.create(db, id, password, password_alloc);
db.insert("local_user", .{
.user_id = id,
.invite_id = options.invite_id,
.email = options.email,
}) catch return error.DbError;
return id;
}
pub const User = struct {
id: Uuid,
username: []const u8,
host: []const u8,
community_id: Uuid,
created_at: DateTime,
};
pub fn get(db: anytype, id: Uuid, alloc: std.mem.Allocator) !User {
const result = (try db.queryRow(
&.{ []const u8, []const u8, Uuid, DateTime },
\\SELECT user.username, community.host, community.id, user.created_at
\\FROM user JOIN community ON user.community_id = community.id
\\WHERE user.id = $1
\\LIMIT 1
,
.{id},
alloc,
)) orelse return error.NotFound;
return User{
.id = id,
.username = result[0],
.host = result[1],
.community_id = result[2],
.created_at = result[3],
};
}