rework issue system

This commit is contained in:
Matthias Hochmeister
2026-03-24 14:21:17 +01:00
parent abb337c683
commit 6c7531438e
9 changed files with 1260 additions and 189 deletions

View File

@@ -0,0 +1,128 @@
-- 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 = (
SELECT jsonb_strip_nulls(
(value::jsonb - '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
)::text
FROM app_settings WHERE key = 'permission_deps'
)
WHERE key = 'permission_deps';