Files
----/backend/migrations/003_audit_triggers.sql
smolkik-code 9b15f8b09c feat: CMDB full-stack app - FastAPI + PostgreSQL + React
- PostgreSQL schema: 14 tables, JSONB attributes, audit triggers, soft delete
- FastAPI backend: CRUD, search/filter, relationship graph, bulk import, JWT RBAC
- React frontend: CI table, detail card, force-graph, dashboard
- Seed data: homelab scenario (Proxmox, Mikrotik, VMs, services)
- Docker Compose + Kubernetes manifests
- 20 backend tests (pytest + httpx)
2026-06-25 13:01:40 +07:00

75 lines
2.6 KiB
PL/PgSQL

-- ============================================================
-- Audit Trigger Migration — Run on production after 001
-- Adds per-field audit triggers to configuration_items
-- ============================================================
BEGIN;
-- Fine-grained audit: capture individual field changes
CREATE OR REPLACE FUNCTION audit_ci_fields_func()
RETURNS TRIGGER AS $$
DECLARE
col TEXT;
old_val TEXT;
new_val TEXT;
BEGIN
IF TG_OP = 'UPDATE' THEN
FOR col IN SELECT unnest(ARRAY[
'name', 'description', 'status', 'location_id',
'serial_number', 'asset_tag', 'purchase_date', 'warranty_expiry',
'tags'
])
LOOP
old_val := row_to_json(OLD) ->> col;
new_val := row_to_json(NEW) ->> col;
IF old_val IS DISTINCT FROM new_val THEN
INSERT INTO changelog (ci_id, action, field_name, old_value, new_value, version, snapshot)
VALUES (NEW.id, 'update', col, old_val, NEW.version, NEW.version, to_jsonb(NEW));
END IF;
END LOOP;
-- JSONB attributes changes
IF OLD.attributes IS DISTINCT FROM NEW.attributes THEN
INSERT INTO changelog (ci_id, action, field_name, old_value, new_value, version, snapshot)
VALUES (NEW.id, 'update', 'attributes', OLD.attributes::text, NEW.attributes::text, NEW.version, to_jsonb(NEW));
END IF;
NEW.updated_at = now();
NEW.version = OLD.version + 1;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_ci_audit ON configuration_items;
CREATE TRIGGER trg_ci_field_audit
AFTER UPDATE ON configuration_items
FOR EACH ROW
EXECUTE FUNCTION audit_ci_fields_func();
-- Relationship audit
CREATE OR REPLACE FUNCTION audit_relationship_func()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO changelog (ci_id, action, field_name, new_value, version, snapshot)
VALUES (NEW.source_ci_id, 'relationship_add', NEW.relationship::text,
NEW.target_ci_id::text, 0, to_jsonb(NEW));
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO changelog (ci_id, action, field_name, old_value, version, snapshot)
VALUES (OLD.source_ci_id, 'relationship_remove', OLD.relationship::text,
OLD.target_ci_id::text, 0, to_jsonb(OLD));
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_relationship_audit
AFTER INSERT OR DELETE ON ci_relationships
FOR EACH ROW
EXECUTE FUNCTION audit_relationship_func();
COMMIT;