Commit graph

7 commits

Author SHA1 Message Date
Claude
8ef0b3bc05 Discord: Tickets als eigene Kanäle in einer Kategorie, Ticket-Meldungen nur für den Support
Mit gesetzter Ticket-Kategorie bekommt jedes offene Ticket einen eigenen Kanal,
sichtbar nur für die Support-Rollen, den Bot und die per Discord verknüpften
Mitglieder des Kunden. Ein minütlicher Abgleich im Worker legt fehlende Kanäle an
(auch für Tickets aus Mail-Eingang/Vertragsverlängerung) und entfernt Kanäle
geschlossener Tickets. Ticket-Meldungen gehen in einen eigenen Support-Kanal
(#ticket-log, wird automatisch angelegt) statt in den Systemmeldungs-Kanal.
Ohne Kategorie bleibt das bisherige Verhalten (private Threads) unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 12:11:51 +02:00
Kundencenter
a9893b6d32 fix(worker): Lease/Heartbeat statt starrer 5-Minuten-Grenze bei Jobwiederaufnahme
Nightbot-Befund #99/#16 (P1): recoverStale gab "running"-Jobs nach starren
fünf Minuten wieder frei, ohne zu prüfen, ob der ursprüngliche Worker noch
aktiv daran arbeitet. Ein legitim länger laufender Job (z. B. ein
langsamer Connector-Abgleich) konnte dadurch von einem zweiten Worker
parallel erneut gestartet werden – Doppelausführung, z. B. doppelte
Provisionierung oder doppelter Mailversand.

- Migration 032: jobs.lease_id (pro Übernahme neu vergeben) und
  jobs.locked_by (Worker-Kennung, nur Diagnose).
- runOnce vergibt beim Übernehmen einen frischen Lease und hält locked_at
  per Heartbeat alle 60 s aktuell, solange der Handler läuft. Jedes
  Abschluss-UPDATE (Erfolg wie Fehler) ist an genau diesen Lease gebunden
  (WHERE lease_id = ?); hat recoverStale die Zeile inzwischen doch
  freigegeben, verpufft ein verspätetes Ergebnis wirkungslos statt den
  neuen Versuch zu überschreiben.
- recoverStale reißt jetzt nur noch Jobs an sich, deren Heartbeat
  tatsächlich ausgeblieben ist (locked_at älter als 5 Minuten), nicht
  mehr solche, die einfach nur lange laufen. Löscht lease_id beim
  Freigeben mit.

Verifiziert: (a) SQL-Ebene direkt durchexerziert – frischer Heartbeat
verhindert das Anreißen, ausgebliebener Heartbeat löst es aus, ein
verspätetes Abschluss-UPDATE mit altem Lease betrifft 0 Zeilen; (b) echter
Code über runOnce/recoverStale gegen die Testdatenbank – Job durchläuft
Übernahme, Fehlschlag, lease-gebundenes Abschluss-UPDATE korrekt. Worker
neu gestartet, läuft fehlerfrei, 1437 bestehende Jobs weiterhin 'succeeded'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 11:41:43 +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
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