117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { accountHasStoredSession } from "@/lib/threads-browser";
|
|
import { prisma } from "@/lib/db";
|
|
import { setActiveAccountForUser } from "@/lib/account-context";
|
|
import { apiRouteErrorResponse } from "@/lib/auth/api";
|
|
import { requireSessionUser } from "@/lib/auth/session";
|
|
|
|
function accountSelect() {
|
|
return {
|
|
id: true,
|
|
username: true,
|
|
displayName: true,
|
|
persona: true,
|
|
styleProfile: true,
|
|
styleBenchmark: true,
|
|
brief: true,
|
|
productBrief: true,
|
|
targetAudience: true,
|
|
goals: true,
|
|
valid: true,
|
|
storageState: true,
|
|
updatedAt: true,
|
|
threadsUserId: true,
|
|
threadsTokenExpiresAt: true,
|
|
} as const;
|
|
}
|
|
|
|
type RawAccount = Awaited<
|
|
ReturnType<typeof prisma.account.findFirst<{ select: ReturnType<typeof accountSelect> }>>
|
|
>;
|
|
|
|
function toPublicAccount(account: NonNullable<RawAccount>) {
|
|
const { threadsUserId, threadsTokenExpiresAt } = account;
|
|
return {
|
|
id: account.id,
|
|
username: account.username,
|
|
displayName: account.displayName,
|
|
persona: account.persona,
|
|
styleProfile: account.styleProfile,
|
|
styleBenchmark: account.styleBenchmark,
|
|
brief: account.brief,
|
|
productBrief: account.productBrief,
|
|
targetAudience: account.targetAudience,
|
|
goals: account.goals,
|
|
valid: account.valid,
|
|
updatedAt: account.updatedAt,
|
|
apiConnected: !!threadsUserId,
|
|
browserConnected: accountHasStoredSession(account),
|
|
sessionSynced: accountHasStoredSession(account),
|
|
threadsUserId,
|
|
threadsTokenExpiresAt: threadsTokenExpiresAt?.toISOString() ?? null,
|
|
};
|
|
}
|
|
|
|
export async function GET() {
|
|
try {
|
|
const user = await requireSessionUser();
|
|
const accounts = await prisma.account.findMany({
|
|
where: { userId: user.id },
|
|
orderBy: { updatedAt: "desc" },
|
|
select: accountSelect(),
|
|
});
|
|
const activeAccountId = user.activeAccountId ?? accounts[0]?.id ?? null;
|
|
|
|
if (!user.activeAccountId && activeAccountId) {
|
|
await setActiveAccountForUser(user.id, activeAccountId);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
accounts: accounts.map(toPublicAccount),
|
|
activeAccountId,
|
|
});
|
|
} catch (error) {
|
|
return apiRouteErrorResponse(error, "accounts");
|
|
}
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const user = await requireSessionUser();
|
|
const body = (await request.json()) as {
|
|
displayName?: string;
|
|
username?: string;
|
|
persona?: string;
|
|
brief?: string;
|
|
productBrief?: string;
|
|
targetAudience?: string;
|
|
goals?: string;
|
|
activate?: boolean;
|
|
};
|
|
|
|
const account = await prisma.account.create({
|
|
data: {
|
|
userId: user.id,
|
|
displayName: body.displayName?.trim() || body.username?.trim() || "新經營帳號",
|
|
username: body.username?.replace(/^@/, "").trim() || null,
|
|
persona: body.persona?.trim() || null,
|
|
brief: body.brief?.trim() || null,
|
|
productBrief: body.productBrief?.trim() || null,
|
|
targetAudience: body.targetAudience?.trim() || null,
|
|
goals: body.goals?.trim() || null,
|
|
storageState: "",
|
|
valid: false,
|
|
},
|
|
select: accountSelect(),
|
|
});
|
|
|
|
if (body.activate ?? true) {
|
|
await setActiveAccountForUser(user.id, account.id);
|
|
}
|
|
|
|
return NextResponse.json({ account: toPublicAccount(account) });
|
|
} catch (error) {
|
|
return apiRouteErrorResponse(error, "accounts/create");
|
|
}
|
|
}
|