fix: seed script fails due to missing content field
The seed script fetches from /prompts.json without the ?full_content=true parameter. The API only returns contentPreview by default, so the content field is undefined, causing PrismaClientValidationError for every prompt. Changes: - Add ?full_content=true to the fetch URL - Make content optional in RemotePrompt interface - Skip prompts without content instead of failing Before: Created 0 prompts (1595 skipped) with validation errors After: Created 1576 prompts (19 skipped - no content)
This commit is contained in:
+36
-8
@@ -3,14 +3,15 @@ import bcrypt from "bcryptjs";
|
|||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
const PROMPTS_JSON_URL = "https://prompts.chat/prompts.json";
|
const PROMPTS_JSON_URL = "https://prompts.chat/prompts.json?full_content=true";
|
||||||
|
|
||||||
interface RemotePrompt {
|
/** Raw shape returned by the remote API — content may be absent. */
|
||||||
|
interface RemotePromptRaw {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
content: string;
|
content?: string;
|
||||||
type: string;
|
type: string;
|
||||||
structuredFormat: string | null;
|
structuredFormat: string | null;
|
||||||
mediaUrl: string | null;
|
mediaUrl: string | null;
|
||||||
@@ -52,11 +53,20 @@ interface RemotePrompt {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemotePromptsResponse {
|
/** Validated prompt with required content field. */
|
||||||
count: number;
|
type RemotePrompt = RemotePromptRaw & { content: string };
|
||||||
prompts: RemotePrompt[];
|
|
||||||
|
/** Type guard that narrows a raw API prompt to a validated RemotePrompt. */
|
||||||
|
function hasContent(prompt: RemotePromptRaw): prompt is RemotePrompt {
|
||||||
|
return typeof prompt.content === "string" && prompt.content.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RemotePromptsResponse {
|
||||||
|
count: number;
|
||||||
|
prompts: RemotePromptRaw[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fetches all prompts from the remote prompts.chat API with full content. */
|
||||||
async function fetchPrompts(): Promise<RemotePromptsResponse> {
|
async function fetchPrompts(): Promise<RemotePromptsResponse> {
|
||||||
console.log(`📡 Fetching prompts from ${PROMPTS_JSON_URL}...`);
|
console.log(`📡 Fetching prompts from ${PROMPTS_JSON_URL}...`);
|
||||||
const response = await fetch(PROMPTS_JSON_URL);
|
const response = await fetch(PROMPTS_JSON_URL);
|
||||||
@@ -68,12 +78,13 @@ async function fetchPrompts(): Promise<RemotePromptsResponse> {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Seeds the database with users, categories, tags, and prompts from prompts.chat. */
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log("🌱 Seeding database from prompts.chat...");
|
console.log("🌱 Seeding database from prompts.chat...");
|
||||||
|
|
||||||
// Create admin user for assigning prompts
|
// Create admin user for assigning prompts
|
||||||
const password = await bcrypt.hash("password123", 12);
|
const password = await bcrypt.hash("password123", 12);
|
||||||
|
|
||||||
const admin = await prisma.user.upsert({
|
const admin = await prisma.user.upsert({
|
||||||
where: { email: "admin@prompts.chat" },
|
where: { email: "admin@prompts.chat" },
|
||||||
update: {},
|
update: {},
|
||||||
@@ -174,7 +185,7 @@ async function main() {
|
|||||||
userIdMap.set(username, admin.id);
|
userIdMap.set(username, admin.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await prisma.user.upsert({
|
const user = await prisma.user.upsert({
|
||||||
where: { username },
|
where: { username },
|
||||||
update: { name: author.name, avatar: author.avatar },
|
update: { name: author.name, avatar: author.avatar },
|
||||||
@@ -225,6 +236,13 @@ async function main() {
|
|||||||
};
|
};
|
||||||
const structuredFormat = remotePrompt.structuredFormat ? formatMap[remotePrompt.structuredFormat] : null;
|
const structuredFormat = remotePrompt.structuredFormat ? formatMap[remotePrompt.structuredFormat] : null;
|
||||||
|
|
||||||
|
// Skip prompts without content
|
||||||
|
if (!hasContent(remotePrompt)) {
|
||||||
|
console.warn(`⚠ Skipping prompt "${remotePrompt.title}" - no content`);
|
||||||
|
promptsSkipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if prompt already exists
|
// Check if prompt already exists
|
||||||
const existingPrompt = await prisma.prompt.findFirst({
|
const existingPrompt = await prisma.prompt.findFirst({
|
||||||
where: { slug: remotePrompt.slug },
|
where: { slug: remotePrompt.slug },
|
||||||
@@ -280,6 +298,16 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ Created ${promptsCreated} prompts (${promptsSkipped} skipped)`);
|
console.log(`✅ Created ${promptsCreated} prompts (${promptsSkipped} skipped)`);
|
||||||
|
|
||||||
|
// Fail-fast if no prompts were created — likely an upstream API regression
|
||||||
|
const total = promptsCreated + promptsSkipped;
|
||||||
|
if (total > 0 && promptsCreated === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Seeding failed: 0 prompts created out of ${total} (${promptsSkipped} skipped). ` +
|
||||||
|
`This likely indicates an upstream API regression — check that ${PROMPTS_JSON_URL} returns content.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
console.log("\n🎉 Seeding complete!");
|
console.log("\n🎉 Seeding complete!");
|
||||||
console.log("\n📋 Test credentials (password: password123):");
|
console.log("\n📋 Test credentials (password: password123):");
|
||||||
console.log(" Admin: admin@prompts.chat");
|
console.log(" Admin: admin@prompts.chat");
|
||||||
|
|||||||
Reference in New Issue
Block a user