diff --git a/backend/migrations/20260128113000_moderation_ledger_delete_guard_fix.sql b/backend/migrations/20260128113000_moderation_ledger_delete_guard_fix.sql new file mode 100644 index 0000000..ea01de8 --- /dev/null +++ b/backend/migrations/20260128113000_moderation_ledger_delete_guard_fix.sql @@ -0,0 +1,17 @@ +-- Fix moderation ledger delete protection: missing allow_ledger_delete setting must default to false + +CREATE OR REPLACE FUNCTION ledger_prevent_delete() +RETURNS TRIGGER AS $$ +BEGIN + -- Allow deletion only by superuser (for legal compliance like GDPR) + IF NOT COALESCE(current_setting('likwid.allow_ledger_delete', true), 'false')::boolean THEN + RAISE EXCEPTION 'Moderation ledger entries cannot be deleted. Set likwid.allow_ledger_delete = true for legal compliance deletions.'; + END IF; + + -- Log the deletion attempt + INSERT INTO ledger_deletion_log (entry_id, deleted_by, reason) + VALUES (OLD.id, current_user, current_setting('likwid.deletion_reason', true)); + + RETURN OLD; +END; +$$ LANGUAGE plpgsql; diff --git a/backend/migrations/20260127150000_demo_seed_data.sql b/backend/migrations_demo/20260127150000_demo_seed_data.sql similarity index 100% rename from backend/migrations/20260127150000_demo_seed_data.sql rename to backend/migrations_demo/20260127150000_demo_seed_data.sql diff --git a/backend/migrations_demo/20260128114000_demo_moderation_log_deterministic_ids.sql b/backend/migrations_demo/20260128114000_demo_moderation_log_deterministic_ids.sql new file mode 100644 index 0000000..8992edd --- /dev/null +++ b/backend/migrations_demo/20260128114000_demo_moderation_log_deterministic_ids.sql @@ -0,0 +1,55 @@ +-- Assign deterministic IDs to seeded moderation_log rows so demo reset can preserve baseline logs. + +-- RFC-001 description edit +WITH seed_row AS ( + SELECT id + FROM moderation_log + WHERE community_id = 'c0000001-0000-0000-0000-000000000001'::uuid + AND moderator_id = 'd0000001-0000-0000-0000-000000000002'::uuid + AND target_user_id IS NULL + AND action_type = 'content_edit' + AND reason = 'Updated RFC-001 description for clarity' + AND details = '{"proposal_id": "b0000001-0000-0000-0000-000000000001", "field": "description", "change_type": "formatting"}'::jsonb + ORDER BY created_at + LIMIT 1 +) +UPDATE moderation_log +SET id = 'f0000001-0000-0000-0000-000000000001'::uuid +WHERE id IN (SELECT id FROM seed_row) + AND NOT EXISTS (SELECT 1 FROM moderation_log WHERE id = 'f0000001-0000-0000-0000-000000000001'::uuid); + +-- Budget proposal thread warning +WITH seed_row AS ( + SELECT id + FROM moderation_log + WHERE community_id = 'c0000001-0000-0000-0000-000000000002'::uuid + AND moderator_id = 'd0000001-0000-0000-0000-000000000002'::uuid + AND target_user_id = 'd0000002-0000-0000-0000-000000000009'::uuid + AND action_type = 'warning' + AND reason = 'Off-topic discussion in budget proposal thread' + AND details = '{"rule": "community_guidelines_3", "comment_id": "comment_example_001"}'::jsonb + ORDER BY created_at + LIMIT 1 +) +UPDATE moderation_log +SET id = 'f0000001-0000-0000-0000-000000000002'::uuid +WHERE id IN (SELECT id FROM seed_row) + AND NOT EXISTS (SELECT 1 FROM moderation_log WHERE id = 'f0000001-0000-0000-0000-000000000002'::uuid); + +-- Proposal deadline extension +WITH seed_row AS ( + SELECT id + FROM moderation_log + WHERE community_id = 'c0000001-0000-0000-0000-000000000003'::uuid + AND moderator_id = 'd0000001-0000-0000-0000-000000000002'::uuid + AND target_user_id IS NULL + AND action_type = 'proposal_extended' + AND reason = 'Extended voting deadline by 48 hours due to technical issues' + AND details = '{"proposal_id": "b0000001-0000-0000-0000-000000000006", "original_end": "2026-01-10T00:00:00Z", "new_end": "2026-01-12T00:00:00Z"}'::jsonb + ORDER BY created_at + LIMIT 1 +) +UPDATE moderation_log +SET id = 'f0000001-0000-0000-0000-000000000003'::uuid +WHERE id IN (SELECT id FROM seed_row) + AND NOT EXISTS (SELECT 1 FROM moderation_log WHERE id = 'f0000001-0000-0000-0000-000000000003'::uuid); diff --git a/backend/migrations_demo/20260128115000_demo_seed_user_roles.sql b/backend/migrations_demo/20260128115000_demo_seed_user_roles.sql new file mode 100644 index 0000000..9ab7634 --- /dev/null +++ b/backend/migrations_demo/20260128115000_demo_seed_user_roles.sql @@ -0,0 +1,16 @@ +-- Ensure demo-seeded users have baseline platform roles for permission checks. + +-- Give all demo-seeded users the base 'user' platform role +INSERT INTO user_roles (user_id, role_id, community_id, granted_by) +SELECT u.id, r.id, NULL, u.id +FROM users u +JOIN roles r ON r.name = 'user' AND r.community_id IS NULL +WHERE u.id::text LIKE 'd000%' +ON CONFLICT (user_id, role_id, community_id) DO NOTHING; + +-- Make demo moderator a platform admin +INSERT INTO user_roles (user_id, role_id, community_id, granted_by) +SELECT 'd0000001-0000-0000-0000-000000000002'::uuid, r.id, NULL, 'd0000001-0000-0000-0000-000000000002'::uuid +FROM roles r +WHERE r.name = 'platform_admin' AND r.community_id IS NULL +ON CONFLICT (user_id, role_id, community_id) DO NOTHING; diff --git a/backend/migrations_demo/20260128130000_demo_convert_topic_delegations_to_community.sql b/backend/migrations_demo/20260128130000_demo_convert_topic_delegations_to_community.sql new file mode 100644 index 0000000..f0b5a55 --- /dev/null +++ b/backend/migrations_demo/20260128130000_demo_convert_topic_delegations_to_community.sql @@ -0,0 +1,26 @@ +UPDATE delegations d +SET + scope = 'community'::delegation_scope, + community_id = COALESCE(d.community_id, t.community_id), + topic_id = NULL +FROM topics t +WHERE d.scope = 'topic'::delegation_scope + AND d.topic_id = t.id; + +UPDATE delegation_log l +SET + scope = 'community'::delegation_scope, + community_id = COALESCE(l.community_id, t.community_id), + topic_id = NULL +FROM topics t +WHERE l.scope = 'topic'::delegation_scope + AND l.topic_id = t.id; + +UPDATE delegation_chains c +SET + scope = 'community'::delegation_scope, + community_id = COALESCE(c.community_id, t.community_id), + topic_id = NULL +FROM topics t +WHERE c.scope = 'topic'::delegation_scope + AND c.topic_id = t.id;