Terminplaner: Status-Route fuer das Team
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4601c5ef3b
commit
39225594e5
4 files changed
+477
-1
No files matched your search
@@ -8,6 +8,7 @@ import { takeoverAdminRouter } from '../takeoverCredits.js'
|
||||
import customersRoutes from './customers.js'
|
||||
import filesRoutes from './files.js'
|
||||
import previewRoutes from './preview.js'
|
||||
import termineTeamRoutes from './termine.js'
|
||||
|
||||
// Team-Eingang fuer das Ticketsystem: alle Mitarbeiter, Appwrite-JWT (Spec 2026-10-01 Admin-Panel ins Ticketsystem)
|
||||
const router = Router()
|
||||
@@ -22,5 +23,7 @@ router.use('/takeover-credits', takeoverAdminRouter)
|
||||
router.use('/offers', offersAdminRouter)
|
||||
router.use('/storage-tasks', storageTasksAdminRouter)
|
||||
router.use('/funktionen', funktionenAdminRouter)
|
||||
// Terminplaner-Betrieb (Status fuer Budget, Warteschlange, Anmeldepause, Tagesjob)
|
||||
router.use('/termine', termineTeamRoutes)
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Router } from 'express'
|
||||
import { statusDaten } from '../../services/termine/betrieb.js'
|
||||
|
||||
/**
|
||||
* Terminplaner fuers Team (Ticketsystem). Eingehaengt in routes/team/index.js unter /api/team/termine, dort hinter requireStaffJwt:
|
||||
* ohne Mitarbeiter-JWT kommt niemand hierher, ein Kunden-Cookie des Portals zaehlt nicht.
|
||||
*/
|
||||
const router = Router()
|
||||
|
||||
/**
|
||||
* Betriebsstand: Mailbudget, Warteschlange, Anmeldepause, letzter Tagesjob, Zahl der aktiven Betriebe (nur Zaehlungen, nie Gast-Daten).
|
||||
* Lesefehler ergeben null-Werte und ein Log (statusDaten), nie einen 500; nie zwischenspeichern.
|
||||
*/
|
||||
router.get('/status', async (_req, res, next) => {
|
||||
try {
|
||||
const daten = await statusDaten(new Date())
|
||||
res.set('Cache-Control', 'no-store')
|
||||
res.json(daten)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user