Files
popiart-server/docs/persistence.md
T

179 lines
4.4 KiB
Markdown
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.
# popiartServer 薄膜持久化设计
这份文档描述当前的轻量化持久化方案。目标不是把 `popiartServer`
做成第二个模型平台,而是让它只保存 PopiArt 自己的产品语义。
## 设计原则
`PopiNewAPI` 已经负责:
- 上游 provider key
- 模型请求
- 原始用量
- 一部分任务型能力的 task 状态
所以 `popiartServer` 不再重复持有:
- 本地 artifact metadata table
- 本地 job_logs table
它只保留三类本地状态:
- `sessions`
- `jobs`(更准确说是 job refs
- `skill_routes`
- `media blobs + metadata`
## 当前关系
```text
session -> jobs
jobs -> result_refs_json
project -> skill_routes
media -> local files + metadata json
```
其中:
- `session` 保存 PopiArt 登录态和对应的 `PopiNewAPI token`
- `jobs` 保存 skill 语义、用户归属、上游引用和同步结果引用
- `result_refs_json` 保存同步能力的结果引用;新结果会优先 re-host 到本地 media store
- `skill_routes` 保存项目级路由覆盖
- `media` 保存稳定 URL 所需的本地文件与元数据
## 存储位置
默认配置来自这些环境变量:
- `POPIART_DATA_DIR`
- `POPIART_SQLITE_PATH`
- `POPIART_SESSION_SECRET`
默认值:
```text
POPIART_DATA_DIR=./data
POPIART_SQLITE_PATH=./data/popiart.db
```
## Repository 接口
代码位置:
- `internal/server/repository.go`
当前只保留三层 repository
1. `SessionRepository`
2. `JobRepository`
3. `RouteRepository`
本地开发版没有对象存储依赖,但现在有一个轻量 media 存储层:
- blob 文件保存在 `POPIART_DATA_DIR/media/blobs/`
- metadata JSON 保存在 `POPIART_DATA_DIR/media/meta/`
## SQLite Schema
### sessions
```sql
CREATE TABLE sessions (
session_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
user_json TEXT NOT NULL,
token_enc BLOB NOT NULL,
token_masked TEXT NOT NULL,
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
```
### jobs
```sql
CREATE TABLE jobs (
job_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
session_id TEXT,
project_id TEXT,
skill_id TEXT,
route_key TEXT,
model_id TEXT,
exec_mode TEXT NOT NULL,
newapi_task_id TEXT,
status TEXT NOT NULL,
input_json TEXT NOT NULL,
result_refs_json TEXT,
usage_json TEXT,
error_json TEXT,
idempotency_key TEXT,
created_at TEXT NOT NULL,
started_at TEXT,
finished_at TEXT,
UNIQUE(user_id, idempotency_key)
);
```
### skill_routes
```sql
CREATE TABLE skill_routes (
route_key TEXT NOT NULL,
scope_key TEXT NOT NULL,
model_id TEXT NOT NULL,
options_json TEXT,
updated_at TEXT NOT NULL,
PRIMARY KEY(route_key, scope_key)
);
```
## 运行时流程
### 登录
1. CLI 把 `PopiNewAPI token` 交给 `popiartServer`
2. `popiartServer``/v1/models` 验证 token
3. server 签发本地 `sess_xxx`
4. 本地 session 持久化到 SQLite,重启后仍可继续使用
### 创建 job
1. server 解析 `skill_id -> route_key -> model_id`
2. `jobs` 表写入一条本地 job ref
3. goroutine 开始向 `PopiNewAPI` 发请求
### 完成同步结果 job
1. 如果 `PopiNewAPI` 直接返回同步结果,例如 `b64_json``url`
2. `popiartServer` 会先把结果 re-host 到本地 media store
3. 再把本地 `local_path + media_id + stable url` 写进 `result_refs_json`
4. `GET /jobs/:id/artifacts` 时再从 `result_refs_json` 派生 artifact 列表
### 拉取 artifact
1. server 从 `artifact_id` 反推出 `job_id + result index`
2. 读取 `jobs.result_refs_json`
3. 如果是本地 `local_path`,直接读取本地文件并流式返回
4. 如果是旧的 `data_url`,直接解码并流式返回
5. 如果是旧的远端 `url`server 代理下载并返回
### 读取 media
1. `POST /v1/media/upload` 会把本地文件写入 `media/blobs/`
2. server 同时写一份 metadata JSON 到 `media/meta/`
3. `GET /v1/media/:id` 返回 media 元数据
4. `GET /v1/media/:id/content` 返回可供模型或客户端直接 fetch 的稳定内容路径
## 当前边界
- server 重启后:
- `session` 会保留
- `job ref` 会保留
- `artifact` 通过 `result_refs_json` 继续可读
-`media` 文件和 metadata 会继续可读
- 已存在的 `pending/running` job 仍然不会自动恢复执行
- 视频类 `upstream_task` 路径只预留了字段,后续再接 `PopiNewAPI` task 查询
- 旧数据里仍可能存在 `data_url` 或上游 `url`
- 新写入路径优先落本地 media store,从而给 artifact 补出稳定 `url`