线上只托管静态文件、不暴露玩具级用户 API;HTTPS 域名下大厅隐藏登录。部署步骤与 Nginx 配置记在 docs/deploy.md,便于以后 rsync 更新。 Co-authored-by: Cursor <cursoragent@cursor.com>
3.2 KiB
3.2 KiB
SoundGame Web 服务器(web/server/server.js)
Node 零依赖(仅用内置模块)的静态文件 + 用户系统 API 服务器。
启动
# 推荐:用项目自带 node
tools/node/bin/node web/server/server.js
# 或用封装脚本(打印局域网地址)
bash web/serve.sh
# 自定义端口(默认 8000)
PORT=8001 tools/node/bin/node web/server/server.js
- 静态根目录:
web/(/即游戏大厅index.html) - 数据目录:
web/server/data/(首次启动自动创建)
⚠ 安全说明
这是家庭局域网玩具级认证,请勿暴露到公网:
- PIN 以明文存储在
data/users.json; - token 永不过期,明文存于
data/sessions.json; - HTTP 明文传输,无 TLS、无防爆破、无速率限制。
用途仅限于同一 WiFi 下的家庭成员各自存档。公网 game.xiangsilian.com 不跑本服务,
只托管静态文件(见 docs/deploy.md)。要公网开用户系统请换正经认证方案。
API
所有 API 请求/响应均为 JSON(Content-Type: application/json)。
错误响应统一为 { "error": "描述" } + 相应 HTTP 状态码。
POST /api/register
注册新用户。
- 请求:
{ "username": "ming", "pin": "1234" }username:1~24 位字母/数字/中文/_/-pin:4~6 位数字
- 成功:
201 { "ok": true, "username": "ming" } - 失败:
400(格式非法)/409 { "error": "用户名已被注册" }
POST /api/login
登录,返回 token。
- 请求:
{ "username": "ming", "pin": "1234" } - 成功:
200 { "ok": true, "token": "<48 位随机 hex>", "username": "ming" } - 失败:
401 { "error": "用户名或 PIN 错误" }
token 存内存并落盘 data/sessions.json(服务器重启后会话仍有效)。
GET /api/progress?game=xxx
读取某游戏的进度数据。
- 请求头:
Authorization: Bearer <token> - 成功:
200 { "game": "escape", "data": { ... } }(无存档时data为null) - 失败:
401(token 缺失/无效)/400(缺少 game 参数)
PUT /api/progress
写入某游戏的进度数据(覆盖该 game 键)。
- 请求头:
Authorization: Bearer <token> - 请求:
{ "game": "escape", "data": { "summary": "已逃脱 2 次 / 最快 3:25", ... } }game:1~32 位字母/数字/_/-;大厅约定用_settings存用户设置data:任意 JSON 值;大厅卡片会展示data.summary字符串(若存在)
- 成功:
200 { "ok": true, "game": "escape" } - 失败:
401/400
数据格式
web/server/data/
├── users.json { "<username>": "<pin>" }
├── sessions.json { "<token>": "<username>" }
└── <username>.json { "<game>": <data>, ... } 一用户一文件
所有写入按文件串行排队(Promise 链),tmp + rename 原子落盘,并发请求不会写坏文件。
静态服务
- MIME:
.ogg audio/ogg、.m4a audio/mp4、.js text/javascript、.html text/html、.png image/png、.json application/json等; - 目录自动解析
index.html; - 支持简化版 HTTP Range(单段
bytes=start-end/start-/-suffix), 返回 206 +Content-Range,供音频元素拖动进度; - 路径穿越防护:请求路径限制在
web/内。