import { PayloadRequest } from "payload"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as os from "node:os"; async function getOrchestrator() { const OPENROUTER_KEY = process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; const REPLICATE_KEY = process.env.REPLICATE_API_KEY; if (!OPENROUTER_KEY) { throw new Error( "Missing OPENROUTER_API_KEY in .env (Required for AI generation)", ); } const importDynamic = new Function("modulePath", "return import(modulePath)"); const { AiBlogPostOrchestrator } = await importDynamic( "@mintel/content-engine", ); return new AiBlogPostOrchestrator({ apiKey: OPENROUTER_KEY, replicateApiKey: REPLICATE_KEY, model: "google/gemini-3-flash-preview", }); } export const generateSlugEndpoint = async (req: PayloadRequest) => { try { let body: any = {}; try { if (req.body) body = (await req.json?.()) || {}; } catch { /* ignore */ } const { title, draftContent, oldSlug, instructions } = body; const orchestrator = await getOrchestrator(); const newSlug = await orchestrator.generateSlug( draftContent, title, instructions, ); if (oldSlug && oldSlug !== newSlug) { await req.payload.create({ collection: "redirects" as any, data: { from: oldSlug, to: newSlug, }, }); } return Response.json({ success: true, slug: newSlug }); } catch (e: any) { return Response.json({ success: false, error: e.message }, { status: 500 }); } }; export const generateThumbnailEndpoint = async (req: PayloadRequest) => { try { let body: any = {}; try { if (req.body) body = (await req.json?.()) || {}; } catch { /* ignore */ } const { draftContent, title, instructions } = body; const OPENROUTER_KEY = process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; const REPLICATE_KEY = process.env.REPLICATE_API_KEY; if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY in .env"); if (!REPLICATE_KEY) throw new Error("Missing REPLICATE_API_KEY in .env"); const importDynamic = new Function( "modulePath", "return import(modulePath)", ); const { AiBlogPostOrchestrator } = await importDynamic( "@mintel/content-engine", ); const { ThumbnailGenerator } = await importDynamic( "@mintel/thumbnail-generator", ); const orchestrator = new AiBlogPostOrchestrator({ apiKey: OPENROUTER_KEY, replicateApiKey: REPLICATE_KEY, model: "google/gemini-3-flash-preview", }); const tg = new ThumbnailGenerator({ replicateApiKey: REPLICATE_KEY }); const prompt = await orchestrator.generateVisualPrompt( draftContent || title || "Technology", instructions, ); const tmpPath = path.join(os.tmpdir(), `mintel-thumb-${Date.now()}.png`); await tg.generateImage(prompt, tmpPath); const fileData = await fs.readFile(tmpPath); const stat = await fs.stat(tmpPath); const fileName = path.basename(tmpPath); const newMedia = await req.payload.create({ collection: "media" as any, data: { alt: title ? `Thumbnail for ${title}` : "AI Generated Thumbnail", }, file: { data: fileData, name: fileName, mimetype: "image/png", size: stat.size, }, }); await fs.unlink(tmpPath).catch(() => {}); return Response.json({ success: true, mediaId: newMedia.id }); } catch (e: any) { return Response.json({ success: false, error: e.message }, { status: 500 }); } }; export const generateSingleFieldEndpoint = async (req: PayloadRequest) => { try { let body: any = {}; try { if (req.body) body = (await req.json?.()) || {}; } catch { /* ignore */ } const { documentTitle, documentContent, fieldName, fieldDescription, instructions, } = body; const OPENROUTER_KEY = process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY"); const contextDocsData = await req.payload.find({ collection: "context-files" as any, limit: 100, }); const projectContext = contextDocsData.docs .map((doc: any) => `--- ${doc.filename} ---\n${doc.content}`) .join("\n\n"); const prompt = `You are an expert AI assistant perfectly trained for generating exact data values for CMS components. PROJECT STRATEGY & CONTEXT: ${projectContext} DOCUMENT TITLE: ${documentTitle} DOCUMENT DRAFT:\n${documentContent}\n YOUR TASK: Generate the exact value for a specific field named "${fieldName}". ${fieldDescription ? `FIELD DESCRIPTION / CONSTRAINTS: ${fieldDescription}\n` : ""} ${instructions ? `EDITOR INSTRUCTIONS for this field: ${instructions}\n` : ""} CRITICAL RULES: 1. Respond ONLY with the requested content value. 2. NO markdown wrapping blocks (like \`\`\`mermaid or \`\`\`html) around the output! Just the raw code or text. 3. If the field implies a diagram or flow, output RAW Mermaid.js code. 4. If it's standard text, write professional B2B German. No quotes, no conversational filler.`; const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${OPENROUTER_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "google/gemini-3-flash-preview", messages: [{ role: "user", content: prompt }], }), }); const data = await res.json(); const text = data.choices?.[0]?.message?.content?.trim() || ""; return Response.json({ success: true, text }); } catch (e: any) { return Response.json({ success: false, error: e.message }, { status: 500 }); } };