Container-ready via docker/ compose (frontend nginx + backend Node). Compose adjusted for Coolify on the prod server: frontend uses expose:80 (no host binding — host 8080 is taken by the Coolify proxy; Traefik routes visigine.de), backend ALLOWED_ORIGINS=https://visigine.de. Secrets stay in server/.env (git-ignored); see server/.env.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
1.6 KiB
JavaScript
43 lines
1.6 KiB
JavaScript
const SUMMARY_PROMPT = `You are a GEO/SEO auditor. The following checks were run on a website. Some FAILED.
|
|
Write a summary in German: 1-2 sentences describing what is missing and why it matters for AI visibility.
|
|
Be specific about which signals are missing. Do not invent checks — only describe the ones listed.
|
|
Return ONLY the summary text, no JSON, no markdown.`
|
|
|
|
function fallback(failedTitles) {
|
|
return `${failedTitles.length} GEO/SEO-Signale fehlen. Behebe die aufgelisteten Probleme für bessere KI-Sichtbarkeit.`
|
|
}
|
|
|
|
export async function generateSummary(failedTitles, targetUrl) {
|
|
const apiKey = process.env.MISTRAL_KEY
|
|
if (!apiKey || failedTitles.length === 0) return fallback(failedTitles)
|
|
|
|
const controller = new AbortController()
|
|
const timer = setTimeout(() => controller.abort(), 10000)
|
|
try {
|
|
const res = await fetch('https://api.mistral.ai/v1/chat/completions', {
|
|
method: 'POST',
|
|
signal: controller.signal,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${apiKey}`,
|
|
},
|
|
body: JSON.stringify({
|
|
model: 'mistral-large-latest',
|
|
max_tokens: 150,
|
|
messages: [
|
|
{ role: 'system', content: SUMMARY_PROMPT },
|
|
{ role: 'user', content: `Website: ${targetUrl}\nFehlende Signale: ${failedTitles.join(', ')}` },
|
|
],
|
|
}),
|
|
})
|
|
if (!res.ok) return fallback(failedTitles)
|
|
const data = await res.json()
|
|
const text = (data?.choices?.[0]?.message?.content || '').trim()
|
|
return text || fallback(failedTitles)
|
|
} catch {
|
|
return fallback(failedTitles)
|
|
} finally {
|
|
clearTimeout(timer)
|
|
}
|
|
}
|