Files
ersteller/cli/create_audit_log.go
T

151 lines
3.9 KiB
Go

package main
import (
"context"
"git.gorlug.de/code/golang/ersteller-lib/ersteller"
"github.com/joho/godotenv"
"os"
)
func main() {
err := godotenv.Load()
if err != nil {
ersteller.LogError("Error loading .env file: %v", err)
panic(err)
}
dbUrl := os.Getenv("DATABASE_URL")
connpool, err := ersteller.CreatePostgresConnpool(dbUrl)
if err != nil {
ersteller.LogError("Failed to create connection pool: %v", err)
panic(err)
}
// https://medium.com/israeli-tech-radar/postgresql-trigger-based-audit-log-fd9d9d5e412c
sql := `
CREATE TABLE IF NOT EXISTS audit_log (
id serial PRIMARY KEY,
table_name TEXT,
record_id TEXT,
operation_type TEXT,
changed_at TIMESTAMP DEFAULT now(),
changed_by TEXT,
original_values jsonb,
new_values jsonb
);
CREATE OR REPLACE FUNCTION audit_trigger() RETURNS TRIGGER AS $$
DECLARE
new_data jsonb;
old_data jsonb;
key text;
new_values jsonb;
old_values jsonb;
user_id text;
BEGIN
user_id := current_setting('audit.user_id', true);
IF user_id IS NULL THEN
user_id := current_user;
END IF;
new_values := '{}';
old_values := '{}';
IF TG_OP = 'INSERT' THEN
new_data := to_jsonb(NEW);
new_values := new_data;
ELSIF TG_OP = 'UPDATE' THEN
new_data := to_jsonb(NEW);
old_data := to_jsonb(OLD);
FOR key IN SELECT jsonb_object_keys(new_data) INTERSECT SELECT jsonb_object_keys(old_data)
LOOP
IF new_data ->> key != old_data ->> key THEN
new_values := new_values || jsonb_build_object(key, new_data ->> key);
old_values := old_values || jsonb_build_object(key, old_data ->> key);
END IF;
END LOOP;
ELSIF TG_OP = 'DELETE' THEN
old_data := to_jsonb(OLD);
old_values := old_data;
FOR key IN SELECT jsonb_object_keys(old_data)
LOOP
old_values := old_values || jsonb_build_object(key, old_data ->> key);
END LOOP;
END IF;
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
INSERT INTO audit_log (table_name, record_id, operation_type, changed_by, original_values, new_values)
VALUES (TG_TABLE_NAME, NEW.id, TG_OP, user_id, old_values, new_values);
RETURN NEW;
ELSE
INSERT INTO audit_log (table_name, record_id, operation_type, changed_by, original_values, new_values)
VALUES (TG_TABLE_NAME, OLD.id, TG_OP, user_id, old_values, new_values);
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public."marketDataLead"
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public.group
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public."groupMembership"
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public."marketDataLeadNote"
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public.settings
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public.user
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
`
_, err = connpool.Exec(context.Background(), sql)
if err != nil {
ersteller.LogError("Failed to create audit log table: %v", err)
panic(err)
}
println("Created audit log table")
}
func createTriggerSQL(tableName string) string {
return `
CREATE OR REPLACE TRIGGER audit_log_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON public."marketDataLead"
FOR EACH ROW
EXECUTE FUNCTION audit_trigger();
`
}