113 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { adminApproveSubmission } from "../../../../db/domains";
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const adminSecret = getHeader(event, "x-admin-secret");
if (adminSecret !== config.adminSecret) {
throw createError({ statusCode: 401, message: "Unauthorized" });
}
const id = getRouterParam(event, "id");
if (!id) throw createError({ statusCode: 400, message: "ID fehlt" });
const body = await readBody(event).catch(() => ({}));
const result = await adminApproveSubmission(id, body?.note);
// Lyra-Post über die neu genehmigte Domain (fire & forget)
const domain = (result as any)?.domain ?? null;
const submitterUserId = (result as any)?.userId ?? null;
const lyraBotUserId = config.lyraBotUserId;
console.log(
`[approve] domain=${domain}, lyraBotUserId=${lyraBotUserId}, hasGroq=${!!config.groqApiKey}`,
);
if (domain && lyraBotUserId && config.groqApiKey) {
// db + submitterName VOR der IIFE holen (usePrisma braucht Event-Kontext)
const db = usePrisma();
let rawName: string | null = null;
if (submitterUserId) {
try {
// Rebreak Prisma-Modell heißt 'profile' (nicht 'user')
const submitter = await db.profile.findUnique({
where: { id: submitterUserId },
select: { nickname: true, username: true },
});
rawName = submitter?.nickname || submitter?.username || null;
} catch {}
}
// Für @mention: Leerzeichen entfernen (Regex matcht nur einzelne Wörter)
const mentionName = rawName?.replace(/\s+/g, "") ?? null;
const hasMention = !!mentionName;
const mentionRef = hasMention
? `@${mentionName}`
: "einem Community-Mitglied";
// Stats für Lyra-Text holen
let statsLine = "";
try {
const stats = await db.blocklistDomain.count({
where: { isActive: true },
});
const startOfMonth = new Date();
startOfMonth.setDate(1);
startOfMonth.setHours(0, 0, 0, 0);
const monthlyAdded = await db.domainSubmission.count({
where: { status: "approved", reviewedAt: { gte: startOfMonth } },
});
statsLine = `Damit schützen wir gemeinsam vor ${stats.toLocaleString("de-DE")} Domains${monthlyAdded > 0 ? ` (+${monthlyAdded} diesen Monat)` : ""}.`;
} catch {}
const groqUserPrompt = hasMention
? `Schreibe einen kurzen Community-Post (max. 2 Sätze, auf Deutsch): Die Domain "${domain}" wurde zur ReBreak-Blockliste hinzugefügt möglich gemacht durch ${mentionRef}. Erwähne ${mentionRef} genau einmal. Füge am Ende diesen Satz exakt ein: "${statsLine}" Warm, direkt, kein doppelter Dank.`
: `Schreibe einen kurzen Community-Post (max. 2 Sätze, auf Deutsch): Die Domain "${domain}" wurde zur ReBreak-Blockliste hinzugefügt. Füge am Ende diesen Satz exakt ein: "${statsLine}" Warm, direkt.`;
const groqApiKey = config.groqApiKey;
(async () => {
try {
const response = await $fetch<{
choices: { message: { content: string } }[];
}>("https://api.groq.com/openai/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${groqApiKey}`,
"Content-Type": "application/json",
},
body: {
model: "llama-3.3-70b-versatile",
max_tokens: 150,
messages: [
{
role: "system",
content: `Du bist Lyra Recovery-Coach der ReBreak-Community. Tonalität: warm, persönlich, direkt. Schreibe NUR den Post-Text, kein Prefix, keine Anführungszeichen.`,
},
{
role: "user",
content: groqUserPrompt,
},
],
},
});
const content = response.choices?.[0]?.message?.content?.trim();
if (content) {
const faviconUrl = `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=64`;
await db.communityPost.create({
data: {
userId: lyraBotUserId,
category: "domain_approved",
content,
imageUrl: faviconUrl,
isAnonymous: false,
isModerated: false,
},
});
console.log(
`[approve] Lyra-Post erstellt für domain=${domain}, submitter=${mentionName ?? "anonym"}`,
);
}
} catch (err) {
console.error(`[approve] Lyra-Post fehlgeschlagen:`, err);
}
})();
}
return { ok: true };
});