ITSM/schema.sql

142 lines
6.7 KiB
SQL

-- ITSM-Plattform: PostgreSQL-Schema
-- Mandantenfaehig (mehrere Organisationen auf einer Instanz, Daten per tenant_id getrennt).
-- Enthaelt audit_log fuer Nachvollziehbarkeit gemaess ISO 27001 A.12.4 / DSGVO Art. 30.
CREATE TABLE IF NOT EXISTS tenants (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
dsb_name TEXT, -- Datenschutzbeauftragter/Kontakt
dsb_email TEXT,
retention_tickets_days INTEGER NOT NULL DEFAULT 1095, -- DSGVO Speicherbegrenzung (Default ~3 Jahre)
retention_audit_days INTEGER NOT NULL DEFAULT 1825, -- Audit-Log-Aufbewahrung (Default ~5 Jahre)
sla_antwort_minuten INTEGER NOT NULL DEFAULT 480,
sla_loesung_minuten INTEGER NOT NULL DEFAULT 2880
);
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'agent', -- 'admin' | 'agent'
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
last_login_at TIMESTAMPTZ
);
CREATE TABLE IF NOT EXISTS services (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
name TEXT NOT NULL,
beschreibung TEXT,
kategorie TEXT,
gebucht BOOLEAN NOT NULL DEFAULT TRUE,
endpoint TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS tickets (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
ticket_nr TEXT NOT NULL,
titel TEXT NOT NULL,
beschreibung TEXT,
service_id INTEGER REFERENCES services(id) ON DELETE SET NULL,
status TEXT NOT NULL DEFAULT 'Offen',
prioritaet TEXT NOT NULL DEFAULT 'Mittel',
kategorie TEXT NOT NULL DEFAULT 'Task',
zugewiesen_an TEXT,
ersteller_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
fortschritt INTEGER NOT NULL DEFAULT 0,
sla_antwort_minuten INTEGER,
sla_loesung_minuten INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, ticket_nr)
);
CREATE TABLE IF NOT EXISTS ticket_timeline (
id SERIAL PRIMARY KEY,
ticket_id INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
tenant_id INTEGER NOT NULL,
zeit TIMESTAMPTZ NOT NULL DEFAULT now(),
akteur TEXT,
text TEXT
);
CREATE TABLE IF NOT EXISTS audit_log (
id SERIAL PRIMARY KEY,
tenant_id INTEGER REFERENCES tenants(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
zeit TIMESTAMPTZ NOT NULL DEFAULT now(),
aktion TEXT NOT NULL,
entity_typ TEXT,
entity_id TEXT,
details JSONB,
ip TEXT
);
CREATE INDEX IF NOT EXISTS idx_tickets_tenant_status ON tickets (tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_tickets_tenant_kategorie ON tickets (tenant_id, kategorie);
CREATE INDEX IF NOT EXISTS idx_timeline_ticket ON ticket_timeline (ticket_id);
CREATE INDEX IF NOT EXISTS idx_audit_tenant_zeit ON audit_log (tenant_id, zeit DESC);
CREATE INDEX IF NOT EXISTS idx_services_tenant ON services (tenant_id);
CREATE INDEX IF NOT EXISTS idx_users_tenant ON users (tenant_id);
-- Vorbereitung fuer spaetere AD/LDAP-Anbindung (Nutzer-Vorgabe 2026-07-13):
-- 'local' = im ITSM selbst verwaltetes Konto (heutiger Stand), 'ldap' reserviert
-- fuer die kommende Ausbaustufe. Kein Verhaltensunterschied heute, nur Vorbereitung.
ALTER TABLE users ADD COLUMN IF NOT EXISTS auth_source TEXT NOT NULL DEFAULT 'local';
-- Zusaetzliche Nutzer-Details (Nutzer-Vorgabe 2026-07-13: "Mehr Details fuer
-- die User (Telefon, Adresse, etc.)"). Alle optional, da nicht jeder Mandant
-- diese Angaben pflegen moechte.
ALTER TABLE users ADD COLUMN IF NOT EXISTS vorname TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS nachname TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS telefon TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS abteilung TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS adresse TEXT;
-- ── Wissensdatenbank ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS kb_articles (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
titel TEXT NOT NULL,
kategorie TEXT NOT NULL DEFAULT 'Allgemein',
inhalt TEXT NOT NULL,
tags TEXT,
autor_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_kb_tenant_kategorie ON kb_articles (tenant_id, kategorie);
-- ── CMDB (Configuration Management Database) ──────────────────────────────────
CREATE TABLE IF NOT EXISTS configuration_items (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
name TEXT NOT NULL,
ci_typ TEXT NOT NULL DEFAULT 'Sonstiges',
status TEXT NOT NULL DEFAULT 'Aktiv',
beschreibung TEXT,
attribute JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_ci_tenant_typ ON configuration_items (tenant_id, ci_typ);
CREATE INDEX IF NOT EXISTS idx_ci_tenant_status ON configuration_items (tenant_id, status);
CREATE TABLE IF NOT EXISTS ci_relationships (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
from_ci_id INTEGER NOT NULL REFERENCES configuration_items(id) ON DELETE CASCADE,
to_ci_id INTEGER NOT NULL REFERENCES configuration_items(id) ON DELETE CASCADE,
beziehungs_typ TEXT NOT NULL DEFAULT 'haengt ab von',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_cirel_tenant ON ci_relationships (tenant_id);
CREATE INDEX IF NOT EXISTS idx_cirel_from ON ci_relationships (from_ci_id);
CREATE INDEX IF NOT EXISTS idx_cirel_to ON ci_relationships (to_ci_id);