Initial commit: Visigine (Vite client + Express/SQLite backend)

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>
This commit is contained in:
2026-06-12 10:06:48 +02:00
commit e344f1b7e7
88 changed files with 11764 additions and 0 deletions

42
server/lib/summary.js Normal file
View File

@@ -0,0 +1,42 @@
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)
}
}