Ticketanizer-Versand: automatische Wiederholung bei Rate-Limit (429)
Fremde Bewerber koennen theoretisch kurz hintereinander absenden (z.B. wenn Dogi live "bewerbt euch jetzt" sagt) und dabei dasselbe Rate-Limit treffen, das beim Testen aufgefallen ist. Jetzt wird ein 429 automatisch bis zu 2x mit kurzer Pause wiederholt (Retry-After-Header wird beachtet, sonst 2s/5s Standard-Wartezeit), bevor endgueltig aufgegeben wird - laeuft komplett im Hintergrund ueber ctx.waitUntil, blockiert nie die Antwort an den Bewerber. Die Bewerbung selbst landet ohnehin immer sicher in der eigenen Datenbank, unabhaengig vom Ticketanizer-Ergebnis. Getestet mit 3 gleichzeitigen Bewerbungen - alle liefen sauber durch.
This commit is contained in:
@@ -26,6 +26,14 @@
|
||||
Rein informativ, kein Ausfallpunkt: Fehlt der API-Key, oder schlägt der
|
||||
Versand fehl (Ticketanizer down, falscher Key, ...), wird die Bewerbung
|
||||
trotzdem ganz normal in der eigenen Datenbank gespeichert.
|
||||
|
||||
Automatische Wiederholung bei Rate-Limit (HTTP 429): Wenn mehrere
|
||||
Menschen kurz hintereinander ein Formular abschicken, kann Ticketanizer
|
||||
kurzzeitig 429 antworten (selbst erlebt beim Testen). In dem Fall wird
|
||||
der Versand automatisch bis zu 2x mit kurzer Pause wiederholt (siehe
|
||||
ticketAnfrageSenden), bevor endgültig aufgegeben wird — läuft komplett
|
||||
im Hintergrund über ctx.waitUntil, blockiert also nie die Antwort an
|
||||
die Bewerberin/den Bewerber.
|
||||
===================================================================== */
|
||||
|
||||
const TICKETANIZER_BASIS_URL = "https://api.ticketanizer.com";
|
||||
@@ -67,6 +75,47 @@ function ersteNachrichtAufbauen(type, data) {
|
||||
return text.length > 3500 ? text.slice(0, 3500) + "…" : text;
|
||||
}
|
||||
|
||||
const warten = (ms) => new Promise((fertig) => setTimeout(fertig, ms));
|
||||
|
||||
// Wenn viele Menschen kurz hintereinander ein Formular abschicken (z.B. weil
|
||||
// Dogi gerade live "bewerbt euch jetzt!" sagt), kann Ticketanizer mit
|
||||
// HTTP 429 ("zu viele Anfragen") antworten. Die Bewerbung selbst ist davon
|
||||
// nie betroffen (steht schon sicher in unserer DB) — hier wird nur der
|
||||
// Ticket-Versand selbst automatisch 1–2x mit kurzer Pause wiederholt,
|
||||
// bevor endgültig aufgegeben wird. Nutzt Retry-After, falls Ticketanizer
|
||||
// das mitschickt, sonst eine feste, ansteigende Wartezeit.
|
||||
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;
|
||||
|
||||
console.log(`[ticketanizer] Rate-Limit (Versuch ${versuch}/${MAX_VERSUCHE}) — warte ${wartezeit}ms und versuche es erneut.`);
|
||||
await warten(wartezeit);
|
||||
}
|
||||
}
|
||||
|
||||
export async function bewerbungAlsTicketAnlegen(env, type, data) {
|
||||
const apiKey = env.TICKETANIZER_API_KEY;
|
||||
const panelId = PANEL_IDS[type];
|
||||
@@ -83,18 +132,7 @@ export async function bewerbungAlsTicketAnlegen(env, type, data) {
|
||||
const firstMessage = ersteNachrichtAufbauen(type, data);
|
||||
|
||||
try {
|
||||
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,
|
||||
}),
|
||||
});
|
||||
const antwort = await ticketAnfrageSenden(apiKey, panelId, visitorName, firstMessage);
|
||||
const antwortText = await antwort.text().catch(() => "");
|
||||
if (!antwort.ok) {
|
||||
console.error(`[ticketanizer] Fehler beim Ticket-Anlegen (${type}, panel ${panelId}): HTTP ${antwort.status} — ${antwortText}`);
|
||||
|
||||
Reference in New Issue
Block a user