Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 6x 24x 24x 24x 12x 12x 12x 7x 7x 5x 5x 7x 11x | import { z } from "zod";
import type { User } from "@/types";
import type { Database } from "@/lib/db";
import { AccountDeletionService } from "@/server/services/AccountDeletionService";
import { UserRepository } from "@/server/repositories/implementations/UserRepository";
const UpdateUserSchema = z.object({
// name-only flow: trim, bound length, allow empty (= clear). Unknown keys stripped.
name: z.string().trim().max(80).optional(),
});
export class UserController {
constructor(
private userRepo: UserRepository,
private db: Database,
) {}
static fromDb(db: Database): UserController {
return new UserController(new UserRepository(db), db);
}
async getCurrentUser(userId: string): Promise<User> {
const user = await this.userRepo.findById(userId);
Iif (!user) {
throw new Error("User not found");
}
return user;
}
async updateCurrentUser(userId: string, data: unknown): Promise<User> {
const parsed = UpdateUserSchema.parse(data);
// No-op when there is nothing to set (email-only or empty body). An empty
// patch would issue an UPDATE with no SET clause → DB error; return as-is.
if (parsed.name === undefined) return this.getCurrentUser(userId);
// `user.name` is NOT NULL (better-auth's canonical schema), so a cleared
// name falls back to the email local-part rather than writing null. The
// session display name (façade getSession() + client useSession()) reads
// this same `user` row, so this single write updates the header too.
const current = await this.getCurrentUser(userId);
const name = parsed.name === "" ? current.email.split("@")[0] : parsed.name;
return this.userRepo.update(userId, { name });
}
async deleteCurrentUser(userId: string): Promise<void> {
await new AccountDeletionService(this.db).deleteCurrentUser(userId);
}
}
|