114 lines
3.9 KiB
TypeScript
114 lines
3.9 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { prisma } from "@/lib/db";
|
|
import { setActiveAccountForUser } from "@/lib/account-context";
|
|
import { assertAccountOwnedByUser } from "@/lib/auth/accounts";
|
|
import { authErrorResponse } from "@/lib/auth/api";
|
|
import { requireSessionUser } from "@/lib/auth/session";
|
|
import { deleteAccountWithRelations } from "@/lib/services/delete-account";
|
|
|
|
function trimOrNull(value: string | null | undefined): string | null {
|
|
if (value == null) return null;
|
|
const trimmed = value.trim();
|
|
return trimmed || null;
|
|
}
|
|
|
|
function trimUsername(value: string | null | undefined): string | null {
|
|
if (value == null) return null;
|
|
const trimmed = value.replace(/^@/, "").trim();
|
|
return trimmed || null;
|
|
}
|
|
|
|
export async function PATCH(
|
|
request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const user = await requireSessionUser();
|
|
const { id } = await params;
|
|
await assertAccountOwnedByUser(user.id, id);
|
|
|
|
const body = (await request.json()) as {
|
|
displayName?: string | null;
|
|
username?: string | null;
|
|
persona?: string | null;
|
|
styleProfile?: string | null;
|
|
styleBenchmark?: string | null;
|
|
brief?: string | null;
|
|
productBrief?: string | null;
|
|
targetAudience?: string | null;
|
|
goals?: string | null;
|
|
};
|
|
|
|
const account = await prisma.account.update({
|
|
where: { id },
|
|
data: {
|
|
...(body.displayName !== undefined && { displayName: trimOrNull(body.displayName) }),
|
|
...(body.username !== undefined && { username: trimUsername(body.username) }),
|
|
...(body.persona !== undefined && { persona: trimOrNull(body.persona) }),
|
|
...(body.styleProfile !== undefined && { styleProfile: trimOrNull(body.styleProfile) }),
|
|
...(body.styleBenchmark !== undefined && {
|
|
styleBenchmark: trimUsername(body.styleBenchmark),
|
|
}),
|
|
...(body.brief !== undefined && { brief: trimOrNull(body.brief) }),
|
|
...(body.productBrief !== undefined && { productBrief: trimOrNull(body.productBrief) }),
|
|
...(body.targetAudience !== undefined && {
|
|
targetAudience: trimOrNull(body.targetAudience),
|
|
}),
|
|
...(body.goals !== undefined && { goals: trimOrNull(body.goals) }),
|
|
},
|
|
});
|
|
|
|
return NextResponse.json({ account });
|
|
} catch (error) {
|
|
const authRes = authErrorResponse(error);
|
|
if (authRes) return authRes;
|
|
const message = error instanceof Error ? error.message : "更新帳號失敗";
|
|
console.error("[accounts] PATCH failed:", error);
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function DELETE(
|
|
_request: Request,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const user = await requireSessionUser();
|
|
const { id } = await params;
|
|
await assertAccountOwnedByUser(user.id, id);
|
|
|
|
const isActive = user.activeAccountId === id;
|
|
let nextActiveId: string | null = user.activeAccountId ?? null;
|
|
|
|
if (isActive) {
|
|
const next = await prisma.account.findFirst({
|
|
where: { userId: user.id, id: { not: id } },
|
|
orderBy: { updatedAt: "desc" },
|
|
select: { id: true },
|
|
});
|
|
nextActiveId = next?.id ?? null;
|
|
}
|
|
|
|
await deleteAccountWithRelations(id);
|
|
|
|
if (isActive) {
|
|
if (nextActiveId) {
|
|
await setActiveAccountForUser(user.id, nextActiveId);
|
|
} else {
|
|
await prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { activeAccountId: null },
|
|
});
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ deleted: true, activeAccountId: nextActiveId });
|
|
} catch (error) {
|
|
const authRes = authErrorResponse(error);
|
|
if (authRes) return authRes;
|
|
const message = error instanceof Error ? error.message : "刪除帳號失敗";
|
|
console.error("[accounts] DELETE failed:", error);
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|