Files
story-edit-web/web/README.md
邓雨鹏 90402c4a17 security: 每人一把口令(口令即身份) + 随机会话token + 无配置拒绝启动 + 爆破节流
- STORY_WEB_PASSWORD(默认story) 废弃 → STORY_WEB_USERS=名字1:口令1,名字2:口令2;
  未配置/口令<8位/口令或用户名重复 → 启动即退出,杜绝弱默认口令裸奔
- cookie 不再存口令原文:登录发 secrets.token_urlsafe(32) 随机token,
  会话存 SQLite sessions 表(30天);登出删token;从 USERS 移除某人=吊销其全部会话
- updated_by 改由服务端按会话身份填写,前端自报 by 不再可信;登录框去掉昵称字段
- 登录失败全局递增节流(最多sleep 5s),口令比较用 secrets.compare_digest
- Dockerfile/compose 移除一切口令默认值;compose 未设 STORY_WEB_USERS 直接报错
- 顺手修 playtest.js 走位/动画/out_ref 行未转义的存储型XSS(esc补齐)
2026-06-10 17:34:50 +08:00

74 lines
3.9 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.

# Story 事件协作 Web 编辑器M5
设计:`docs/plans/2026-06-06-story-event-pipeline-design.md`§5.1/§6, D1D4/D8
计划:`docs/plans/2026-06-08-story-event-M5-web-editor-plan.md`
少数人各凭专属口令(口令即身份,改动自动署名)在网页里审校/编辑剧情事件 → 静态校验 + 剧本试走(零引擎)→ 一键编译
所有 confirmed 事件成 `.events.json` + `.i18n.tsv` 打包下载。校验/编译走 `ir_core`,与 CLI
`ir_compile.py`)逐字节同口径。
## 起服务
```bash
cd tools/event_authoring/web
pip install -r requirements.txt
# Windows PowerShell: $env:STORY_WEB_USERS="bia:口令A,ljl:口令B"
set STORY_WEB_USERS=bia:口令A,ljl:口令B # 必填;未配置则拒绝启动
uvicorn app:app --host 0.0.0.0 --port 8787
```
浏览器打开 `http://<host>:8787`,输入自己的专属口令进入(服务端按口令认人,
`updated_by` 自动署名;口令要求 ≥8 位且互不相同cookie 只存随机会话 token 不存口令;
`STORY_WEB_USERS` 移除某人即吊销其所有会话)。
## 用法
1. **导入 IR**:右上「导入 IR」粘贴单个或数组形式的 IR JSON`samples/*.ir.json`)。
2. **审校/编辑**:左栏选事件 → 中栏分支树 → 点节点在右栏改文案/增删节点/下拉改分支/角色表/点位下拉。
3. **校验**:与 CLI 同口径(断链、选项无兜底、未登记 kind、未声明角色、点位缺失、out_ref 失效…)。
4. **试走**:从首节点走,点选项/掷随机/手选战斗胜负,实时累计银两/道具/友好度账面与结局。
5. **确认/丢弃**改事件状态pending/confirmed/discarded
6. **导出 confirmed**:编译所有 confirmed → `story_export.zip`;任一 confirmed 校验不过则整体拒绝。
导出后把 `{group}.events.json` 放进 `Assets/StreamingAssets/Story/Config/`(或 `Qiyu/` 子目录),
`{group}.i18n.tsv` 的韩文翻译合并进 `Assets/StreamingAssets/i18n/ko.tsv`
## 数据
- 事件存 SQLite `story_events.db`(本目录,已 gitignore末次写入生效不做锁
- 词典 `../ir_dictionary.json`、点位集 `Assets/StreamingAssets/Story/PointSets/*.points.json` 只读引用。
## Docker 部署M6
单容器FastAPI + 静态前端 + SQLite + 纯 Python 编译器)。构建上下文是
`tools/event_authoring`(需含 `ir_core`/`ir_dictionary.json`/`web`)。
```bash
cd tools/event_authoring/web
STORY_WEB_USERS="bia:口令A,ljl:口令B" docker compose up -d --build
# 或不用 compose
# docker build -f web/Dockerfile -t story-event-web ..
# docker run -d -p 8787:8787 -e STORY_WEB_USERS="bia:口令A,ljl:口令B" \
# -v "$PWD/web/data:/data" \
# -v "$PWD/Assets/StreamingAssets/Story/PointSets:/pointsets:ro" story-event-web
```
- **卷**`./data:/data`SQLite 持久化,容器重建不丢事件,**勿删**
`…/PointSets:/pointsets:ro`(开发侧点位集只读;缺失时坐标校验降级为警告)。
- **环境变量**`STORY_WEB_USERS`(必填,`名字1:口令1,名字2:口令2`,未配置拒绝启动)、
`STORY_WEB_PORT`(宿主端口,默认 8787
`STORY_DB_PATH`(默认 `/data/story_events.db`)、`STORY_POINTSETS_DIR`(默认 `/pointsets`)、
可选 `TZ=Asia/Shanghai`(否则 `updated_at` 按 UTC 显示)。
- **NAS + VPS**NAS 跑容器VPS 用反代/frp/Cloudflare Tunnel 把 8787 映射出去。点位集更新只需
同步文件到 NAS 的 `/pointsets` 卷路径,**无需重建镜像**。备份=拷 `data/story_events.db`
## API鉴权后
`POST /api/login` · `GET /api/dictionary` · `GET /api/pointsets` · `POST /api/import` ·
`GET /api/events?status=` · `GET /api/events/{group}` · `PUT /api/events/{group}` ·
`POST /api/events/{group}/status` · `POST /api/validate` · `POST /api/export`
## 非目标(见设计)
容器化部署M6、多人账号/并发锁、手拖画布、引擎侧新语义、把坐标嵌进 events.json。