feat(handler): add app
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|---|---|---|
|
||||
| 0001 | `migrations/0001_core.sql` | IAM 核心表(tenant/user/role/permission 等)与基础种子数据 |
|
||||
| 0002 | `migrations/0002_enabled_apps.sql` | enabled_apps(租户应用开通)、平台租户与平台权限(SuperAdmin) |
|
||||
| 0003 | `migrations/0003_app_lifecycle.sql` | apps 生命周期管理(扩展字段、变更记录、上下线审批) |
|
||||
| 0004 | `migrations/0004_password_reset.sql` | 密码重置(权限码与 Admin/SuperAdmin 授权) |
|
||||
|
||||
校验脚本映射(与 migrations 一一对应):
|
||||
|
||||
@@ -23,6 +25,8 @@
|
||||
|---|---|---|
|
||||
| 0001 | `scripts/db/verify/0001_core.sql` | 校验核心表、索引与基础种子 |
|
||||
| 0002 | `scripts/db/verify/0002_enabled_apps.sql` | 校验 enabled_apps 相关表与平台种子 |
|
||||
| 0003 | `scripts/db/verify/0003_app_lifecycle.sql` | 校验 apps 生命周期管理相关表与权限种子 |
|
||||
| 0004 | `scripts/db/verify/0004_password_reset.sql` | 校验密码重置权限码种子 |
|
||||
|
||||
## 执行方式
|
||||
|
||||
|
||||
64
scripts/db/migrations/0003_app_lifecycle.sql
Normal file
64
scripts/db/migrations/0003_app_lifecycle.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE apps
|
||||
ADD COLUMN IF NOT EXISTS app_type VARCHAR(50) NOT NULL DEFAULT 'generic',
|
||||
ADD COLUMN IF NOT EXISTS owner VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS owner_user_id UUID,
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW();
|
||||
|
||||
UPDATE apps
|
||||
SET updated_at = COALESCE(updated_at, created_at, NOW());
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_apps_status ON apps(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_apps_app_type ON apps(app_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_apps_created_at ON apps(created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS app_change_logs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
app_id VARCHAR(32) NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
actor_user_id UUID,
|
||||
before JSONB,
|
||||
after JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_app_change_logs_app_id ON app_change_logs(app_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_app_change_logs_created_at ON app_change_logs(created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS app_status_change_requests (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
app_id VARCHAR(32) NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
|
||||
from_status VARCHAR(20) NOT NULL,
|
||||
to_status VARCHAR(20) NOT NULL,
|
||||
requested_by UUID,
|
||||
requested_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
effective_at TIMESTAMP WITH TIME ZONE,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
approved_by UUID,
|
||||
approved_at TIMESTAMP WITH TIME ZONE,
|
||||
rejected_by UUID,
|
||||
rejected_at TIMESTAMP WITH TIME ZONE,
|
||||
reason TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_app_status_change_requests_status ON app_status_change_requests(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_app_status_change_requests_app_id ON app_status_change_requests(app_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_app_status_change_requests_effective_at ON app_status_change_requests(effective_at);
|
||||
|
||||
INSERT INTO permissions (code, description, resource, action) VALUES
|
||||
('iam:app:read', 'Read apps registry', 'app', 'read'),
|
||||
('iam:app:write', 'Manage apps registry', 'app', 'write'),
|
||||
('iam:app:approve', 'Approve app status change', 'app_status_change', 'approve'),
|
||||
('iam:app:delete', 'Delete apps registry', 'app', 'delete')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r, permissions p
|
||||
WHERE r.name = 'SuperAdmin'
|
||||
AND r.tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
AND p.code IN ('iam:app:read', 'iam:app:write', 'iam:app:approve', 'iam:app:delete')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
COMMIT;
|
||||
24
scripts/db/migrations/0004_password_reset.sql
Normal file
24
scripts/db/migrations/0004_password_reset.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO permissions (code, description, resource, action) VALUES
|
||||
('user:password:reset:any', 'Reset any user password in tenant', 'user_password', 'reset_any')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r, permissions p
|
||||
WHERE r.name = 'Admin'
|
||||
AND r.is_system = TRUE
|
||||
AND p.code = 'user:password:reset:any'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role_id, permission_id)
|
||||
SELECT r.id, p.id
|
||||
FROM roles r, permissions p
|
||||
WHERE r.name = 'SuperAdmin'
|
||||
AND r.tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
AND p.code = 'user:password:reset:any'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
COMMIT;
|
||||
|
||||
24
scripts/db/rollback/0003.down.sql
Normal file
24
scripts/db/rollback/0003.down.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
BEGIN;
|
||||
|
||||
DELETE FROM role_permissions rp
|
||||
USING roles r, permissions p
|
||||
WHERE rp.role_id = r.id
|
||||
AND rp.permission_id = p.id
|
||||
AND r.name = 'SuperAdmin'
|
||||
AND r.tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
AND p.code IN ('iam:app:read', 'iam:app:write', 'iam:app:approve', 'iam:app:delete');
|
||||
|
||||
DELETE FROM permissions
|
||||
WHERE code IN ('iam:app:read', 'iam:app:write', 'iam:app:approve', 'iam:app:delete');
|
||||
|
||||
DROP TABLE IF EXISTS app_status_change_requests;
|
||||
DROP TABLE IF EXISTS app_change_logs;
|
||||
|
||||
ALTER TABLE apps
|
||||
DROP COLUMN IF EXISTS app_type,
|
||||
DROP COLUMN IF EXISTS owner,
|
||||
DROP COLUMN IF EXISTS owner_user_id,
|
||||
DROP COLUMN IF EXISTS updated_at;
|
||||
|
||||
COMMIT;
|
||||
|
||||
17
scripts/db/rollback/0004.down.sql
Normal file
17
scripts/db/rollback/0004.down.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
BEGIN;
|
||||
|
||||
DELETE FROM role_permissions rp
|
||||
USING roles r, permissions p
|
||||
WHERE rp.role_id = r.id
|
||||
AND rp.permission_id = p.id
|
||||
AND p.code = 'user:password:reset:any'
|
||||
AND (
|
||||
(r.name = 'Admin' AND r.is_system = TRUE)
|
||||
OR (r.name = 'SuperAdmin' AND r.tenant_id = '00000000-0000-0000-0000-000000000001')
|
||||
);
|
||||
|
||||
DELETE FROM permissions
|
||||
WHERE code = 'user:password:reset:any';
|
||||
|
||||
COMMIT;
|
||||
|
||||
24
scripts/db/verify/0003_app_lifecycle.sql
Normal file
24
scripts/db/verify/0003_app_lifecycle.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('public.app_change_logs') IS NULL THEN
|
||||
RAISE EXCEPTION 'missing table: app_change_logs';
|
||||
END IF;
|
||||
IF to_regclass('public.app_status_change_requests') IS NULL THEN
|
||||
RAISE EXCEPTION 'missing table: app_status_change_requests';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'apps' AND column_name = 'app_type'
|
||||
) THEN
|
||||
RAISE EXCEPTION 'apps.app_type missing';
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM permissions WHERE code = 'iam:app:read') THEN
|
||||
RAISE EXCEPTION 'missing seed permission iam:app:read';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM permissions WHERE code = 'iam:app:approve') THEN
|
||||
RAISE EXCEPTION 'missing seed permission iam:app:approve';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
7
scripts/db/verify/0004_password_reset.sql
Normal file
7
scripts/db/verify/0004_password_reset.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM permissions WHERE code = 'user:password:reset:any') THEN
|
||||
RAISE EXCEPTION 'missing permission user:password:reset:any';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
Reference in New Issue
Block a user