Chat: Bilder, Kamerafoto, Dateien und Sprachnachrichten

- Kundenbereich und Admin-Chat: Bueroklammer-Menue (Bild, Kamera, Datei),
  Mikrofon fuer Sprachnachrichten, Vorschau vor dem Senden mit
  Upload-Fortschritt, Einfuegen (Strg+V) und Hineinziehen von Dateien
- Kamera: am Handy/Tablet die Kamera-App, am Rechner die Kamera im Browser
  mit Vorschau, Kamerawechsel und "Nochmal"
- Sprachnachricht: Aufnahme mit Zeit und Pegel, vorher anhoerbar oder
  direkt senden, hoechstens 5 Minuten
- Verlauf: Bilder (einzeln bzw. Raster, Grossansicht), Player fuer
  Sprachnachrichten, Datei-Kacheln mit Download
- Server: jede Datei einzeln roh hochgeladen (max. 25 MB, 10 je Nachricht,
  max. 3 gleichzeitig), Typ aus dem Inhalt, nur erlaubte Endungen, keine
  Programme; Ablage unter upload-data/chat/<Kunde>/ ausserhalb von public/,
  Auslieferung nur mit Login; Bilder, Audio und PDF inline, alles andere
  als Download (nosniff, CSP sandbox)
- Nicht gesendete Anhaenge werden nach 24 Stunden geloescht
- Benachrichtigungs-Mail nennt nur Art und Namen der Anhaenge
- Appwrite: neues Attribut portalMessages.attachments
  (scripts/setup-chat-collection.mjs, vor dem Deploy ausfuehren)
- Datenschutzerklaerung Abschnitt 6 ergaenzt (Stand 26.09.2026), PDF neu

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-26 14:01:17 +00:00
1 parent 6e18a70a5d
commit 35d98da977
15 files changed
+2086 -107

No files matched your search

+9
View File
@@ -14,6 +14,7 @@ import chatRoutes from './routes/chat.js'
import aboRoutes from './routes/abo.js'
import takeoverCreditRoutes from './routes/takeoverCredits.js'
import { purgeExpiredUploads } from './services/takeoverCredits.js'
import { purgePendingChatUploads } from './services/chatAttachments.js'
import giteaWebhookRoutes from './routes/webhook/gitea.js'
import adminGiteaRoutes from './routes/admin/gitea.js'
import adminWebsiteProjectsRoutes from './routes/admin/websiteProjects.js'
@@ -118,6 +119,14 @@ const server = app.listen(config.port, '0.0.0.0', () => {
.catch((err) => console.warn('[takeover] Aufräumen fehlgeschlagen:', err.message))
setTimeout(purge, 60 * 1000).unref()
setInterval(purge, 24 * 60 * 60 * 1000).unref()
// Chat-Anhaenge, die nie abgeschickt wurden, nach config.chat.pendingTtlHours loeschen (stuendlich)
const purgeChat = () =>
purgePendingChatUploads()
.then((r) => r.purged && console.log(`[chat] ${r.purged} nicht gesendete Anhänge gelöscht`))
.catch((err) => console.warn('[chat] Aufräumen fehlgeschlagen:', err.message))
setTimeout(purgeChat, 90 * 1000).unref()
setInterval(purgeChat, 60 * 60 * 1000).unref()
})
server.on('error', (err) => {