Terminplaner: Status-Route fuer das Team

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 11:24:02 +00:00
1 parent 4601c5ef3b
commit 39225594e5
4 files changed
+477 -1

No files matched your search

+49 -1
View File
@@ -1,7 +1,7 @@
import { config } from '../../config.js'
import { Query, countDocuments } from '../appwriteAdmin.js'
import { sendAdminNotice } from '../mailer.js'
import { mailBudget, pausiertBis, warteschlangeAlter } from './outbox.js'
import { mailBudget, pausiertBis, verworfeneG01, warteschlangeAlter } from './outbox.js'
import { pruefeNow, pruefeText } from './util.js'
import { datumKurz, uhrzeit } from './zeit.js'
@@ -31,6 +31,9 @@ import { datumKurz, uhrzeit } from './zeit.js'
*
* Lesefehler (Appwrite) ergeben keinen Vorfall und keine Mail, nur ein Log (ebenfalls hoechstens stuendlich je Pruefung).
* `now` ist immer ein Parameter (feste Uhr in Tests).
*
* Statusroute (statusDaten, GET /api/team/termine/status): derselbe Stand auf Abruf fuers Team, ohne Vorfall, Mail oder
* Abkuehlzeit; Lesefehler ergeben null-Werte und ein Log, nie einen Fehler.
*/
const LOG = '[termine-betrieb]'
@@ -253,6 +256,51 @@ export async function meldeTagesjob(ergebnis, now) {
return gemeldet
}
// ---------------------------------------------------------------- Statusroute
/**
* Liest einen Wert fuer die Statusroute: ein Lesefehler ergibt `ersatz` und ein Log (hoechstens stuendlich je Wert, eigener
* Merkzettel `status-<wert>`), nie einen Fehler.
*/
async function leseOderErsatz(wert, nowMs, lese, ersatz) {
try {
return await lese()
} catch (err) {
meldeFehler(`status-${wert}`, err, nowMs)
return ersatz
}
}
/**
* Stand des Terminplaner-Betriebs fuer die Team-Route GET /api/team/termine/status: nur Zahlen, Wahrheitswerte, Zeitpunkte und
* Funktions-IDs, nie eine Adresse oder ein Name eines Gastes.
* budget mailBudget(now) plus verworfeneG01 (veraltete G01, die dieser Prozess seit dem Start verworfen hat)
* queue { wartet, aeltesteMin } wie warteschlangeAlter; bei einem Lesefehler beide null
* pause { aktiv, bis }: aktiv, solange das Ende der Anmeldepause nach `now` liegt, dann ist bis dessen Zeitpunkt (ISO), sonst null
* letzterTagesjob null (seit dem Start lief noch kein Tagesjob) oder { at (ISO), fehler, gedeckelt, geloescht }
* aktiveBetriebe Zahl der aktiven Termine-Dokumente (zaehleAktiveBetriebe); null bei einem Lesefehler
* Wirft nie (ausser mit TypeError bei ungueltigem `now`, wie alle Termine-Bausteine): was nicht lesbar war, ist null und steht im Log.
* @param {Date} now
* @returns {Promise<{ budget: object, queue: { wartet: number|null, aeltesteMin: number|null }, pause: { aktiv: boolean, bis: string|null },
* letzterTagesjob: { at: string, fehler: number, gedeckelt: number, geloescht: number }|null, aktiveBetriebe: number|null }>}
*/
export async function statusDaten(now) {
const nowMs = pruefeNow(now, 'statusDaten')
const [queue, aktiveBetriebe] = await Promise.all([
leseOderErsatz('queue', nowMs, () => warteschlangeAlter(now), { wartet: null, aeltesteMin: null }),
leseOderErsatz('betriebe', nowMs, zaehleAktiveBetriebe, null),
])
const bis = pausiertBis()
const pauseAktiv = bis !== null && bis.getTime() > nowMs
return {
budget: { ...mailBudget(now), verworfeneG01: verworfeneG01() },
queue: { wartet: queue.wartet, aeltesteMin: queue.aeltesteMin },
pause: { aktiv: pauseAktiv, bis: pauseAktiv ? bis.toISOString() : null },
letzterTagesjob: letzterTagesjob(),
aktiveBetriebe,
}
}
/** Nur fuer Tests: den gemerkten Zustand (Zeit der letzten Warnung und Mail je Schluessel, letzter Tagesjob, Deckel-Folge) zuruecksetzen. */
export function resetBetriebForTests() {
gemerkt.clear()