ionian/packages/server/src/db/user.ts
2024-03-29 00:19:36 +09:00

87 lines
2.6 KiB
TypeScript

import { getKysely } from "./kysely";
import { type IUser, Password, type UserAccessor, type UserCreateInput } from "../model/user";
class SqliteUser implements IUser {
readonly username: string;
readonly password: Password;
constructor(username: string, pw: Password, private kysely = getKysely()) {
this.username = username;
this.password = pw;
}
async reset_password(password: string) {
this.password.set_password(password);
await this.kysely
.updateTable("users")
.where("username", "=", this.username)
.set({ password_hash: this.password.hash, password_salt: this.password.salt })
.execute();
}
async get_permissions() {
const permissions = await this.kysely
.selectFrom("permissions")
.selectAll()
.where("username", "=", this.username)
.execute();
return permissions.map((x) => x.name);
}
async add(name: string) {
const result = await this.kysely
.insertInto("permissions")
.values({ username: this.username, name })
.onConflict((oc) => oc.doNothing())
.executeTakeFirst();
return (result.numInsertedOrUpdatedRows ?? 0n) > 0;
}
async remove(name: string) {
const result = await this.kysely
.deleteFrom("permissions")
.where("username", "=", this.username)
.where("name", "=", name)
.executeTakeFirst();
return (result.numDeletedRows ?? 0n) > 0;
}
}
export const createSqliteUserController = (kysely = getKysely()): UserAccessor => {
const createUser = async (input: UserCreateInput) => {
if (undefined !== (await findUser(input.username))) {
return undefined;
}
const user = new SqliteUser(input.username, new Password(input.password), kysely);
await kysely
.insertInto("users")
.values({ username: user.username, password_hash: user.password.hash, password_salt: user.password.salt })
.execute();
return user;
};
const findUser = async (id: string) => {
const user = await kysely
.selectFrom("users")
.selectAll()
.where("username", "=", id)
.executeTakeFirst();
if (!user) return undefined;
if (!user.password_hash || !user.password_salt) {
throw new Error("password hash or salt is missing");
}
if (user.username === null) {
throw new Error("username is null");
}
return new SqliteUser(user.username, new Password({
hash: user.password_hash,
salt: user.password_salt
}), kysely);
};
const delUser = async (id: string) => {
const result = await kysely.deleteFrom("users")
.where("username", "=", id)
.executeTakeFirst();
return (result.numDeletedRows ?? 0n) > 0;
};
return {
createUser: createUser,
findUser: findUser,
delUser: delUser,
};
};