-- Migration 053: Issues rework -- 1. Fix update trigger bug (uses wrong column name) -- 2. Dynamic issue types table (issue_typen) -- 3. Migrate issues.typ → issues.typ_id -- 4. Permission rework: replace issues:manage with granular permissions -- ═══════════════════════════════════════════════════════════════════════════ -- 1. Fix update trigger -- The old trigger calls update_aktualisiert_am() which sets NEW.aktualisiert_am, -- but issues table uses updated_at → crashes every UPDATE. -- ═══════════════════════════════════════════════════════════════════════════ DROP TRIGGER IF EXISTS trg_issues_updated ON issues; CREATE OR REPLACE FUNCTION update_issues_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_issues_updated BEFORE UPDATE ON issues FOR EACH ROW EXECUTE FUNCTION update_issues_updated_at(); -- ═══════════════════════════════════════════════════════════════════════════ -- 2. Dynamic types table -- ═══════════════════════════════════════════════════════════════════════════ CREATE TABLE IF NOT EXISTS issue_typen ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, parent_id INT REFERENCES issue_typen(id) ON DELETE CASCADE, icon VARCHAR(50) DEFAULT NULL, farbe VARCHAR(20) DEFAULT NULL, erlaubt_abgelehnt BOOLEAN NOT NULL DEFAULT true, sort_order INT NOT NULL DEFAULT 0, aktiv BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_issue_typen_parent ON issue_typen(parent_id); -- Seed default types INSERT INTO issue_typen (id, name, parent_id, icon, farbe, erlaubt_abgelehnt, sort_order) VALUES (1, 'Bug', NULL, 'BugReport', 'error', false, 1), (2, 'Funktionsanfrage', NULL, 'FiberNew', 'info', true, 2), (3, 'Sonstiges', NULL, 'HelpOutline', 'action', true, 3) ON CONFLICT (id) DO NOTHING; -- Ensure sequence is past seeded IDs SELECT setval('issue_typen_id_seq', GREATEST((SELECT MAX(id) FROM issue_typen), 3)); -- ═══════════════════════════════════════════════════════════════════════════ -- 3. Migrate issues.typ column → typ_id FK -- ═══════════════════════════════════════════════════════════════════════════ ALTER TABLE issues ADD COLUMN IF NOT EXISTS typ_id INT REFERENCES issue_typen(id) ON DELETE SET NULL; -- Migrate existing data UPDATE issues SET typ_id = 1 WHERE typ = 'bug' AND typ_id IS NULL; UPDATE issues SET typ_id = 2 WHERE typ = 'feature' AND typ_id IS NULL; UPDATE issues SET typ_id = 3 WHERE typ = 'sonstiges' AND typ_id IS NULL; -- Fallback: anything unmapped → Sonstiges UPDATE issues SET typ_id = 3 WHERE typ_id IS NULL; -- Drop old constraint and column ALTER TABLE issues DROP CONSTRAINT IF EXISTS issues_typ_check; ALTER TABLE issues DROP COLUMN IF EXISTS typ; CREATE INDEX IF NOT EXISTS idx_issues_typ_id ON issues(typ_id); CREATE INDEX IF NOT EXISTS idx_issues_zugewiesen_an ON issues(zugewiesen_an); -- ═══════════════════════════════════════════════════════════════════════════ -- 4. Permission rework -- Replace issues:manage with granular permissions -- ═══════════════════════════════════════════════════════════════════════════ -- 4a. Find all groups that had issues:manage and give them the new permissions -- We use a temp table to store the groups that had manage CREATE TEMP TABLE IF NOT EXISTS _issues_manage_groups AS SELECT authentik_group FROM group_permissions WHERE permission_id = 'issues:manage'; -- 4b. Insert new permissions INSERT INTO permissions (id, feature_group_id, label, description, sort_order) VALUES ('issues:change_status', 'issues', 'Status ändern', 'Status ändern und kommentieren', 4), ('issues:edit', 'issues', 'Bearbeiten', 'Issues bearbeiten (Titel, Beschreibung, Typ, Priorität, Zuweisung)', 5), ('issues:edit_settings', 'issues', 'Einstellungen', 'Issue-Kategorien verwalten', 6), ('issues:delete', 'issues', 'Löschen', 'Issues löschen', 7) ON CONFLICT (id) DO NOTHING; -- 4c. Grant all new permissions to groups that had manage INSERT INTO group_permissions (authentik_group, permission_id) SELECT g.authentik_group, p.id FROM _issues_manage_groups g CROSS JOIN (VALUES ('issues:view_all'), ('issues:change_status'), ('issues:edit'), ('issues:edit_settings'), ('issues:delete') ) AS p(id) ON CONFLICT DO NOTHING; -- 4d. Remove old manage permission DELETE FROM group_permissions WHERE permission_id = 'issues:manage'; DELETE FROM permissions WHERE id = 'issues:manage'; DROP TABLE IF EXISTS _issues_manage_groups; -- 4e. Update permission_deps in app_settings JSON -- Remove old issues entries and add new ones UPDATE app_settings SET value = jsonb_strip_nulls( (value - 'issues:view_own' - 'issues:view_all' - 'issues:manage') || '{ "issues:create": ["issues:view_own"], "issues:view_all": ["issues:view_own"], "issues:change_status": ["issues:view_all"], "issues:edit": ["issues:view_all"], "issues:delete": ["issues:view_all"], "issues:edit_settings": ["issues:view_all"] }'::jsonb ) WHERE key = 'permission_deps';