Terminplaner: oeffentliche Schnittstelle v1 mit Missbrauchsschutz
Router /api/public/termine/v1 (config, tage, zeiten, buchungen, widerruf, Token-Seiten /termin/*, Kalender-Abo) mit eigenem 16-KB-Parser und Fehlermiddleware, CORS nach Herkunft, Honeypot und Formular-Token, Grenzen je IP (IPv6 je /64), siteKey und Adresse in limits.js, Mindestantwortzeit (TERMINE_MIN_ANTWORT_MS) und 10-Sekunden-Cache fuer tage/zeiten. Vor dem globalen express.json eingehaengt; /api/public/ ist vom Kundenansicht-Waechter ausgenommen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
43ac05aa06
commit
2b0c05b8e9
10 files changed
+1928
-7
No files matched your search
@@ -7,6 +7,7 @@ const TERMINE_ENV = [
|
||||
'TERMINE_ENABLED',
|
||||
'TERMINE_PILOT_EMAILS',
|
||||
'TERMINE_FORM_MIN_SECONDS',
|
||||
'TERMINE_MIN_ANTWORT_MS',
|
||||
'APPWRITE_COLLECTION_TERMINE_CONFIG',
|
||||
'APPWRITE_COLLECTION_TERMINE_BOOKINGS',
|
||||
'APPWRITE_COLLECTION_TERMINE_CLAIMS',
|
||||
@@ -47,6 +48,7 @@ test('Konfiguration: Vorgaben ohne Umgebungsvariablen', async () => {
|
||||
zelleMin: 5,
|
||||
mailsProStunde: 200,
|
||||
formMinSekunden: 3,
|
||||
minAntwortMs: 300,
|
||||
})
|
||||
assert.equal(fresh.collections.termineConfig, 'termineConfig')
|
||||
assert.equal(fresh.collections.termineBookings, 'termineBookings')
|
||||
@@ -59,6 +61,7 @@ test('Konfiguration: Umgebungsvariablen werden gelesen und bereinigt', async ()
|
||||
TERMINE_ENABLED: ' Pilot ',
|
||||
TERMINE_PILOT_EMAILS: ' Kenso.Gri@Gmail.com, ,justin@webklar.com ',
|
||||
TERMINE_FORM_MIN_SECONDS: '0',
|
||||
TERMINE_MIN_ANTWORT_MS: '0',
|
||||
APPWRITE_COLLECTION_TERMINE_CONFIG: 'tConfig',
|
||||
APPWRITE_COLLECTION_TERMINE_BOOKINGS: 'tBookings',
|
||||
APPWRITE_COLLECTION_TERMINE_CLAIMS: 'tClaims',
|
||||
@@ -67,6 +70,7 @@ test('Konfiguration: Umgebungsvariablen werden gelesen und bereinigt', async ()
|
||||
assert.equal(fresh.termine.mode, 'pilot')
|
||||
assert.deepEqual(fresh.termine.pilotEmails, ['kenso.gri@gmail.com', 'justin@webklar.com'])
|
||||
assert.equal(fresh.termine.formMinSekunden, 0)
|
||||
assert.equal(fresh.termine.minAntwortMs, 0)
|
||||
assert.deepEqual(
|
||||
[fresh.collections.termineConfig, fresh.collections.termineBookings, fresh.collections.termineClaims, fresh.collections.termineOutbox],
|
||||
['tConfig', 'tBookings', 'tClaims', 'tOutbox'],
|
||||
@@ -83,6 +87,11 @@ test('Konfiguration: leerer oder unbekannter Schalter ist off, ungueltige Sekund
|
||||
assert.equal(fresh.termine.formMinSekunden, 3, `TERMINE_FORM_MIN_SECONDS=${JSON.stringify(value)}`)
|
||||
}
|
||||
assert.equal((await freshConfig({ TERMINE_FORM_MIN_SECONDS: '7' })).termine.formMinSekunden, 7)
|
||||
for (const value of ['', 'abc', '-1', 'NaN']) {
|
||||
const fresh = await freshConfig({ TERMINE_MIN_ANTWORT_MS: value })
|
||||
assert.equal(fresh.termine.minAntwortMs, 300, `TERMINE_MIN_ANTWORT_MS=${JSON.stringify(value)}`)
|
||||
}
|
||||
assert.equal((await freshConfig({ TERMINE_MIN_ANTWORT_MS: '750' })).termine.minAntwortMs, 750)
|
||||
})
|
||||
|
||||
test('termineModus: leer und unbekannt sind off, pilot und on bleiben', () => {
|
||||
|
||||
Reference in new issue
Block a user