chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
216
platform/internal/meta/migrate.go
Normal file
216
platform/internal/meta/migrate.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package meta
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const metaSchemaSQL = `
|
||||
CREATE SCHEMA IF NOT EXISTS platform_meta;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.tenants (
|
||||
tenant_id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.users (
|
||||
user_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT REFERENCES platform_meta.tenants(tenant_id),
|
||||
username VARCHAR(64) NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
display_name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT uq_users_username UNIQUE (username)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_tenant ON platform_meta.users (tenant_id);
|
||||
|
||||
-- 兼容旧库:允许无租户(pending)
|
||||
ALTER TABLE platform_meta.users ALTER COLUMN tenant_id DROP NOT NULL;
|
||||
ALTER TABLE platform_meta.users ADD COLUMN IF NOT EXISTS status VARCHAR(32) NOT NULL DEFAULT 'active';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.tenant_apps (
|
||||
app_id UUID PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
schema_name VARCHAR(64) NOT NULL,
|
||||
engine VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
blueprint_json JSONB NOT NULL,
|
||||
ddl_json JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
endpoints_json JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
error_msg TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT uq_tenant_apps_tenant_slug UNIQUE (tenant_id, slug)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tenant_apps_tenant_status
|
||||
ON platform_meta.tenant_apps (tenant_id, status);
|
||||
|
||||
ALTER TABLE platform_meta.tenant_apps
|
||||
ADD COLUMN IF NOT EXISTS database_name VARCHAR(64) NOT NULL DEFAULT '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.audit_logs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
detail TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 智能体服务账号(机器身份,供宿主自动登录)
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.agent_accounts (
|
||||
agent_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES platform_meta.tenants(tenant_id),
|
||||
name VARCHAR(128) NOT NULL,
|
||||
client_id VARCHAR(64) NOT NULL,
|
||||
client_secret_hash TEXT NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
created_by BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
last_token_at TIMESTAMPTZ,
|
||||
CONSTRAINT uq_agent_client_id UNIQUE (client_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_accounts_tenant
|
||||
ON platform_meta.agent_accounts (tenant_id);
|
||||
|
||||
ALTER TABLE platform_meta.agent_accounts
|
||||
ADD COLUMN IF NOT EXISTS host_key VARCHAR(128) NOT NULL DEFAULT '';
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_agent_tenant_host_key
|
||||
ON platform_meta.agent_accounts (tenant_id, host_key)
|
||||
WHERE host_key <> '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.agent_permissions (
|
||||
agent_id BIGINT NOT NULL REFERENCES platform_meta.agent_accounts(agent_id) ON DELETE CASCADE,
|
||||
perm VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (agent_id, perm)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.agent_app_grants (
|
||||
agent_id BIGINT NOT NULL REFERENCES platform_meta.agent_accounts(agent_id) ON DELETE CASCADE,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (agent_id, slug)
|
||||
);
|
||||
|
||||
-- 可编辑角色(租户级)
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.roles (
|
||||
role_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES platform_meta.tenants(tenant_id),
|
||||
code VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT uq_roles_tenant_code UNIQUE (tenant_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_roles_tenant ON platform_meta.roles (tenant_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.role_permissions (
|
||||
role_id BIGINT NOT NULL REFERENCES platform_meta.roles(role_id) ON DELETE CASCADE,
|
||||
perm VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (role_id, perm)
|
||||
);
|
||||
|
||||
ALTER TABLE platform_meta.agent_accounts
|
||||
ADD COLUMN IF NOT EXISTS role_id BIGINT;
|
||||
|
||||
-- 租户邀请码:pending 用户凭码加入已有公司
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.tenant_invites (
|
||||
invite_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES platform_meta.tenants(tenant_id) ON DELETE CASCADE,
|
||||
code VARCHAR(64) NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'editor',
|
||||
created_by BIGINT NOT NULL DEFAULT 0,
|
||||
max_uses INT NOT NULL DEFAULT 1,
|
||||
used_count INT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
expires_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT uq_tenant_invites_code UNIQUE (code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tenant_invites_tenant
|
||||
ON platform_meta.tenant_invites (tenant_id);
|
||||
|
||||
-- 租户内多级组织(默认最多 5 级)
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.org_units (
|
||||
org_unit_id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL REFERENCES platform_meta.tenants(tenant_id) ON DELETE CASCADE,
|
||||
parent_id BIGINT REFERENCES platform_meta.org_units(org_unit_id) ON DELETE CASCADE,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
code VARCHAR(64) NOT NULL DEFAULT '',
|
||||
depth INT NOT NULL DEFAULT 1,
|
||||
path VARCHAR(512) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_org_units_tenant_parent
|
||||
ON platform_meta.org_units (tenant_id, parent_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_org_units_tenant_path
|
||||
ON platform_meta.org_units (tenant_id, path);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_org_units_tenant_code
|
||||
ON platform_meta.org_units (tenant_id, code)
|
||||
WHERE code <> '';
|
||||
|
||||
ALTER TABLE platform_meta.users
|
||||
ADD COLUMN IF NOT EXISTS org_unit_id BIGINT;
|
||||
|
||||
ALTER TABLE platform_meta.tenant_invites
|
||||
ADD COLUMN IF NOT EXISTS org_unit_id BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE platform_meta.users
|
||||
ALTER COLUMN role TYPE VARCHAR(64);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.tenant_permissions (
|
||||
tenant_id BIGINT NOT NULL REFERENCES platform_meta.tenants(tenant_id) ON DELETE CASCADE,
|
||||
perm VARCHAR(64) NOT NULL,
|
||||
PRIMARY KEY (tenant_id, perm)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.tenant_entitlement_state (
|
||||
tenant_id BIGINT PRIMARY KEY REFERENCES platform_meta.tenants(tenant_id) ON DELETE CASCADE,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 公司路径 slug(www.example.com/{slug}/);旧库补列
|
||||
ALTER TABLE platform_meta.tenants ADD COLUMN IF NOT EXISTS slug VARCHAR(64) NOT NULL DEFAULT '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_tenants_slug ON platform_meta.tenants (slug) WHERE slug <> '';
|
||||
|
||||
-- 手机号(可绑定;非空时全局唯一,可用于登录)
|
||||
ALTER TABLE platform_meta.users ADD COLUMN IF NOT EXISTS phone VARCHAR(20) NOT NULL DEFAULT '';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_users_phone ON platform_meta.users (phone) WHERE phone <> '';
|
||||
|
||||
-- 已绑手机时可禁用用户名登录(仅手机号+密码/短信)
|
||||
ALTER TABLE platform_meta.users ADD COLUMN IF NOT EXISTS username_login_disabled BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- 授权租约已消费 id(删 leases 目录后仍能拦截旧延期包)
|
||||
CREATE TABLE IF NOT EXISTS platform_meta.license_consumed (
|
||||
lease_id VARCHAR(64) PRIMARY KEY,
|
||||
active BOOLEAN NOT NULL DEFAULT false,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_license_consumed_active ON platform_meta.license_consumed (active) WHERE active;
|
||||
`
|
||||
|
||||
// EnsureSchema 创建平台元数据表(幂等)。
|
||||
func EnsureSchema(ctx context.Context, db *sql.DB) error {
|
||||
if db == nil {
|
||||
return fmt.Errorf("db is nil")
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, metaSchemaSQL); err != nil {
|
||||
return fmt.Errorf("meta migrate: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user