kundencenter/packages/platform/src/config.ts
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

38 lines
1.7 KiB
TypeScript

import { readdirSync, readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
/** Lädt *.env aus KC_ENV_DIR (Standard /etc/kundencenter), ohne bereits gesetzte Variablen zu überschreiben. */
function loadEnvDir(dir: string): void {
if (!existsSync(dir)) return;
for (const f of readdirSync(dir).filter((n) => n.endsWith('.env')).sort()) {
for (const line of readFileSync(join(dir, f), 'utf8').split('\n')) {
const m = /^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/.exec(line);
if (m && m[1] && process.env[m[1]] === undefined) process.env[m[1]] = m[2] ?? '';
}
}
}
loadEnvDir(process.env.KC_ENV_DIR ?? '/etc/kundencenter');
function req(name: string): string {
const v = process.env[name];
if (!v) throw new Error(`Konfiguration fehlt: ${name}`);
return v;
}
export const config = {
env: process.env.KC_NODE_ENV ?? 'development',
isProd: (process.env.KC_NODE_ENV ?? 'development') === 'production',
port: Number(process.env.KC_API_PORT ?? 4100),
baseUrl: process.env.KC_BASE_URL ?? 'http://localhost:4101',
/** Erlaubte Browser-Origins (CSRF/Origin-Prüfung): baseUrl plus optional KC_ALLOWED_ORIGINS (kommagetrennt). */
allowedOrigins: new Set([process.env.KC_BASE_URL ?? 'http://localhost:4101', ...(process.env.KC_ALLOWED_ORIGINS ?? '').split(',').map((o) => o.trim()).filter(Boolean)]),
secretKey: Buffer.from(req('KC_SECRET_KEY'), 'base64'),
db: {
host: process.env.DB_HOST ?? '127.0.0.1',
port: Number(process.env.DB_PORT ?? 3306),
database: req('DB_NAME'),
user: req('DB_USER'),
password: req('DB_PASSWORD'),
},
};
if (config.secretKey.length !== 32) throw new Error('KC_SECRET_KEY muss 32 Byte (base64) sein');