Files
gerbeur/api/services/user-service.ts
2026-03-16 07:34:49 +00:00

141 lines
3.4 KiB
TypeScript

import {
APIErrorCode,
APIException,
type RegisterUserRequest,
type UpdateUserRequest,
type User,
} from "../model/interfaces.ts";
import { db, isUserRow, userApiToRow, userRowToApi } from "../model/db.ts";
import { hashPassword } from "../lib/jwt.ts";
export async function createUser(
request: RegisterUserRequest,
): Promise<User> {
const userId = crypto.randomUUID();
const createdAt = new Date();
const existingUser = db.prepare(
"SELECT id FROM users WHERE username = ?;",
).get(request.username);
if (existingUser) {
throw new APIException(
APIErrorCode.VALIDATION_ERROR,
400,
"Username already exists",
);
}
const passwordHash = await hashPassword(request.password);
db.prepare(
`INSERT INTO users (id, username, password_hash, is_admin, created_at)
VALUES (?, ?, ?, ?, ?);`,
).run(
userId,
request.username,
passwordHash,
0,
createdAt.toISOString(),
);
return {
id: userId,
username: request.username,
passwordHash,
isAdmin: false,
createdAt,
};
}
export function getUserById(userId: string): User {
const userRow = db.prepare(
`SELECT id, username, password_hash, is_admin, created_at, avatar_mime
FROM users WHERE id = ?`,
).get(userId);
if (!userRow || !isUserRow(userRow)) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "User not found");
}
return userRowToApi(userRow);
}
export function getUserByUsername(username: string): User {
const userRow = db.prepare(
`SELECT id, username, password_hash, is_admin, created_at, avatar_mime
FROM users WHERE username = ?`,
).get(username);
if (!userRow || !isUserRow(userRow)) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "User not found");
}
return userRowToApi(userRow);
}
export function listUsers(): User[] {
const userRows = db.prepare(
`SELECT id, username, password_hash, is_admin, created_at, avatar_mime FROM users`,
).all();
if (!userRows || !userRows.every(isUserRow)) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "No user found");
}
return userRows.map(userRowToApi);
}
export async function updateUser(
userId: string,
request: UpdateUserRequest,
): Promise<User> {
const user = getUserById(userId);
const { password, ...requestFields } = request;
const updatedUser: User = {
...user,
passwordHash: password ? await hashPassword(password) : user.passwordHash,
...requestFields,
};
const updatedUserRow = userApiToRow(updatedUser);
const userResult = db.prepare(
`UPDATE users SET username = ?, password_hash = ?, is_admin = ? WHERE id = ?`,
).run(
updatedUserRow.username,
updatedUserRow.password_hash,
updatedUserRow.is_admin,
updatedUserRow.id,
);
if (userResult.changes === 0) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Dump not found");
}
return updatedUser;
}
export function updateUserAvatar(userId: string, mime: string): void {
const result = db.prepare(
`UPDATE users SET avatar_mime = ? WHERE id = ?`,
).run(mime, userId);
if (result.changes === 0) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "User not found");
}
}
export function deleteUser(userId: string): void {
const result = db.prepare(
`DELETE FROM users WHERE id = ?;`,
).run(userId);
if (result.changes === 0) {
throw new APIException(APIErrorCode.NOT_FOUND, 404, "User not found");
}
}