feat: database-first refactor, unified architecture and visual developer manual
Summary of changes: - Consolidated Studio+CMS and Experience+LMS into unified services. - Moved core business logic (enrollment, grading, auth) to PostgreSQL functions. - Implemented advanced auditing via DB triggers and session context. - Added gamification (XP/Levels/Leaderboards) and logic encapsulation. - Updated installation/diagnostic scripts for the new architecture. - Created a comprehensive Visual Developer Manual in README.md with hardware scaling.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
-- Migration: Advanced Auditing and Automatic Triggers
|
||||
-- Upgrade audit_logs table and implement automated change tracking
|
||||
|
||||
-- 1. Upgrade audit_logs table
|
||||
ALTER TABLE audit_logs
|
||||
ADD COLUMN IF NOT EXISTS event_type VARCHAR(50) DEFAULT 'USER_EVENT',
|
||||
ADD COLUMN IF NOT EXISTS old_data JSONB,
|
||||
ADD COLUMN IF NOT EXISTS new_data JSONB,
|
||||
ADD COLUMN IF NOT EXISTS ip_address INET,
|
||||
ADD COLUMN IF NOT EXISTS public_ip INET,
|
||||
ADD COLUMN IF NOT EXISTS user_agent TEXT,
|
||||
ADD COLUMN IF NOT EXISTS metadata JSONB DEFAULT '{}';
|
||||
|
||||
-- 2. Create Audit Trigger Function
|
||||
CREATE OR REPLACE FUNCTION fn_trigger_audit_log()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_user_id UUID;
|
||||
v_org_id UUID;
|
||||
v_ip INET;
|
||||
v_user_agent TEXT;
|
||||
v_event_type VARCHAR(50);
|
||||
v_old_data JSONB := NULL;
|
||||
v_new_data JSONB := NULL;
|
||||
v_action VARCHAR(50);
|
||||
BEGIN
|
||||
-- Try to get context from session variables
|
||||
BEGIN
|
||||
v_user_id := current_setting('app.current_user_id', true)::UUID;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
v_user_id := NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
v_org_id := current_setting('app.current_org_id', true)::UUID;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
v_org_id := NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
v_ip := current_setting('app.client_ip', true)::INET;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
v_ip := NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
v_user_agent := current_setting('app.user_agent', true);
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
v_user_agent := NULL;
|
||||
END;
|
||||
|
||||
BEGIN
|
||||
v_event_type := current_setting('app.event_type', true);
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
v_event_type := 'USER_EVENT';
|
||||
END;
|
||||
|
||||
-- Handle different operations
|
||||
IF (TG_OP = 'DELETE') THEN
|
||||
v_old_data := to_jsonb(OLD);
|
||||
v_action := 'DELETE';
|
||||
ELSIF (TG_OP = 'UPDATE') THEN
|
||||
v_old_data := to_jsonb(OLD);
|
||||
v_new_data := to_jsonb(NEW);
|
||||
v_action := 'UPDATE';
|
||||
ELSIF (TG_OP = 'INSERT') THEN
|
||||
v_new_data := to_jsonb(NEW);
|
||||
v_action := 'INSERT';
|
||||
END IF;
|
||||
|
||||
-- Insert into audit_logs
|
||||
INSERT INTO audit_logs (
|
||||
organization_id,
|
||||
user_id,
|
||||
action,
|
||||
entity_type,
|
||||
entity_id,
|
||||
event_type,
|
||||
old_data,
|
||||
new_data,
|
||||
ip_address,
|
||||
user_agent
|
||||
)
|
||||
VALUES (
|
||||
COALESCE(v_org_id, (CASE WHEN TG_OP = 'DELETE' THEN OLD.organization_id ELSE NEW.organization_id END)),
|
||||
v_user_id,
|
||||
v_action,
|
||||
TG_TABLE_NAME,
|
||||
CASE WHEN TG_OP = 'DELETE' THEN OLD.id ELSE NEW.id END,
|
||||
COALESCE(v_event_type, 'USER_EVENT'),
|
||||
v_old_data,
|
||||
v_new_data,
|
||||
v_ip,
|
||||
v_user_agent
|
||||
);
|
||||
|
||||
IF (TG_OP = 'DELETE') THEN
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 3. Attach triggers to core tables
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Courses
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'courses') THEN
|
||||
DROP TRIGGER IF EXISTS trg_audit_courses ON courses;
|
||||
CREATE TRIGGER trg_audit_courses
|
||||
AFTER INSERT OR UPDATE OR DELETE ON courses
|
||||
FOR EACH ROW EXECUTE FUNCTION fn_trigger_audit_log();
|
||||
END IF;
|
||||
|
||||
-- Lessons
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'lessons') THEN
|
||||
DROP TRIGGER IF EXISTS trg_audit_lessons ON lessons;
|
||||
CREATE TRIGGER trg_audit_lessons
|
||||
AFTER INSERT OR UPDATE OR DELETE ON lessons
|
||||
FOR EACH ROW EXECUTE FUNCTION fn_trigger_audit_log();
|
||||
END IF;
|
||||
|
||||
-- Users
|
||||
IF EXISTS (SELECT 1 FROM pg_tables WHERE tablename = 'users') THEN
|
||||
DROP TRIGGER IF EXISTS trg_audit_users ON users;
|
||||
CREATE TRIGGER trg_audit_users
|
||||
AFTER INSERT OR UPDATE OR DELETE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION fn_trigger_audit_log();
|
||||
END IF;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user