# 空数据库到租户建站验收

本页记录 Development 环境从全新 PostgreSQL 数据库完成平台管理员、租户、Owner 激活、网站发布和学生端访问的真实流程。数据库迁移只由 `Tiku.DbMigrator` 执行，API 不会自动更新 Schema。

## 1. 准备环境

先完成[本地开发快速上手](quickstart.md)中的依赖安装。本流程额外需要 Redis、`psql`、`createdb`、`dropdb` 和租户前端仓库。

```bash
pg_isready -h 127.0.0.1 -p 5432
redis-cli -h 127.0.0.1 -p 6379 ping
npm --prefix /path/to/tiku-saas-web install
```

## 2. 创建空数据库

以下操作只针对本地数据库 `tiku`。如果它已经包含需要保留的数据，请先备份，不要执行清理命令。

```bash
dropdb --if-exists -h 127.0.0.1 -U <数据库用户> tiku
createdb -h 127.0.0.1 -U <数据库用户> tiku
```

Development 未配置连接串时默认使用当前系统用户连接本机 `tiku`。其他用户或端口应显式设置：

```bash
export ConnectionStrings__Database='Host=127.0.0.1;Port=5432;Database=tiku;Username=<数据库用户>;Password=<本地密码>'
```

不要把连接串、密码或 Token 写入 `appsettings*.json`、README 或 Git。

## 3. 初始化目录、starter 套餐和首个平台账号

先设置一次性 Bootstrap 参数，再执行生产式空库初始化：

```bash
export ASPNETCORE_ENVIRONMENT=Development
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL='<平台管理员邮箱>'
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD='<临时密码>'
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME='<显示名称>'

dotnet run --project Tiku.DbMigrator -- \
  --skip-development-seed \
  --bootstrap-platform-admin
```

该命令按固定顺序执行：

1. EF Core Migration；
2. Feature、Permission、菜单和额度目录；
3. 内置 `starter` 套餐及其已发布版本；
4. 可选的平台超级管理员 Bootstrap。

`starter` 是零元、CNY、已发布的建站基础套餐，包含 `core.backoffice` 和 `marketing.site_content`。首个平台账号使用临时密码，首次登录必须改密。

清除 Bootstrap 密码并再运行一次 Migrator，确认日常重复执行不会创建演示租户或重复目录：

```bash
unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD
unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL
unset TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME

dotnet run --project Tiku.DbMigrator -- --skip-development-seed
```

Migration 需要 `citext`、`ltree` 和 `pg_trgm` 扩展。执行用户必须可以创建这些扩展，或由数据库管理员预先安装。

## 4. 启动真实服务

确保 Redis 已运行，然后从后端仓库启动 API：

```bash
export ASPNETCORE_ENVIRONMENT=Development
export ConnectionStrings__Redis='localhost:6379,abortConnect=false'
dotnet run --project Tiku.Api --launch-profile http
```

API 在 Development 会同时拉起平台管理端 Vite 服务：

- 平台管理端：<http://localhost:5173>
- API：<http://localhost:5090>
- Scalar：<http://localhost:5090/scalar/v1>
- OpenAPI：<http://localhost:5090/openapi/v1.json>
- Readiness：<http://localhost:5090/api/system/health/ready>

不要使用 `http://localhost:5090/platform-admin/` 作为开发入口；该路径受 API 授权保护，未登录访问返回 401。

在 `tiku-saas-web` 仓库另开终端，使用真实 API 模式启动租户前端：

```bash
VITE_DATA_MODE=api \
VITE_DEV_API_TARGET='http://localhost:5090' \
npm run dev -- --host 0.0.0.0 --port 5180
```

浏览器始终请求同源 `/api`，Vite 只在服务端把它代理到 `VITE_DEV_API_TARGET`。代理保留原始 Host，因此 `school.localhost` 能由后端解析到正确租户。

## 5. 浏览器建站流程

### 5.1 平台首次登录

1. 打开 <http://localhost:5173>；
2. 使用 Bootstrap 邮箱和临时密码登录；
3. 按页面要求设置新密码；
4. 进入“租户管理”。

