Commit graph

10 commits

Author SHA1 Message Date
Kundencenter
4976fc529e fix(discord): /ticket auch für Personal nutzbar, klarere Fehlermeldung
Bug gefunden: /ticket prüfte ausschließlich ein verknüpftes
Kundenkonto. Ein Discord-Konto kann aber immer nur mit einem
Kundencenter-Konto verknüpft sein – wer sein Konto (wie in der
Anleitung empfohlen) als Personal verknüpft hat, bekam die
irreführende Meldung "Bitte zuerst dein Konto verknüpfen", obwohl das
Konto sehr wohl verknüpft war (nur eben nicht als Kunde).

Jetzt: /ticket erkennt automatisch, ob das aufrufende Discord-Konto
mit einem Kunden- oder Personal-Konto verknüpft ist.
- Kunde: legt wie bisher ein eigenes Ticket samt Thread an.
- Personal: neue Option "kunde" (Kundennummer oder Name) legt ein
  Ticket für den jeweiligen Kunden an (Status "wartet auf Kunde", wie
  beim Anlegen über die Web-Oberfläche). Kein automatischer Thread,
  da der Kunde dafür selbst Discord verknüpft haben müsste.
- Fehlermeldung bei fehlender Verknüpfung präzisiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 00:17:55 +02:00
Kundencenter
078b1357fb refactor(discord): einheitliche Berechtigung über verknüpfte Konten
Nutzerfrage: warum gibt es neben der Kontoverknüpfung noch eine
separate "Erlaubte Nutzer-IDs"-Liste, und woher weiß der Bot, ob
jemand Kunde oder Personal ist? Antwort: das waren zwei parallele,
verwirrende Mechanismen. /kc-status und /kc-kunde prüften bisher eine
manuell gepflegte Discord-ID-Liste (aus der Zeit vor der OAuth-
Verknüpfung), während /zuweisen und /schliessen bereits das verknüpfte
Personal-Konto (users.discord_user_id, kind='staff') prüften.

Jetzt einheitlich: alle vier Personal-Befehle verlangen ein verknüpftes
Personal-Konto, genau wie /zuweisen und /schliessen. Die Liste entfällt
vollständig (Migration 029, Spalte staff_user_ids entfernt) – wer ein
Personal-Konto im Kundencenter hat und es unter "Mein Konto" mit
Discord verknüpft, darf automatisch alle vier Befehle nutzen, ohne
zusätzliche manuelle Pflege einer ID-Liste.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 00:12:10 +02:00
Kundencenter
8243cdb111 feat(discord): Ticket-Befehle + saubere Rechtetrennung Kunde/Personal
Drei neue Slash-Befehle:
- /ticket (jeder verknüpfte Kunde): erstellt direkt ein neues Ticket
  samt zugehörigem privaten Thread, ganz ohne Web-Besuch.
- /zuweisen und /schliessen (nur Personal, nur innerhalb eines
  Ticket-Threads): weisen das Ticket des aktuellen Threads einer
  per Discord verknüpften Personal-Person zu bzw. schließen es.

Rechtetrennung (Kunden: nur eigenes Ticket erstellen/lesen, Personal:
mehr Rechte):
- Zuweisen/Schließen prüfen serverseitig ein verknüpftes
  Personal-Konto (kind='staff'), unabhängig von der separaten
  staffUserIds-Liste für /kc-status und /kc-kunde.
- Alle vier Personal-Befehle sind zusätzlich per
  setDefaultMemberPermissions(ManageThreads) so eingestellt, dass sie
  Kunden gar nicht erst in der Befehlsliste angezeigt werden (die
  serverseitige Prüfung bleibt trotzdem bestehen, das ist nur die
  UI-Sichtbarkeit).
- Bugfix dabei gefunden: eine Kundenantwort im Thread eines bereits
  geschlossenen Tickets hat es bisher unbemerkt wieder geöffnet (die
  Web-Oberfläche verhindert das schon lange). Jetzt wird das erkannt
  und die Antwort mit einem Hinweis abgelehnt, wie im Web.

Anleitung unter Einstellungen > Discord entsprechend ergänzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 00:05:38 +02:00
Kundencenter
1cd1a8b03a feat(discord): Ticket-Threads mit bidirektionaler Synchronisierung
Zweiter Teil des Discord-Ausbaus. Hat ein Kunde sein Konto verknüpft
(siehe vorheriger Commit), bekommt sein Ticket automatisch einen
privaten Thread im konfigurierten Ticket-Kanal:

- Neuer Job discord.ticket_sync: legt den Thread beim ersten Bedarf
  an (Name "ticket-<Nummer>", Kunde wird als Mitglied hinzugefügt)
  und postet dort jede neue, nicht-interne Nachricht (sowohl Kunden-
  als auch Personal-Antworten). Wird von /tickets (Erstellung durch
  Kunden) und /tickets/:id/messages (Antworten, keine internen
  Notizen) ausgelöst.
- Umgekehrte Richtung: Antwortet der Kunde direkt im Thread, übernimmt
  ein neuer messageCreate-Listener im Bot die Nachricht als
  Ticket-Antwort – nur wenn der Absender über discord_user_id
  eindeutig als Mitglied der Organisation des Tickets verifiziert ist.
  Personal-Nachrichten im Thread werden bewusst nicht übernommen (Web-
  Oberfläche bleibt die Quelle für Personal-Antworten, kein Echo).
