Files
ocdp-go/backend/scripts/migrations/20250409_add_workspace_quotas.sql
Ivan087 29d0310f03 feat(frontend): add Helm chart browser, monitoring, chart-references and values templates pages
Add new frontend pages for the multi-tenant OCDP platform:

- Charts page (/charts): Browse Harbor OCI registries to list Helm chart repositories
  and versions, with deploy modal to launch charts on selected clusters
- Monitoring page (/monitoring): Display cluster metrics (CPU/Memory/GPU usage)
  and per-node details with resource utilization bars
- Chart References page (/chart-references): CRUD for chart metadata references
- Values Templates page (/templates): CRUD for Helm values templates with version
  history and rollback support
- Sidebar: Add Charts navigation, update Storage and Templates links
- api.ts: Add all API client functions (clusterApi, registryApi, instanceApi,
  monitoringApi, storageApi, chartReferenceApi, valuesTemplateApi,
  workspaceApi, userApi) with full TypeScript types

Note: deploy flow and values template rollback not yet end-to-end tested.
2026-04-15 16:59:31 +08:00

257 lines
13 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- OCDP 多租户权限系统迁移脚本
-- 版本: v2.0.0
-- 日期: 2026-04-09
-- ===== 1. 修改 users 表 =====
ALTER TABLE users ADD COLUMN IF NOT EXISTS role VARCHAR(20) NOT NULL DEFAULT 'user';
ALTER TABLE users ADD COLUMN IF NOT EXISTS workspace_id VARCHAR(36);
ALTER TABLE users ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT TRUE;
ALTER TABLE users ADD COLUMN IF NOT EXISTS must_change_password BOOLEAN DEFAULT FALSE;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_users_role ON users(role);
CREATE INDEX IF NOT EXISTS idx_users_workspace ON users(workspace_id);
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
COMMENT ON COLUMN users.role IS '用户角色: admin, user';
COMMENT ON COLUMN users.workspace_id IS '所属工作空间 ID';
COMMENT ON COLUMN users.is_active IS '账户是否激活';
COMMENT ON COLUMN users.must_change_password IS '首次登录必须修改密码';
-- ===== 2. 创建 workspaces 表 =====
CREATE TABLE IF NOT EXISTS workspaces (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
description TEXT,
created_by VARCHAR(36) REFERENCES users(id),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_workspaces_name ON workspaces(name);
CREATE INDEX IF NOT EXISTS idx_workspaces_created_by ON workspaces(created_by);
COMMENT ON TABLE workspaces IS '工作空间/租户表';
-- ===== 3. 创建 workspace_quotas 表 =====
CREATE TABLE IF NOT EXISTS workspace_quotas (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36) NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
resource_type VARCHAR(50) NOT NULL, -- 'cpu', 'gpu', 'gpu_memory'
hard_limit DECIMAL(10,2) NOT NULL DEFAULT 0, -- 硬限制0表示无限制
soft_limit DECIMAL(10,2) NOT NULL DEFAULT 0, -- 软限制(警告阈值)
used DECIMAL(10,2) DEFAULT 0, -- 当前使用量
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(workspace_id, resource_type)
);
CREATE INDEX IF NOT EXISTS idx_workspace_quotas_workspace ON workspace_quotas(workspace_id);
CREATE INDEX IF NOT EXISTS idx_workspace_quotas_type ON workspace_quotas(resource_type);
COMMENT ON TABLE workspace_quotas IS '工作空间资源配额表';
COMMENT ON COLUMN workspace_quotas.resource_type IS '资源类型: cpu, gpu, gpu_memory';
COMMENT ON COLUMN workspace_quotas.hard_limit IS '硬限制0表示无限制';
COMMENT ON COLUMN workspace_quotas.soft_limit IS '软限制(警告阈值)';
COMMENT ON COLUMN workspace_quotas.used IS '当前使用量';
-- ===== 4. 修改 clusters 表 =====
ALTER TABLE clusters ADD COLUMN IF NOT EXISTS workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE SET NULL;
ALTER TABLE clusters ADD COLUMN IF NOT EXISTS owner_id VARCHAR(36) REFERENCES users(id);
ALTER TABLE clusters ADD COLUMN IF NOT EXISTS isolation_mode VARCHAR(20) DEFAULT 'namespace';
ALTER TABLE clusters ADD COLUMN IF NOT EXISTS default_namespace VARCHAR(255);
ALTER TABLE clusters ADD COLUMN IF NOT EXISTS is_shared BOOLEAN DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_clusters_workspace ON clusters(workspace_id);
CREATE INDEX IF NOT EXISTS idx_clusters_owner ON clusters(owner_id);
CREATE INDEX IF NOT EXISTS idx_clusters_is_shared ON clusters(is_shared);
-- 删除旧的唯一约束添加新的允许同一workspace内名称唯一
ALTER TABLE clusters DROP CONSTRAINT IF EXISTS clusters_name_key;
COMMENT ON COLUMN clusters.workspace_id IS '所属工作空间 ID';
COMMENT ON COLUMN clusters.owner_id IS '创建者用户 ID';
COMMENT ON COLUMN clusters.isolation_mode IS '隔离模式: namespace(共享集群) 或 cluster(独立集群)';
COMMENT ON COLUMN clusters.default_namespace IS '默认命名空间前缀';
COMMENT ON COLUMN clusters.is_shared IS '是否为共享集群';
-- ===== 5. 修改 registries 表 =====
ALTER TABLE registries ADD COLUMN IF NOT EXISTS workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE SET NULL;
ALTER TABLE registries ADD COLUMN IF NOT EXISTS owner_id VARCHAR(36) REFERENCES users(id);
ALTER TABLE registries ADD COLUMN IF NOT EXISTS is_shared BOOLEAN DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_registries_workspace ON registries(workspace_id);
CREATE INDEX IF NOT EXISTS idx_registries_owner ON registries(owner_id);
CREATE INDEX IF NOT EXISTS idx_registries_is_shared ON registries(is_shared);
-- 删除旧的唯一约束
ALTER TABLE registries DROP CONSTRAINT IF EXISTS registries_name_key;
COMMENT ON COLUMN registries.workspace_id IS '所属工作空间 ID';
COMMENT ON COLUMN registries.owner_id IS '创建者用户 ID';
COMMENT ON COLUMN registries.is_shared IS '是否为共享注册表';
-- ===== 6. 创建 storage_backends 表 =====
CREATE TABLE IF NOT EXISTS storage_backends (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE CASCADE,
owner_id VARCHAR(36) REFERENCES users(id),
name VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL, -- 'nfs', 'pv', 'hostPath'
config JSONB NOT NULL, -- 存储配置
description TEXT,
is_default BOOLEAN DEFAULT FALSE,
is_shared BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(workspace_id, name)
);
CREATE INDEX IF NOT EXISTS idx_storage_backends_workspace ON storage_backends(workspace_id);
CREATE INDEX IF NOT EXISTS idx_storage_backends_owner ON storage_backends(owner_id);
CREATE INDEX IF NOT EXISTS idx_storage_backends_type ON storage_backends(type);
COMMENT ON TABLE storage_backends IS '存储后端表 (NFS/PV/HostPath)';
COMMENT ON COLUMN storage_backends.type IS '存储类型: nfs, pv, hostPath';
COMMENT ON COLUMN storage_backends.config IS '存储配置 (JSON)';
-- ===== 7. 创建 chart_references 表 =====
CREATE TABLE IF NOT EXISTS chart_references (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE CASCADE,
registry_id VARCHAR(36) REFERENCES registries(id) ON DELETE CASCADE,
repository VARCHAR(500) NOT NULL, -- OCI repository path
chart_name VARCHAR(255) NOT NULL,
description TEXT,
is_enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(workspace_id, registry_id, repository)
);
CREATE INDEX IF NOT EXISTS idx_chart_references_workspace ON chart_references(workspace_id);
CREATE INDEX IF NOT EXISTS idx_chart_references_registry ON chart_references(registry_id);
COMMENT ON TABLE chart_references IS 'Helm Chart 引用表';
-- ===== 8. 创建 values_templates 表 =====
CREATE TABLE IF NOT EXISTS values_templates (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE CASCADE,
owner_id VARCHAR(36) REFERENCES users(id),
chart_reference_id VARCHAR(36) REFERENCES chart_references(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
values_yaml TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(workspace_id, chart_reference_id, name, version)
);
CREATE INDEX IF NOT EXISTS idx_values_templates_workspace ON values_templates(workspace_id);
CREATE INDEX IF NOT EXISTS idx_values_templates_chart ON values_templates(chart_reference_id);
CREATE INDEX IF NOT EXISTS idx_values_templates_name ON values_templates(name);
COMMENT ON TABLE values_templates IS 'Values 模板表(带版本管理)';
COMMENT ON COLUMN values_templates.version IS '模板版本号';
-- ===== 9. 创建 user_config_overrides 表 =====
CREATE TABLE IF NOT EXISTS user_config_overrides (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE CASCADE,
user_id VARCHAR(36) REFERENCES users(id),
target_type VARCHAR(50) NOT NULL, -- 'storage', 'template', 'global'
target_id VARCHAR(36),
config JSONB NOT NULL, -- 覆盖配置
priority INTEGER DEFAULT 0, -- 优先级
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_user_config_overrides_workspace ON user_config_overrides(workspace_id);
CREATE INDEX IF NOT EXISTS idx_user_config_overrides_user ON user_config_overrides(user_id);
CREATE INDEX IF NOT EXISTS idx_user_config_overrides_target ON user_config_overrides(target_type, target_id);
COMMENT ON TABLE user_config_overrides IS '用户配置覆盖表';
COMMENT ON COLUMN user_config_overrides.target_type IS '目标类型: storage, template, global';
COMMENT ON COLUMN user_config_overrides.priority IS '优先级(越高越优先)';
-- ===== 10. 修改 instances 表 =====
ALTER TABLE instances ADD COLUMN IF NOT EXISTS workspace_id VARCHAR(36) REFERENCES workspaces(id) ON DELETE SET NULL;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS owner_id VARCHAR(36) REFERENCES users(id);
ALTER TABLE instances ADD COLUMN IF NOT EXISTS chart_reference_id VARCHAR(36) REFERENCES chart_references(id) ON DELETE SET NULL;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS values_template_id VARCHAR(36) REFERENCES values_templates(id) ON DELETE SET NULL;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS user_override_yaml TEXT;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS cpu_requested DECIMAL(10,2) DEFAULT 0;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS memory_requested VARCHAR(50) DEFAULT '0Mi';
ALTER TABLE instances ADD COLUMN IF NOT EXISTS gpu_requested DECIMAL(10,2) DEFAULT 0;
ALTER TABLE instances ADD COLUMN IF NOT EXISTS gpu_memory_requested VARCHAR(50) DEFAULT '0Mi';
CREATE INDEX IF NOT EXISTS idx_instances_workspace ON instances(workspace_id);
CREATE INDEX IF NOT EXISTS idx_instances_owner ON instances(owner_id);
COMMENT ON COLUMN instances.workspace_id IS '所属工作空间 ID';
COMMENT ON COLUMN instances.owner_id IS '创建者用户 ID';
COMMENT ON COLUMN instances.chart_reference_id IS 'Chart 引用 ID';
COMMENT ON COLUMN instances.values_template_id IS 'Values 模板 ID';
COMMENT ON COLUMN instances.user_override_yaml IS '用户覆盖配置';
COMMENT ON COLUMN instances.cpu_requested IS '请求的 CPU 核数';
COMMENT ON COLUMN instances.memory_requested IS '请求的内存';
COMMENT ON COLUMN instances.gpu_requested IS '请求的 GPU 卡数';
COMMENT ON COLUMN instances.gpu_memory_requested IS '请求的 GPU 内存';
-- ===== 11. 创建 audit_logs 表 =====
CREATE TABLE IF NOT EXISTS audit_logs (
id VARCHAR(36) PRIMARY KEY,
workspace_id VARCHAR(36),
user_id VARCHAR(36) REFERENCES users(id),
action VARCHAR(100) NOT NULL, -- 'create', 'update', 'delete', 'deploy', 'scale'
resource_type VARCHAR(50) NOT NULL, -- 'cluster', 'registry', 'instance', 'quota', 'user', 'workspace'
resource_id VARCHAR(36),
resource_name VARCHAR(255),
details JSONB,
ip_address VARCHAR(50),
user_agent TEXT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_audit_logs_workspace ON audit_logs(workspace_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource_type, resource_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit_logs(created_at);
COMMENT ON TABLE audit_logs IS '审计日志表';
COMMENT ON COLUMN audit_logs.action IS '操作类型: create, update, delete, deploy, scale';
COMMENT ON COLUMN audit_logs.resource_type IS '资源类型: cluster, registry, instance, quota, user, workspace';
-- ===== 12. 插入迁移版本 =====
INSERT INTO schema_migrations (version) VALUES ('v2.0.0')
ON CONFLICT (version) DO NOTHING;
-- ===== 13. 更新现有 admin 用户为 admin 角色 =====
UPDATE users SET role = 'admin', workspace_id = NULL WHERE username = 'admin' AND role = 'user';
-- ===== 14. 创建默认 workspace可选用于旧数据兼容=====
-- 如果需要将现有数据迁移到默认 workspace取消下面注释
-- INSERT INTO workspaces (id, name, description)
-- VALUES (gen_random_uuid(), 'default', '默认工作空间')
-- ON CONFLICT (name) DO NOTHING;
--
-- UPDATE clusters SET workspace_id = (SELECT id FROM workspaces WHERE name = 'default')
-- WHERE workspace_id IS NULL;
--
-- UPDATE registries SET workspace_id = (SELECT id FROM workspaces WHERE name = 'default')
-- WHERE workspace_id IS NULL;
--
-- UPDATE instances SET workspace_id = (SELECT id FROM workspaces WHERE name = 'default')
-- WHERE workspace_id IS NULL;
-- ===== 迁移完成 =====
DO $$
BEGIN
RAISE NOTICE 'Migration v2.0.0 completed successfully!';
END $$;