# order-site-backend

腾讯活动浏览器会话后端。

当前主线只有一条：

- 后端托管 Playwright 浏览器
- 前端展示二维码并轮询会话状态
- 登录完成后，后端直接在活动页填写 CDK、识别验证码、执行兑换
- 兑换完成后返回状态，并按配置决定是否生成截图/证明产物

## 快速启动

先安装 Node 依赖：

```bash
npm install
```

复制并填写环境变量后启动：

```bash
cp /Users/yml/codes/order-site-workspace/.env.mac-docker.example /Users/yml/codes/order-site-workspace/.env
cd /Users/yml/codes/order-site-workspace/apps/backend
npm run dev
```

Linux 部署：

```bash
npm install
npm run build
npm run browser:install:linux
npm run start
```

后端渐进 TypeScript 迁移已经进入源码和构建双轨模式：

```bash
npm run typecheck
npm run build
npm test
```

当前约定：

- 开发：`npm run dev` 使用 `tsx watch` 直接运行 `src`，并额外监听 `config`
- 数据库迁移：`npm run db:migrate` 使用 `tsx`
- 生产：`npm run build` 输出 `dist`，`npm run start` 运行 `dist/index.js`
- 新增或迁移后端模块优先使用 `.ts`

## 配置

当前后端采用 `.env` 单一路径做环境配置：

- 通用默认值：`config/default.cjs`
- 本地/部署覆盖：工作区根目录 `.env`

当前最常改的配置有：

- 服务端口
- 日志级别 `LOG_LEVEL=debug|info|warn|error`
- 浏览器是否无头、是否预热、是否常驻、slowMo
- OCR 外部服务镜像 `OCR_IMAGE` 与访问地址 `OCR_BASE_URL`
- 会话调试开关
- 兑换证明模式 `full | basic | off`
- Agiso 全局配置与后台店铺文件配置

### Agiso 多店铺

当前支持同一个 Agiso 应用下配置多个店铺的消息 token。

- 全局兜底配置仍走 `.env`：
  - `AGISO_APP_SECRET`
  - `AGISO_MESSAGING_ENABLED`
- 默认消息模板与店铺级覆盖配置改为文件：
  - `apps/backend/data/agiso-shops.json`
- 后台维护入口：
  - `#/admin/platform-shops`

消息发送时会按 webhook 识别出的 `shop_id` 读取 `agiso-shops.json` 中的店铺配置；模板未命中时，会先回退到文件里的 `defaults`，再回退到系统内置默认值。店铺的 `accessToken` 也需要在文件中维护，不再使用全局 `.env` 兜底。

## 接口

- `POST /api/v1/tencent/browser/session`
  创建浏览器会话，返回 `sessionId`、二维码和初始状态
- `GET /api/v1/tencent/browser/session/:sessionId`
  返回完整会话状态
- `GET /api/v1/tencent/browser/session/:sessionId/summary`
  轮询用轻量摘要接口，默认不返回二维码 base64
- `POST /api/v1/tencent/browser/session/:sessionId/refresh`
  强制刷新后端活动页
- `POST /api/v1/tencent/browser/session/:sessionId/redeem`
  执行兑换
- `GET /api/v1/tencent/browser/session/:sessionId/screenshot`
  读取最近一次兑换截图
- `DELETE /api/v1/tencent/browser/session/:sessionId`
  关闭浏览器会话

## 目录

- `src/index.ts`
  Express 入口
- `src/routes/tencent.ts`
  接口路由
- `src/services/session/session.ts`
  会话编排与浏览器生命周期
- `src/services/session/*.ts`
  登录、兑换、OCR、凭证产物等模块

## Docker 开发

当前 `docker-compose.dev.yml` 已切到源码挂载模式：

- `postgres` 容器提供开发库，后端通过 `DATABASE_URL` 连接
- `ocr-worker` 使用外部镜像启动，后端通过 `OCR_BASE_URL=http://ocr-worker:8100` 调用
- 后端容器启动时执行 `npm install` 和 `npm run dev`
- 平时改 `src/` 或 `.env`，一般都不需要重建镜像
- 生产镜像会在 build 阶段编译后端并只复制 `dist`、`config` 和运行依赖

只有下面几类改动通常还需要 `docker compose -f docker-compose.dev.yml up -d --build backend`：

- `deploy/docker/backend.Dockerfile`
- 系统层依赖
- Playwright 浏览器基础环境

## 产物

浏览器会话产物默认保存在：

- `data/browser-sessions/<sessionId>/qq-qr.png`
- `data/browser-sessions/<sessionId>/session.json`
- `data/browser-sessions/<sessionId>/captcha-attempt-*.png`
- `data/browser-sessions/<sessionId>/redeem-result.png`
- `data/browser-sessions/<sessionId>/page.html`
- `data/browser-sessions/<sessionId>/result.json`

这些都是运行时产物，默认不提交 Git。

运行日志默认保存在：

- `data/logs/app-YYYY-MM-DD.log`
- `data/logs/webhook-YYYY-MM-DD.log`

日志按天切分，默认自动清理 7 天前的旧日志。
成功的 `/health`、`/health/live`、`/health/ready` 探活请求默认不写 access log。

`LOG_LEVEL` 默认是 `info`：

- `debug`：输出最详细的调试日志
- `info`：输出启动、请求、业务成功/失败等常规日志
- `warn`：只保留告警和错误
- `error`：只保留错误

OCR 识别通过 `OCR_BASE_URL` 调用 HTTP 服务。Docker Compose 会启动 `ocr-worker` 外部镜像并注入默认地址 `http://ocr-worker:8100`；如果单独运行后端且未配置该地址，启动健康检查会标记 OCR 为 degraded，兑换中需要 OCR 的流程会在调用时返回明确错误。

`TENCENT_REDEEM_PROOF_MODE` 对应的配置文件项会影响生成强度：

- `full`：完整证明
- `basic`：只保留最终截图和结果 JSON
- `off`：不生成证明文件
