"use server"; import { config } from "../../../content-engine.config"; import { getPayloadHMR } from "@payloadcms/next/utilities"; import configPromise from "@payload-config"; 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 async function generateSlugAction( title: string, draftContent: string, oldSlug?: string, instructions?: string, ) { try { const orchestrator = await getOrchestrator(); const newSlug = await orchestrator.generateSlug( draftContent, title, instructions, ); if (oldSlug && oldSlug !== newSlug) { const payload = await getPayloadHMR({ config: configPromise }); await payload.create({ collection: "redirects", data: { from: oldSlug, to: newSlug, }, }); } return { success: true, slug: newSlug }; } catch (e: any) { return { success: false, error: e.message }; } } export async function generateThumbnailAction( draftContent: string, title?: string, instructions?: string, ) { try { const payload = await getPayloadHMR({ config: configPromise }); 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 (Required for Thumbnails)", ); } 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 payload.create({ collection: "media", data: { alt: title ? `Thumbnail for ${title}` : "AI Generated Thumbnail", }, file: { data: fileData, name: fileName, mimetype: "image/png", size: stat.size, }, }); // Cleanup temp file await fs.unlink(tmpPath).catch(() => {}); return { success: true, mediaId: newMedia.id }; } catch (e: any) { return { success: false, error: e.message }; } } export async function generateSingleFieldAction( documentTitle: string, documentContent: string, fieldName: string, fieldDescription: string, instructions?: string, ) { try { const OPENROUTER_KEY = process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY"); const payload = await getPayloadHMR({ config: configPromise }); // Fetch context documents from DB const contextDocsData = await payload.find({ collection: "context-files", limit: 100, }); const projectContext = contextDocsData.docs .map((doc) => `--- ${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 { success: true, text }; } catch (e: any) { return { success: false, error: e.message }; } }