Shop Review A M3: Eingangsbestaetigung G12 je Empfaengeradresse begrenzen (3 je Stunde)
Ueber die gemeinsame Empfaenger-Kappe (darfEmpfaenger) geht an dieselbe Adresse hoechstens dreimal je Stunde ein G12. Darueber hinaus wird der Widerruf trotzdem gespeichert und mit eingang beantwortet, es wird aber keine weitere Mail eingereiht (bestaetigungAt bleibt leer). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c6706f88ff
commit
45487d6c7e
2 files changed
+29
-2
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { config } from '../../config.js'
|
||||
import { createDocument, getDocument, listDocuments, Query, updateDocument } from '../appwriteAdmin.js'
|
||||
import { withLocks } from '../keyedLock.js'
|
||||
import { darfWiderrufsversuch, zaehle } from '../termine/limits.js'
|
||||
import { darfEmpfaenger, darfWiderrufsversuch, zaehle } from '../termine/limits.js'
|
||||
import { pruefeNow } from '../termine/util.js'
|
||||
import { datumKurz, uhrzeit } from '../termine/zeit.js'
|
||||
import { bestellDaten } from './bestellungen.js'
|
||||
@@ -147,8 +147,10 @@ export async function erfasseWiderruf(ctx, eingabe, { now = new Date(), ip } = {
|
||||
if (order) await verlaufErgaenzen(order.$id, now)
|
||||
|
||||
// G12: Eingangsbestaetigung auf dauerhaftem Datentraeger, sofort; bestaetigungAt nur, wenn die Warteschlange sie angenommen hat
|
||||
// Pro Empfaengeradresse hoechstens 3 je Stunde (gemeinsame Kappe der Funktionen): sonst liesse sich G12 als Mail-Relay an fremde
|
||||
// Adressen missbrauchen. Der Widerruf ist gespeichert und beantwortet, die Bestaetigung entfaellt dann (bestaetigungAt bleibt leer).
|
||||
try {
|
||||
const g12 = await anKaeufer('G12', ctx, order, { an: email, bookingId: doc.$id, name, bestellnummer, eingang: now, nachricht }, now)
|
||||
const g12 = !darfEmpfaenger(email.trim().toLowerCase(), now) ? null : await anKaeufer('G12', ctx, order, { an: email, bookingId: doc.$id, name, bestellnummer, eingang: now, nachricht }, now)
|
||||
if (g12?.ok) await updateDocument(col(), doc.$id, { bestaetigungAt: now.toISOString() })
|
||||
} catch (err) {
|
||||
console.warn(`[shop-widerrufe] G12 nicht bestätigt: ${err?.name}`)
|
||||
|
||||
@@ -94,6 +94,11 @@ afterEach(async () => {
|
||||
|
||||
const widerrufe = () => [...aw.docs('shopWiderrufe').values()]
|
||||
const outbox = () => [...aw.docs('termineOutbox').values()]
|
||||
/** Zahl der G12 in der Warteschlange zu Widerrufen mit dieser E-Mail-Adresse (ohne passende Bestellung gilt die Widerruf-ID als bookingId) */
|
||||
const g12An = (email) => {
|
||||
const ids = new Set(widerrufe().filter((d) => d.email === email).map((d) => d.$id))
|
||||
return outbox().filter((e) => e.art === 'G12' && ids.has(e.bookingId)).length
|
||||
}
|
||||
const fehlerVon = async (promise) => {
|
||||
try {
|
||||
await promise
|
||||
@@ -257,6 +262,26 @@ test('Grenzen: 10 je IP und Stunde ⇒ 429, danach wieder frei; 20 je Shop und S
|
||||
await erfasseWiderruf(ctxMit('active', 'f_shop02'), EINGABE, { now: t, ip: '198.51.100.99' })
|
||||
})
|
||||
|
||||
test('M3: G12 geht je Empfängeradresse höchstens 3 Mal pro Stunde raus; der Widerruf wird trotzdem gespeichert und mit eingang beantwortet', async () => {
|
||||
const ziel = { ...EINGABE, email: 'opfer@example.com' }
|
||||
const antworten = []
|
||||
for (let i = 0; i < 5; i += 1) antworten.push(await erfasseWiderruf(ctxMit(), ziel, { now: nach(i * 1000), ip: `203.0.113.${100 + i}` }))
|
||||
assert.ok(antworten.every((a) => a.ok === true && typeof a.eingang === 'string' && a.eingang !== ''))
|
||||
assert.equal(widerrufe().filter((d) => d.email === 'opfer@example.com').length, 5, 'alle Widerrufe gespeichert')
|
||||
await warteAufVersand()
|
||||
assert.equal(g12An('opfer@example.com'), 3)
|
||||
const mitBestaetigung = widerrufe().filter((d) => d.email === 'opfer@example.com' && d.bestaetigungAt).length
|
||||
assert.equal(mitBestaetigung, 3)
|
||||
// eine andere Adresse ist nicht betroffen
|
||||
await erfasseWiderruf(ctxMit(), { ...EINGABE, email: 'andere@example.com' }, { now: nach(6000), ip: '203.0.113.120' })
|
||||
await warteAufVersand()
|
||||
assert.equal(g12An('andere@example.com'), 1)
|
||||
// nach einer Stunde geht wieder eine Bestaetigung raus
|
||||
await erfasseWiderruf(ctxMit(), ziel, { now: nach(61 * 60 * 1000), ip: '203.0.113.130' })
|
||||
await warteAufVersand()
|
||||
assert.equal(g12An('opfer@example.com'), 4)
|
||||
})
|
||||
|
||||
test('ein ungültiger Versuch zählt gegen die Grenze (kein Raten ohne Limit)', async () => {
|
||||
const ip = '203.0.113.9'
|
||||
for (let i = 0; i < 10; i += 1) await fehlerVon(erfasseWiderruf(ctxMit(), { ...EINGABE, email: 'x' }, { now: JETZT, ip }))
|
||||
|
||||
Reference in new issue
Block a user