fediglam/src/main/api/users.zig
2022-09-29 14:52:01 -07:00

119 lines
2.8 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 Role = enum {
user,
admin,
};
pub const CreateOptions = struct {
invite_id: ?Uuid = null,
email: ?[]const u8 = null,
role: Role = .user,
};
fn lookupByUsernameInternal(db: anytype, username: []const u8, community_id: Uuid) CreateError!?Uuid {
return if (db.queryRow(
std.meta.Tuple(&.{Uuid}),
"SELECT user.id FROM user WHERE community_id = $1 AND username = $2",
.{ community_id, username },
null,
) catch return error.DbError) |result|
result[0]
else
null;
}
pub fn lookupByUsername(db: anytype, username: []const u8, community_id: Uuid) CreateError!Uuid {
return (lookupByUsernameInternal(db, username, community_id) catch return error.DbError) orelse error.NotFound;
}
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 lookupByUsernameInternal(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(
std.meta.Tuple(&.{ []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],
};
}