### 5.2 创建租户和本地域名

点击“新建租户”，至少填写：

- 租户短编码，例如 `school`；
- 租户名称；
- 主域名 `school.localhost`；
- Owner 姓名；
- Owner 邮箱或手机号。

不选择套餐时，后端自动使用 `starter` 和默认试用天数。Development 配置只对精确的 `localhost` 或 `*.localhost` 启用 DNS/TLS 旁路，通常数秒内显示“DNS 与 TLS 已激活”。其他域名仍走真实 DNS 和网关流程。

### 5.3 签发并消费 Owner 链接

1. 域名 Active 后点击“领取激活链接”；
2. 填写审计原因并确认；
3. 立即保存弹窗中的一次性链接；
4. 用完整链接打开 `http://school.localhost:5180/activate/...#token=...`；
5. Owner 设置密码后自动进入 `/manage/onboarding`。

Token 只在签发成功弹窗显示一次。租户前端读入 Fragment 后立即清除地址栏；后端消费后不能重放。平台管理员看不到 Owner 密码，也不需要审批 Owner 激活。

### 5.4 配置并发布

向导依次完成：品牌信息、模板、主题样式、页面模块、桌面/移动预览、发布上线。保存草稿不会影响学生端；发布成功后 Runtime 读取已发布配置。

打开或刷新 <http://school.localhost:5180/>，应看到新的品牌、导航、主题和首页模块。退出租户后台后，可在 <http://school.localhost:5180/manage/login> 使用 Owner 账号重新登录。

## 6. 预期状态

| 阶段 | 预期状态 |
| --- | --- |
| 刚创建域名 | `pending` |
| Development 后台任务处理完成 | 域名 `active` |
| Owner 尚未领取链接 | `ready_to_issue` |
| 链接签发 | `issued` |
| Owner 激活并登录 | 进入 `/manage/onboarding` |
| 草稿完成但未发布 | `ready_to_launch` |
| 发布完成 | 学生端显示已发布配置 |

## 7. 常见问题

### `.localhost` 一直 Pending

确认 API 使用 `ASPNETCORE_ENVIRONMENT=Development`，并加载：

```json
{
  "TenantDomains": {
    "PollSeconds": 2,
    "EnableDevelopmentLocalhostBypass": true
  }
}
```

API 日志应出现 `Processed ... pending Development tenant domains`。非 Development 环境启用该旁路会在启动时失败。

### 激活页只出现 OPTIONS，没有 POST

不要把 API 地址配置为浏览器请求 Base URL。租户前端真实模式必须使用同源 `/api`，开发代理目标使用：

```bash
VITE_DEV_API_TARGET=http://localhost:5090
```

确保旧的 `VITE_API_BASE_URL` 未注入进程，并重启 Vite。

### 激活失败后地址栏已没有 Token

如果后端尚未消费 Token，可重新打开平台最初交付的完整链接。已经消费、过期或丢失明文时，必须由平台撤销并重新签发，不能从数据库或日志恢复。

### 发布后旧标签仍显示筹备页

草稿与已发布配置相互隔离。确认向导显示发布成功后，刷新学生端标签，让它重新请求 Runtime Bootstrap。

### Cookie 没有建立

必须通过 `school.localhost:5180` 访问激活和后台，不能改用 `localhost:5180` 或把 `tenantId` 填进请求。浏览器 Session 使用 Secure/HttpOnly Cookie，写请求使用 CSRF 双提交 Token，前端不得降级保存 JWT。

### Readiness 返回 503

确认 PostgreSQL 和 Redis 均可访问。匿名 readiness 只返回总体状态；依赖详情需要具有 `platform:operations:view` 权限的平台账号。

## 8. 清理

先停止 API、平台 Vite 和租户 Vite，再只删除明确的本地数据库：

```bash
dropdb -h 127.0.0.1 -U <数据库用户> tiku
```

该操作不可恢复，会删除本轮创建的平台账号、租户、Session、草稿和发布配置。
