chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:31:17 +08:00
commit 4ca82fb58a
203 changed files with 45745 additions and 0 deletions

View 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()
);
-- 公司路径 slugwww.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
}