Portal-Dienste und geteiltes Design-System für die anderen Apps

- Dienste (Gitea, Kanban, Cloud …) werden im System-Tab gepflegt und
  erscheinen als Kacheln auf der Startseite; GET /api/services ist
  öffentlich, Pflege braucht den settings-Scope
- /brand.css liefert die Design-Tokens (Farben live aus dem Brand-Tab,
  ändern sich damit überall mit) plus Basis-Klassen d4rk-card,
  d4rk-btn, d4rk-title, d4rk-tag
- /brand-nav.js baut die D4RKST3R-Leiste in jede fremde App ein, mit
  Links zum Hub und zu allen gepflegten Diensten; die Links stecken
  fertig im Skript, dadurch kein zweiter Request und kein CORS nötig
- Beide Dateien mit offenem CORS-Header und 5 Minuten Cache
- Doku für Anbindung + Schriften in docs/sso.md ergänzt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 13:28:50 +02:00
co-authored by Claude Fable 5
parent 70cae85064
commit c73b4e1074
9 changed files with 350 additions and 1 deletions
+24
View File
@@ -516,6 +516,30 @@ export function deleteUserData(userId) {
return result;
}
// Portal: eigene Dienste (Gitea, Kanban, Cloud …) für die Kachel-Übersicht
db.exec(`
CREATE TABLE IF NOT EXISTS services (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
url TEXT NOT NULL,
icon TEXT NOT NULL DEFAULT '🔗',
description TEXT NOT NULL DEFAULT '',
sort INTEGER NOT NULL DEFAULT 0
);
`);
const insertService = db.prepare(
'INSERT INTO services (name, url, icon, description, sort) VALUES (@name, @url, @icon, @description, @sort)'
);
const updateServiceStmt = db.prepare(
'UPDATE services SET name = @name, url = @url, icon = @icon, description = @description, sort = @sort WHERE id = @id'
);
const listServicesStmt = db.prepare('SELECT * FROM services ORDER BY sort, name');
const deleteServiceStmt = db.prepare('DELETE FROM services WHERE id = ?');
export const createService = (s) => insertService.run(s).lastInsertRowid;
export const updateService = (s) => updateServiceStmt.run(s).changes > 0;
export const listServices = () => listServicesStmt.all();
export const deleteService = (id) => deleteServiceStmt.run(id).changes > 0;
// SSO: Fremd-Apps (Kanban, Platform …), die den Discord-Login des Bots mitnutzen
db.exec(`
CREATE TABLE IF NOT EXISTS sso_apps (