haixunMaster/lib/db.ts

89 lines
3.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import "server-only";
import { Prisma, PrismaClient } from "@prisma/client";
import { buildStyle8DPromptBlock } from "@/lib/types/style-profile";
const globalForPrisma = globalThis as unknown as {
prisma?: PrismaClient & { __schemaFingerprint?: string };
};
/** 與 schema 同步dmmf 不可用時用固定字串讓 dev 快取仍能失效。 */
const SCHEMA_FINGERPRINT_FALLBACK =
"ProductProfile:accountId,brand,brandId,context,createdAt,id,label,matchTags,topics,updatedAt|Topic:accountId,active,brief,brandProfileId,createdAt,id,label,productContext,productProfileId,query,researchMap,selectedTags,topicGoal";
let schemaFingerprint: string | null = null;
function getSchemaFingerprint(): string {
if (schemaFingerprint) return schemaFingerprint;
try {
const models = Prisma.dmmf?.datamodel?.models;
if (models) {
const account = models.find((m) => m.name === "Account");
const product = models.find((m) => m.name === "ProductProfile");
const topic = models.find((m) => m.name === "Topic");
const productFields = product?.fields.map((f) => f.name).sort().join(",") ?? "";
const topicFields = topic?.fields.map((f) => f.name).sort().join(",") ?? "";
const accountFields = account?.fields.map((f) => f.name).sort().join(",") ?? "";
schemaFingerprint = `Account:${accountFields}|ProductProfile:${productFields}|Topic:${topicFields}`;
return schemaFingerprint;
}
} catch {
// Prisma.dmmf 僅在 server runtime 可用
}
schemaFingerprint = SCHEMA_FINGERPRINT_FALLBACK;
return schemaFingerprint;
}
function createPrismaClient() {
const fingerprint = getSchemaFingerprint();
const client = new PrismaClient({
log: process.env.NODE_ENV === "development" ? ["error", "warn"] : ["error"],
}) as PrismaClient & { __schemaFingerprint?: string };
client.__schemaFingerprint = fingerprint;
return client;
}
function isPrismaClientCurrent(
client: PrismaClient & { __schemaFingerprint?: string }
): boolean {
return (
"backgroundJob" in client &&
"user" in client &&
"productProfile" in client &&
"brandProfile" in client &&
client.__schemaFingerprint === getSchemaFingerprint()
);
}
function getPrismaClient() {
const cached = globalForPrisma.prisma;
if (cached && isPrismaClientCurrent(cached)) return cached;
const client = createPrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = client;
}
return client;
}
export const prisma = getPrismaClient();
export function resolvePersona(
settings: { persona: string | null },
account?: {
persona: string | null;
styleProfile?: string | null;
targetAudience?: string | null;
goals?: string | null;
} | null
) {
const blocks = [
account?.persona?.trim() || settings.persona?.trim(),
buildStyle8DPromptBlock(account?.styleProfile),
account?.targetAudience?.trim() ? `目標受眾:${account.targetAudience.trim()}` : null,
account?.goals?.trim() ? `經營目標:${account.goals.trim()}` : null,
].filter(Boolean);
return blocks.length > 0 ? blocks.join("\n\n") : null;
}