haixunMaster/app/api/product-profiles/route.ts

137 lines
4.3 KiB
TypeScript

import { NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { getActiveAccountId } from "@/lib/account-context";
import { apiRouteErrorResponse } from "@/lib/auth/api";
import { isAccountInUserScope, requireUserAccountScope } from "@/lib/auth/user-scope";
import {
migrateOrphanProductContexts,
syncProfileContextToTopics,
validateProfileContext,
} from "@/lib/services/product-profile";
export async function GET() {
try {
const accountId = await getActiveAccountId();
const { where, accountIds } = await requireUserAccountScope(accountId);
try {
await migrateOrphanProductContexts(accountIds);
} catch (migrateErr) {
console.error("[product-profiles] migrate failed:", migrateErr);
}
const profiles = await prisma.productProfile.findMany({
where,
orderBy: { updatedAt: "desc" },
});
return NextResponse.json({ profiles });
} catch (error) {
return apiRouteErrorResponse(error, "product-profiles");
}
}
export async function POST(request: Request) {
try {
const { label, context } = (await request.json()) as {
label?: string;
context?: string;
};
if (!label?.trim()) {
return NextResponse.json({ error: "請填寫名稱" }, { status: 400 });
}
const serialized = context?.trim() || "";
if (!validateProfileContext(serialized)) {
return NextResponse.json({ error: "請填寫品牌、產品或特色" }, { status: 400 });
}
const accountId = await getActiveAccountId();
if (!accountId) {
return NextResponse.json({ error: "請先建立並選定經營帳號" }, { status: 400 });
}
const profile = await prisma.productProfile.create({
data: {
accountId,
label: label.trim(),
context: serialized,
},
});
return NextResponse.json({ profile });
} catch (error) {
return apiRouteErrorResponse(error, "product-profiles/create");
}
}
export async function PATCH(request: Request) {
try {
const { id, label, context } = (await request.json()) as {
id?: string;
label?: string;
context?: string;
};
if (!id) {
return NextResponse.json({ error: "缺少 id" }, { status: 400 });
}
const { accountIds } = await requireUserAccountScope(await getActiveAccountId());
const existing = await prisma.productProfile.findUnique({ where: { id } });
if (!existing || !isAccountInUserScope(accountIds, existing.accountId)) {
return NextResponse.json({ error: "找不到品牌與產品" }, { status: 404 });
}
const nextContext = context !== undefined ? context.trim() : existing.context;
if (context !== undefined && !validateProfileContext(nextContext)) {
return NextResponse.json({ error: "請填寫品牌、產品或特色" }, { status: 400 });
}
const profile = await prisma.productProfile.update({
where: { id },
data: {
...(label !== undefined && { label: label.trim() }),
...(context !== undefined && { context: nextContext }),
},
});
if (context !== undefined) {
await syncProfileContextToTopics(id, nextContext);
}
return NextResponse.json({ profile });
} catch (error) {
return apiRouteErrorResponse(error, "product-profiles/update");
}
}
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({ error: "缺少 id" }, { status: 400 });
}
const { accountIds } = await requireUserAccountScope(await getActiveAccountId());
const existing = await prisma.productProfile.findUnique({ where: { id } });
if (!existing || !isAccountInUserScope(accountIds, existing.accountId)) {
return NextResponse.json({ error: "找不到品牌與產品" }, { status: 404 });
}
const linked = await prisma.topic.count({ where: { productProfileId: id } });
if (linked > 0) {
return NextResponse.json(
{ error: `仍有 ${linked} 個主題使用此品牌,請先更換或刪除主題` },
{ status: 400 }
);
}
await prisma.productProfile.delete({ where: { id } });
return NextResponse.json({ success: true });
} catch (error) {
return apiRouteErrorResponse(error, "product-profiles/delete");
}
}