GET/PUT /api/zeiten/:projectId und GET .../vorschau für den Editor im Portal. Zugang nach Funktionszustand (ordered/setup/active lesen und speichern, disabled mit Daten nur lesen), Version 0 vor dem ersten Speichern, 409 ZEITEN_VERALTET, 60 Speichervorgänge je Kunde und Stunde. Die Kundenansicht darf speichern (Protokoll zeiten_gespeichert), den WhatsApp-Haken setzt nur der Kunde selbst: bestaetigtAt kommt vom Server (R12), in der Kundenansicht 403. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
2.3 KiB
JavaScript
67 lines
2.3 KiB
JavaScript
import { config } from '../config.js'
|
|
import { createDocument, deleteDocument, listDocuments, Query } from './appwriteAdmin.js'
|
|
|
|
/**
|
|
* Protokoll der Kundenansicht (Datenschutzerklaerung Abschnitt 6): wer wann das Portal eines Kunden geoeffnet
|
|
* oder darin etwas geaendert hat. Schreiben nur ueber den Server-Schluessel; Eintraege leben 12 Monate.
|
|
*/
|
|
export const VIEW_ACTIONS = [
|
|
'geoeffnet',
|
|
'beendet',
|
|
'abgelaufen',
|
|
'news_angelegt',
|
|
'news_bild',
|
|
'news_geaendert',
|
|
'news_geloescht',
|
|
'termin_einstellungen',
|
|
'termin_aktion',
|
|
'termin_manuell',
|
|
'zeiten_gespeichert',
|
|
]
|
|
|
|
const DETAIL_MAX = 300
|
|
const RETENTION_MS = 365 * 24 * 60 * 60 * 1000
|
|
const PAGE = 100
|
|
|
|
const COLLECTION = () => config.collections.customerViewLog
|
|
|
|
/** Eintrag schreiben; wirft nie (die protokollierte Aktion selbst bleibt gueltig). */
|
|
export async function logViewAction({ customerId, staffUserId, staffName, action, detail = '' }) {
|
|
try {
|
|
await createDocument(COLLECTION(), {
|
|
customerId: String(customerId || ''),
|
|
staffUserId: String(staffUserId || ''),
|
|
staffName: String(staffName || ''),
|
|
action: String(action || ''),
|
|
detail: String(detail || '').slice(0, DETAIL_MAX),
|
|
createdAt: new Date().toISOString(),
|
|
})
|
|
} catch (err) {
|
|
console.warn(`[kundenansicht] Protokoll (${action}, Kunde ${customerId}) nicht geschrieben:`, err.message)
|
|
}
|
|
}
|
|
|
|
/** Eintraege eines Kunden, neueste zuerst. */
|
|
export async function listViewLog(customerId, { limit = 200 } = {}) {
|
|
const docs = await listDocuments(COLLECTION(), [
|
|
Query.equal('customerId', customerId),
|
|
Query.orderDesc('createdAt'),
|
|
Query.limit(limit),
|
|
])
|
|
return docs.map((d) => ({ id: d.$id, at: d.createdAt, staffName: d.staffName || '', action: d.action || '', detail: d.detail || '' }))
|
|
}
|
|
|
|
/** Eintraege aelter als 12 Monate loeschen (taeglicher Lauf), seitenweise bis keine mehr uebrig sind. */
|
|
export async function purgeViewLog({ now = new Date() } = {}) {
|
|
const cutoff = new Date(now.getTime() - RETENTION_MS).toISOString()
|
|
let deleted = 0
|
|
for (;;) {
|
|
const docs = await listDocuments(COLLECTION(), [Query.lessThan('createdAt', cutoff), Query.limit(PAGE)])
|
|
for (const doc of docs) {
|
|
await deleteDocument(COLLECTION(), doc.$id)
|
|
deleted += 1
|
|
}
|
|
if (docs.length < PAGE) return { deleted }
|
|
}
|
|
}
|