- Dafür nötig: privilegierte Message Content Intent (war in der
  Anleitung aus dem vorigen Schritt schon vermerkt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 23:44:53 +02:00
Kundencenter
c8da9362a7 feat: Discord-Bot über die Oberfläche konfigurierbar
Bisher kam die Discord-Konfiguration (Token, Server-ID, Kanal-ID,
erlaubte Nutzer-IDs) ausschließlich aus Umgebungsvariablen. Jetzt
unter Einstellungen > Discord einstellbar, inkl. Schritt-für-Schritt-
Anleitung zum Einrichten einer Discord-Anwendung/eines Bots (Token
erzeugen, einladen, IDs ermitteln).

- Migration 026: discord_settings (Token verschlüsselt, wie SMTP/IMAP).
- apps/worker/src/discord.ts liest die Einstellungen jetzt aus der DB
  statt aus process.env; syncDiscord() läuft periodisch (60s) und
  verbindet automatisch neu, wenn sich Token/Server/Aktivierung
  geändert haben (Trennung von Verbindungs- und Laufzeitdaten: Kanal-ID
  und erlaubte Nutzer werden bei jeder Nutzung frisch aus der DB
  gelesen, ohne Neuverbindung).
- Neues API-Modul apps/api/src/modules/discord: Einstellungen lesen/
  schreiben, Token- und Testnachricht-Prüfung direkt über die Discord-
  REST-API (unabhängig vom laufenden Bot-Prozess im Worker).
- Rechte discord.read (Admin+) / discord.write (Superadmin), wie beim
  E-Mail-Modul.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 23:28:24 +02:00
Kundencenter
fe18bf0bce feat: IMAP-Posteingang fürs Ticketsystem (Mail-Antworten -> Tickets)
Neues Postfach lässt sich unter Einstellungen > E-Mail > IMAP-Posteingang
einrichten (Host/Port/Verschlüsselung/Zugangsdaten/Ordner, An/Aus,
Verbindungstest). Der Worker ruft es alle 2 Minuten ab (mailbox.ts) und
ordnet eingehende Mails zu:

1. Betreff enthält eine Ticketnummer (T-123) UND Absender gehört
   nachweislich zum Kunden dieses Tickets -> wird als Kundennachricht
   angehängt (Status pending_staff, Discord-Benachrichtigung).
2. Sonst, wenn der Absender ein bekannter aktiver Kunde ist -> neues
   Ticket wird automatisch unter seinem Kundenkonto angelegt.
3. Sonst (unbekannter/nicht verifizierbarer Absender) -> landet im
   neuen Posteingang (/admin/inbox, UI unter /admin/posteingang) zur
   manuellen Zuordnung: Personal wählt einen Kunden (legt Ticket an),
   hängt die Mail an ein bestehendes Ticket, oder ignoriert sie.

Beim ersten Aktivieren wird kein Mailbox-Bestand verarbeitet, nur ab
dann neu Eingehendes (last_uid wird auf den aktuellen Stand gesetzt).

Migration 024: imap_settings (Verbindung, verschlüsseltes Passwort,
last_uid/last_error), inbox_messages (Warteschlange). Rechte:
email.read/write für die IMAP-Verbindung (wie SMTP), tickets.read/write
für den Posteingang (Zuordnung ist eine Ticket-Aktion, daher im
Tickets-Modul statt eines eigenen Moduls, um keine Modulgrenzen zu
verletzen).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 17:35:58 +02:00
Kundencenter
a68b4cf6ec feat: SMTP-Stack mit konfigurierbaren Mailvorlagen
Bisher gab es nur zwei fest im Code verdrahtete E-Mails (Einladung,
Passwort-Reset) und die SMTP-Verbindung kam ausschließlich aus
Umgebungsvariablen. Jetzt:

- SMTP-Verbindung unter Einstellungen > E-Mail konfigurierbar (Host,
  Port, Verschlüsselung, Zugangsdaten verschlüsselt gespeichert,
  Absender), mit Testmail-Versand. Die Mail-Logik wandert dafür nach
  @kc/platform/mail, damit API und Worker sie gemeinsam nutzen.
- Mailvorlagen-Verwaltung: jedes Ereignis hat eine feste "Definition"
  (Name, Auslöser, verfügbare Platzhalter/Daten), Betreff/Text sind
  editierbar, je Vorlage einzeln aktivierbar, mit Testversand anhand
  von Beispieldaten. Unbekannte Platzhalter werden beim Speichern
  abgelehnt.
- Neue Ereignisse verdrahtet: Ticket erstellt (Bestätigung an Kunde),
  Personal antwortet auf Ticket (Benachrichtigung an Kunde), Rechnung
  ausgestellt, sowie die zuvor zurückgestellte Funktion "Panel-
  Zugangsdaten per E-Mail senden" nach einem Passwort-Reset. Diese
  laufen über die Jobqueue (mail.template), analog zu discord.notify.
- Versandprotokoll (letzte 100 Versuche, ohne Inhalte) einsehbar unter
  Einstellungen > E-Mail.
- Migration 022, Rechte email.read (Admin+) / email.write (Superadmin).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 12:53:29 +02:00
Kundencenter
2f8566307b fix(worker): handle invoice.issued event in discord.notify
The invoices module started enqueueing invoice.issued notifications but
the worker's discord.notify handler had no case for it, causing the job
to exhaust retries and land in needs_review (surfaced on the dashboard
as "Aufträge mit Fehlern / Prüfbedarf").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 11:34:59 +02:00
Kundencenter
ca5b48ab47 Support-Tickets: Modul, Oberfläche, Discord-Benachrichtigung 2026-09-27 08:36:18 +02:00
Kundencenter
4763548bfb Stand vor Einführung des Nacht-Agenten 2026-09-27 00:51:32 +02:00