Vollständiger 1:1-Port des cloudflare-worker/ (dogfather-universe-postfach) auf server-internal/: Bewerbungs-Postfach mit Rollen/Rechten, Team- Zugangsverwaltung (verschlüsselte Owner-only-Codes), DogiCrew-Supporter-Abo (PayPal Subscriptions, Google-Login, Resend-E-Mail, 30-Monats-Prämienzyklus) und automatische TikTok-Live-Erkennung. D1 -> better-sqlite3, Cloudflare- Cron -> node-cron. Läuft ohne gesetzte Secrets weiter (PayPal/Google/Resend/ Ticketanizer/Discord bleiben "nicht konfiguriert" statt kaputt), bis die echten Zugangsdaten eingetragen werden.
61 lines
2.8 KiB
JavaScript
61 lines
2.8 KiB
JavaScript
/* =====================================================================
|
|
lib/ticketanizer.js — Bewerbungen als Ticket bei Ticketanizer eröffnen.
|
|
1:1 portiert aus cloudflare-worker/src/lib/ticketanizer.js.
|
|
===================================================================== */
|
|
|
|
const TICKETANIZER_BASIS_URL = "https://api.ticketanizer.com";
|
|
|
|
const PANEL_IDS = { modi: 4, kooperation: 5, scout: 6, creator: 7 };
|
|
|
|
const FIELD_LABELS = {
|
|
name: "Name", email: "E-Mail", kontakt: "Kontakt (Discord/TikTok)", erfahrung: "Erfahrung",
|
|
verfuegbarkeit: "Verfügbarkeit", warum: "Warum möchtest du dabei sein?", platform: "Plattform",
|
|
content: "Content-Art", firma: "Firma / Projekt", idee: "Idee / Anliegen",
|
|
};
|
|
|
|
function ersteNachrichtAufbauen(type, data) {
|
|
const zeilen = Object.entries(data || {})
|
|
.filter(([, v]) => String(v ?? "").trim() !== "")
|
|
.map(([key, value]) => `**${FIELD_LABELS[key] || key}:** ${String(value).trim()}`);
|
|
if (!zeilen.length) zeilen.push("_Keine weiteren Angaben übermittelt._");
|
|
const text = zeilen.join("\n");
|
|
return text.length > 3500 ? text.slice(0, 3500) + "…" : text;
|
|
}
|
|
|
|
const warten = (ms) => new Promise((fertig) => setTimeout(fertig, ms));
|
|
const MAX_VERSUCHE = 3;
|
|
const STANDARD_WARTEZEITEN_MS = [2000, 5000];
|
|
|
|
async function ticketAnfrageSenden(apiKey, panelId, visitorName, firstMessage) {
|
|
for (let versuch = 1; versuch <= MAX_VERSUCHE; versuch++) {
|
|
const antwort = await fetch(`${TICKETANIZER_BASIS_URL}/v1/tickets`, {
|
|
method: "POST",
|
|
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
body: JSON.stringify({ panel_id: panelId, visitor_name: visitorName, first_message: firstMessage }),
|
|
});
|
|
if (antwort.status !== 429 || versuch === MAX_VERSUCHE) return antwort;
|
|
const retryAfterHeader = Number(antwort.headers.get("Retry-After"));
|
|
const wartezeit = Number.isFinite(retryAfterHeader) && retryAfterHeader > 0 ? retryAfterHeader * 1000 : STANDARD_WARTEZEITEN_MS[versuch - 1] || 5000;
|
|
await warten(wartezeit);
|
|
}
|
|
}
|
|
|
|
export async function bewerbungAlsTicketAnlegen(type, data) {
|
|
const apiKey = process.env.TICKETANIZER_API_KEY;
|
|
const panelId = PANEL_IDS[type];
|
|
if (!apiKey || !panelId) return;
|
|
|
|
const visitorName = String(data?.name || data?.firma || "Unbekannt").trim().slice(0, 80) || "Unbekannt";
|
|
const firstMessage = ersteNachrichtAufbauen(type, data);
|
|
|
|
try {
|
|
const antwort = await ticketAnfrageSenden(apiKey, panelId, visitorName, firstMessage);
|
|
if (!antwort.ok) {
|
|
const antwortText = await antwort.text().catch(() => "");
|
|
console.error(`[ticketanizer] Fehler beim Ticket-Anlegen (${type}): HTTP ${antwort.status} — ${antwortText}`);
|
|
}
|
|
} catch (fehler) {
|
|
console.error(`[ticketanizer] Ausnahme beim Ticket-Anlegen (${type}):`, fehler && fehler.message);
|
|
}
|
|
}
|