SIP_GoldBees_Database/db_migrations/20260120_add_roles.sql
2026-02-01 14:14:57 +00:00

106 lines
2.9 KiB
PL/PgSQL

BEGIN;
ALTER TABLE app_user
ADD COLUMN IF NOT EXISTS role TEXT NOT NULL DEFAULT 'USER';
CREATE INDEX IF NOT EXISTS idx_app_user_role ON app_user(role);
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_app_user_role') THEN
ALTER TABLE app_user
ADD CONSTRAINT chk_app_user_role
CHECK (role IN ('USER','ADMIN','SUPER_ADMIN'));
END IF;
END $$;
UPDATE app_user
SET role = 'SUPER_ADMIN'
WHERE role <> 'SUPER_ADMIN' AND COALESCE(is_super_admin, false) = true;
UPDATE app_user
SET role = 'ADMIN'
WHERE role = 'USER' AND COALESCE(is_admin, false) = true;
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'app_user' AND column_name = 'is_admin'
) THEN
UPDATE app_user
SET is_admin = (role IN ('ADMIN','SUPER_ADMIN'))
WHERE is_admin IS DISTINCT FROM (role IN ('ADMIN','SUPER_ADMIN'));
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'app_user' AND column_name = 'is_super_admin'
) THEN
UPDATE app_user
SET is_super_admin = (role = 'SUPER_ADMIN')
WHERE is_super_admin IS DISTINCT FROM (role = 'SUPER_ADMIN');
END IF;
END $$;
CREATE TABLE IF NOT EXISTS admin_role_audit (
id BIGSERIAL PRIMARY KEY,
actor_user_id TEXT NOT NULL,
target_user_id TEXT NOT NULL,
old_role TEXT NOT NULL,
new_role TEXT NOT NULL,
changed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE OR REPLACE FUNCTION prevent_super_admin_delete()
RETURNS trigger AS $$
BEGIN
IF OLD.role = 'SUPER_ADMIN' OR OLD.is_super_admin THEN
RAISE EXCEPTION 'cannot delete super admin user';
END IF;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
DROP VIEW IF EXISTS admin_user_metrics;
CREATE OR REPLACE VIEW admin_user_metrics AS
WITH session_stats AS (
SELECT
user_id,
MIN(created_at) AS first_session_at,
MAX(COALESCE(last_seen_at, created_at)) AS last_login_at
FROM app_session
GROUP BY user_id
),
run_stats AS (
SELECT
user_id,
COUNT(*) AS runs_count,
MAX(CASE WHEN status = 'RUNNING' THEN run_id END) AS active_run_id,
MAX(CASE WHEN status = 'RUNNING' THEN status END) AS active_run_status,
MIN(created_at) AS first_run_at
FROM strategy_run
GROUP BY user_id
),
broker_stats AS (
SELECT user_id, BOOL_OR(connected) AS broker_connected
FROM user_broker
GROUP BY user_id
)
SELECT
u.id AS user_id,
u.username,
u.role,
(u.role IN ('ADMIN','SUPER_ADMIN')) AS is_admin,
COALESCE(session_stats.first_session_at, run_stats.first_run_at) AS created_at,
session_stats.last_login_at,
COALESCE(run_stats.runs_count, 0) AS runs_count,
run_stats.active_run_id,
run_stats.active_run_status,
COALESCE(broker_stats.broker_connected, FALSE) AS broker_connected
FROM app_user u
LEFT JOIN session_stats ON session_stats.user_id = u.id
LEFT JOIN run_stats ON run_stats.user_id = u.id
LEFT JOIN broker_stats ON broker_stats.user_id = u.id;
COMMIT;