- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
205 lines
9.4 KiB
SQL
205 lines
9.4 KiB
SQL
-- =====================================================================
|
|
-- aether-admin — Datenbankschema
|
|
--
|
|
-- Grundsatz: Das Admin-Panel führt Buch und greift durch, aber es
|
|
-- erfindet keine Wahrheit. Rechte und Bans leben in `aether_users`
|
|
-- (aus aether-core) — dort prüft der Core sie bereits beim Connect.
|
|
-- Dieses Schema ergänzt nur, was der Core nicht kennt: Verwarnungen,
|
|
-- Reports, Notizen, das Prüfprotokoll und die verwaltbaren Datensätze
|
|
-- einzelner Panel-Bereiche.
|
|
--
|
|
-- Voraussetzung: sql/aether.sql (aether_users, aether_characters) ist
|
|
-- eingespielt. `admin_group` und die Bann-Spalten existieren dort schon.
|
|
-- =====================================================================
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Verwarnungen
|
|
--
|
|
-- Eine Verwarnung ist eine dokumentierte Ermahnung, mehr nicht. Sie
|
|
-- sperrt nichts und läuft nicht ab — sie steht in der Akte, bis ein
|
|
-- Admin sie entfernt.
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_warns` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`license` VARCHAR(64) NOT NULL COMMENT 'Betroffener Benutzer',
|
|
`character` INT UNSIGNED DEFAULT NULL COMMENT 'Charakter, falls bekannt',
|
|
`name` VARCHAR(64) NOT NULL COMMENT 'Anzeigename zum Zeitpunkt',
|
|
`grund` VARCHAR(255) NOT NULL,
|
|
`admin_license` VARCHAR(64) DEFAULT NULL,
|
|
`admin_name` VARCHAR(64) NOT NULL DEFAULT 'System',
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_license` (`license`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Bann-Verlauf
|
|
--
|
|
-- Die Durchsetzung liegt in `aether_users` (is_banned/ban_reason/
|
|
-- ban_until) — der Core sperrt darüber beim Connect. Diese Tabelle ist
|
|
-- das Gedächtnis: jeder Bann und jede Entbannung bleibt nachlesbar,
|
|
-- auch für Identifier ohne Benutzerkonto (Offline-Ban).
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_bans` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`license` VARCHAR(64) DEFAULT NULL COMMENT 'Lizenz, falls bekannt',
|
|
`name` VARCHAR(64) NOT NULL,
|
|
`grund` VARCHAR(255) NOT NULL,
|
|
`admin_license` VARCHAR(64) DEFAULT NULL,
|
|
`admin_name` VARCHAR(64) NOT NULL DEFAULT 'System',
|
|
`bis` DATETIME DEFAULT NULL COMMENT 'NULL = dauerhaft',
|
|
`aktiv` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '0 = wieder entbannt',
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
`aufgehoben_am` DATETIME DEFAULT NULL,
|
|
`aufgehoben_von` VARCHAR(64) DEFAULT NULL,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_license` (`license`),
|
|
KEY `idx_aktiv` (`aktiv`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Reports / Support-Tickets
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_reports` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`melder_license` VARCHAR(64) DEFAULT NULL,
|
|
`melder_name` VARCHAR(64) NOT NULL,
|
|
`betreff` VARCHAR(160) NOT NULL,
|
|
`nachricht` TEXT DEFAULT NULL,
|
|
`prioritaet` TINYINT NOT NULL DEFAULT 3 COMMENT '1 hoch … 3 niedrig',
|
|
`status` VARCHAR(16) NOT NULL DEFAULT 'offen'
|
|
COMMENT 'offen/bearbeitung/geschlossen',
|
|
`bearbeiter` VARCHAR(64) DEFAULT NULL COMMENT 'Admin, der übernommen hat',
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
`aktualisiert_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_status` (`status`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- Nachrichtenverlauf eines Reports (Antworten des Teams / Rückfragen)
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_report_msgs` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`report` INT UNSIGNED NOT NULL,
|
|
`autor` VARCHAR(64) NOT NULL,
|
|
`ist_admin` TINYINT(1) NOT NULL DEFAULT 1,
|
|
`nachricht` TEXT NOT NULL,
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_report` (`report`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Interne Spielernotizen (nur für das Team sichtbar)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_notes` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`license` VARCHAR(64) NOT NULL,
|
|
`name` VARCHAR(64) NOT NULL,
|
|
`notiz` TEXT NOT NULL,
|
|
`admin_name` VARCHAR(64) NOT NULL DEFAULT 'System',
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_license` (`license`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Prüfprotokoll (Audit-Log)
|
|
--
|
|
-- Jede eingreifende Aktion hinterlässt eine Spur: wer, was, an wem,
|
|
-- mit welchem Detail. Einträge werden nur geschrieben und gelesen.
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_log` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`admin_license` VARCHAR(64) DEFAULT NULL,
|
|
`admin_name` VARCHAR(64) NOT NULL DEFAULT 'System',
|
|
`aktion` VARCHAR(64) NOT NULL,
|
|
`ziel` VARCHAR(96) DEFAULT NULL,
|
|
`detail` VARCHAR(512) DEFAULT NULL,
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`),
|
|
KEY `idx_admin` (`admin_license`),
|
|
KEY `idx_zeit` (`erstellt_am`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Ankündigungen (Verlauf der Rundmeldungen)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_broadcasts` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`typ` VARCHAR(16) NOT NULL DEFAULT 'info',
|
|
`nachricht` VARCHAR(512) NOT NULL,
|
|
`admin_name` VARCHAR(64) NOT NULL DEFAULT 'System',
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Staatsprojekte (Abstimmungen; später koppelbar an eine Handy-App)
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_projects` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`titel` VARCHAR(160) NOT NULL,
|
|
`beschreibung` TEXT DEFAULT NULL,
|
|
`kosten` BIGINT NOT NULL DEFAULT 0,
|
|
`ja` INT UNSIGNED NOT NULL DEFAULT 0,
|
|
`nein` INT UNSIGNED NOT NULL DEFAULT 0,
|
|
`abstimmung_offen` TINYINT(1) NOT NULL DEFAULT 1,
|
|
`aktiviert` TINYINT(1) NOT NULL DEFAULT 0,
|
|
`erstellt_am` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (`id`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Immobilien
|
|
--
|
|
-- Solange kein eigenständiges Immobilien-Resource läuft, verwaltet
|
|
-- das Panel die Objekte selbst. `HoleImmobilien` ist als Export
|
|
-- vorgesehen, damit ein späteres Modul die Hoheit übernehmen kann.
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_properties` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`name` VARCHAR(96) NOT NULL,
|
|
`ort` VARCHAR(96) DEFAULT NULL,
|
|
`preis` BIGINT NOT NULL DEFAULT 0,
|
|
`besitzer` INT UNSIGNED DEFAULT NULL COMMENT 'Charakter aus aether_characters',
|
|
PRIMARY KEY (`id`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Fraktions-Metadaten
|
|
--
|
|
-- Mitgliedschaft ergibt sich aus aether_characters.faction. Hier
|
|
-- liegen nur die Zusatzangaben (Gehalt, Farbe), die der Charakter
|
|
-- nicht kennt.
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_factions` (
|
|
`name` VARCHAR(48) NOT NULL,
|
|
`gehalt` INT NOT NULL DEFAULT 0 COMMENT 'Payday pro Stunde',
|
|
`farbe` VARCHAR(9) NOT NULL DEFAULT '#34d39a',
|
|
`aktiv` TINYINT(1) NOT NULL DEFAULT 1,
|
|
PRIMARY KEY (`name`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Persistierte Server-Einstellungen (Schlüssel/Wert)
|
|
-- z. B. staatskasse, steuersatz, pvp, whitelist, servername …
|
|
-- ---------------------------------------------------------------------
|
|
CREATE TABLE IF NOT EXISTS `aether_admin_settings` (
|
|
`schluessel` VARCHAR(48) NOT NULL,
|
|
`wert` VARCHAR(255) DEFAULT NULL,
|
|
PRIMARY KEY (`schluessel`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Grundbestand (nur einmalig, wenn leer)
|
|
-- ---------------------------------------------------------------------
|
|
INSERT INTO `aether_admin_settings` (`schluessel`, `wert`) VALUES
|
|
('staatskasse', '12480000'),
|
|
('steuersatz', '8'),
|
|
('pvp', '1'),
|
|
('whitelist', '1'),
|
|
('voice', '1'),
|
|
('economy', '1')
|
|
ON DUPLICATE KEY UPDATE `schluessel` = `schluessel`;
|