# YMhut Box 反馈工单服务

这是 `server/feedback-mailer` 下的 Go + Gin + SQLite 反馈服务。旧 PHP 入口已移除，线上入口由 Go 服务接管；`config.txt`、`storage/`、`storage/feedback.sqlite` 和历史 `.ymfb` / `.zip` 文件继续保留并兼容读取。

## 运行

在 `server/feedback-mailer` 目录直接运行：

```bash
go run main.go
```

监听地址以核心配置文件 `config.txt` 的 `listen` 为准：

```php
'listen' => ':8080',
```

服务器部署时也可以显式设置服务根目录：

```bash
export YMHUT_FEEDBACK_HOME=/opt/feedback-mailer
./feedback-mailer
```

后台入口：

```text
http://127.0.0.1:8080/admin/
```

生产构建与检查：

```bash
npm --prefix admin-web install
npm --prefix admin-web run build
go test ./...
go build .
```

## 配置

优先读取当前目录的 `config.txt`，这是线上实际配置文件，部署时不要覆盖。没有 `config.txt` 时才回退读取 `config.json`。

关键字段：

- `listen`：服务监听地址，例如 `:8080`、`:9090`、`127.0.0.1:8080`。
- `admin_password_hash` / `admin_password`：后台登录密码，推荐使用 bcrypt hash。
- `client_signature_key`：旧 WinUI 客户端 HMAC 签名密钥，必须保持一致。
- `package_encryption_key`：`.ymfb` 反馈包解密密钥，必须保持一致。
- `storage_dir`：默认 `storage`，保留历史反馈包。
- `database_path`：默认 `storage/feedback.sqlite`，保留旧 SQLite 数据。
- `rate_limit`：提交、状态查询、验证码、登录和后台 API 的令牌桶限流。
- `upload_guard`：zip 文件数量、解压后大小、单文件大小、压缩比、路径穿越校验。
- `backup.dir` / `backup_dir`：数据库只读备份输出目录，默认 `storage/backups`。
- `webhooks`：通用 Webhook 通知配置，secret 只用于签名，不进入 URL 或配置健康 API。

## 兼容接口

旧客户端提交接口保持不变：

```text
POST /
```

继续接收 `payload`、`timestamp`、`nonce`、`packageSha256`、`signature`、`package`，并保留旧错误码。

旧状态查询保持不变：

```text
GET /?api=status&code=FB-YYYYMMDD-XXXXXX
```

响应保留旧字段，并兼容增加 `statusDetail`、`category`、`priority`。

## 后台能力

- 公共首页 `/`：反馈中心正式页和无需登录的状态查询。
- 后台 `/admin/`：工作台、工单流、状态看板、通知记录、Webhook 集成、运维、配置健康。
- 10 秒轮询刷新：统计卡片、SVG 图表、工单列表、详情、通知和配置摘要都会重绘。
- 工单增强：状态、分类、优先级、SLA、指派人、截止时间、标签、评论、解决说明、内部备注、公开回复。
- 看板操作：按 `new / triaged / investigating / resolved / archived` 分栏，拖动工单即可改状态。
- 批量处理：多选后批量归类、处理中、解决、归档。
- 导出：`GET /api/admin/feedbacks/export?format=csv` 导出当前筛选结果，不包含包文件和密钥。
- 审计日志：登录、工单修改、批量操作、导出、备份、Webhook 测试等都会记录。
- 数据库备份：后台创建和下载 SQLite 备份，不提供在线删除、VACUUM 或破坏性维护。
- Webhook：支持 `feedback.created`、`feedback.updated`、`feedback.status_changed`、`feedback.comment_created`、`mail.failed`。

Webhook 请求头：

```text
X-YMhut-Event
X-YMhut-Delivery
X-YMhut-Signature
```

签名格式为 `sha256=<hex hmac>`。

## 数据升级

服务启动时会对旧 `feedbacks` 表做非破坏性补列，并创建新表：

- `feedback_comments`
- `feedback_tags`
- `audit_logs`
- `webhook_deliveries`
- `feedback_events`

SQLite 默认启用 WAL，并设置 `busy_timeout`。不会删除 `storage/feedback.sqlite`，不会修改历史 `.ymfb`、`.zip` 文件。

## 后台 API

需要登录 session，写接口需要 CSRF：

- `GET /api/admin/overview`
- `GET /api/admin/config`
- `GET /api/admin/feedbacks?page=&perPage=&status=&category=&priority=&mail=&q=&assignee=&tag=&sla=&overdue=&sort=`
- `GET /api/admin/feedbacks/export?format=csv`
- `GET /api/admin/feedbacks/summary`
- `GET /api/admin/feedbacks/:code`
- `PATCH /api/admin/feedbacks/:code`
- `PATCH /api/admin/feedbacks/bulk`
- `POST /api/admin/feedbacks/:code/comments`
- `GET /api/admin/mails?page=&perPage=&status=`
- `POST /api/admin/mails/test`
- `GET /api/admin/audit-logs?page=&perPage=&actor=&type=`
- `GET /api/admin/webhooks/deliveries?page=&perPage=&status=`
- `POST /api/admin/webhooks/test`
- `POST /api/admin/backups/database`
- `GET /api/admin/backups`
- `GET /api/admin/backups/:name`

## 增强依据

- OWASP File Upload Cheat Sheet：上传限额、存储隔离、路径和内容校验、防 zip bomb。
- OWASP REST Security Cheat Sheet：限流、审计、避免敏感信息出现在 URL、统一错误语义。
- SQLite WAL 与备份文档：读写并发和在线备份思路。
- Go `golang.org/x/time/rate`：内存令牌桶限流。
- Gin graceful shutdown 示例：使用 `http.Server.Shutdown()` 优雅停机。

## 验证

```bash
go test ./...
go build .
npm --prefix admin-web run build
go run main.go
```

启动后建议检查：

```text
http://127.0.0.1:8080/
http://127.0.0.1:8080/admin/
http://127.0.0.1:8080/?api=status&code=BAD
http://127.0.0.1:8080/api/auth/captcha
```
