diff --git a/INSTALL.md b/INSTALL.md index fad816e..1eb8c33 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,90 +1,34 @@ -# MC Report System — 部署文档 +# MC Report — 部署文档 ## 环境要求 - Node.js ≥ 18 / npm ≥ 9 - MySQL ≥ 5.7(或 MariaDB ≥ 10.2) -- 任意操作系统 ## 快速开始 ```bash -# 1. 安装依赖 npm install - -# 2. 启动 node backend/server.js - -# 3. 浏览器打开 -# → 自动跳转安装向导 +# → 浏览器打开 http://localhost:3100 +# → 自动跳转安装向导 ``` -访问 `http://localhost:3100`,将自动进入安装向导。 +### 安装向导 -## 安装向导 - -两步完成: - -**第1步 — 数据库配置** +**第1步:数据库配置** - MySQL 主机(默认 localhost) - 端口(默认 3306) - 用户名 / 密码 - 数据库名(不存在则自动创建) -**第2步 — 创建服主账号** +**第2步:创建服主账号** - 站点名称、站点地址 -- 服主用户名、密码、邮箱、游戏名 +- 服主用户名、密码、邮箱 -点击完成后配置写入 `data/config.json`,**重启服务器**即可加载业务路由。 +安装完成后 **重启服务器** 加载业务路由。 -## 生产环境 - -### 环境变量 - -| 变量 | 说明 | 默认值 | -|------|------|--------| -| `PORT` | 监听端口 | `3100` | -| `CORS_ORIGIN` | 允许的跨域来源 | `http://localhost:3100` | - -### 启动(生产) - -```bash -# Linux / macOS -PORT=8080 CORS_ORIGIN=https://your-domain.com node backend/server.js - -# Windows PowerShell -$env:PORT="8080" -$env:CORS_ORIGIN="https://your-domain.com" -node backend/server.js -``` - -### 持久化进程(推荐 pm2) - -```bash -npm i -g pm2 -pm2 start backend/server.js --name mc-report -pm2 save -pm2 startup -``` - -### Nginx 反代 - -```nginx -server { - listen 80; - server_name report.your-domain.com; - - location / { - proxy_pass http://127.0.0.1:3100; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } -} -``` - -## 配置文件 - -安装后生成 `data/config.json`: +## 配置文件 `data/config.json` ```json { @@ -93,68 +37,74 @@ server { "db_user": "root", "db_pass": "", "db_name": "mc_report", - "jwt_secret": "(随机64位)", - "api_key": "(随机48位)" + "jwt_secret": "(安装时随机64位)", + "api_key": "(安装时随机48位)", + "external_api_key": "(安装时随机48位)" } ``` -- `jwt_secret` 安装时随机生成,用于签发登录令牌 -- `api_key` 安装时随机生成,前端自动注入请求头,保护 API 接口 - -## 数据库 - -MySQL 数据库安装时自动建表。如需备份: +## 生产部署 ```bash -mysqldump -u root -p mc_report > backup_$(date +%Y%m%d).sql +PORT=3100 CORS_ORIGIN=https://your-domain.com node backend/server.js ``` -## 上传文件 +### PM2 -存储在 `data/uploads/`,通过 `/api/uploads/:filename` 鉴权下载。 +```bash +npm i -g pm2 +pm2 start backend/server.js --name mc-report +pm2 save && pm2 startup +``` -| 限制 | 值 | -|------|----| -| 单文件上限 | 50MB | -| 每次最多 | 5 个 | -| 允许格式 | JPG/PNG/GIF/WebP/MP4/WebM | +### Nginx + +```nginx +server { + listen 80; + server_name mc.example.com; + client_max_body_size 55m; + + location / { + proxy_pass http://127.0.0.1:3100; + include nginx.inc; + } +} +``` ## 邮件配置 -后台"系统设置"页面配置 SMTP。未配置时注册用户自动激活。 +后台 → 系统设置 → SMTP 填写。未配置时注册用户自动激活。 ## Webhook -后台"通知配置"页面添加 Discord 兼容 Webhook,可选触发事件。 +后台 → 通知配置 → 添加 Discord Webhook URL → 选择触发事件(工单创建/认领/转交/更新)。 -## 目录结构 +## 数据库 + +MySQL,安装时自动建表。备份: + +```bash +mysqldump -u root -p mc_report > backup.sql +``` + +## 目录 ``` mc-report/ ├── backend/ │ ├── server.js -│ ├── db.js # MySQL 连接池 + 建表 -│ ├── mailer.js # SMTP 邮件 -│ ├── captcha.js # 算术验证码 -│ ├── webhook.js # Webhook 通知 -│ ├── middleware/ -│ │ ├── auth.js # JWT 认证 -│ │ ├── upload.js # 文件上传安全 -│ │ └── security.js # 限速 + XSS + API Key -│ └── routes/ -│ ├── install.js # 安装向导 API -│ ├── auth.js # 注册/登录 -│ ├── tickets.js # 工单核心 -│ ├── users.js # 用户管理 -│ ├── settings.js # 设置 + 邮件模板 -│ ├── export.js # 数据导出 -│ ├── captcha.js # 验证码 -│ ├── notifications.js # Webhook 配置 -│ └── uploads.js # 附件下载 -├── public/ # 前端 SPA -├── data/ -│ ├── config.json # 数据库连接 + 密钥 -│ └── uploads/ # 上传文件 +│ ├── db.js +│ ├── mailer.js +│ ├── webhook.js +│ ├── middleware/{auth,security,upload}.js +│ └── routes/{auth,tickets,install,external,users,settings,export,polls,features,notifications,captcha,uploads}.js +├── public/ # 前端 SPA(16 页面) +├── mc-report-plugin/ # Paper 1.20+ 插件 +├── nginx.inc +├── data/config.json +├── data/uploads/ ├── package.json +├── README.md └── INSTALL.md ``` diff --git a/README.md b/README.md index 856e2c6..739755a 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,184 @@ # MC Report System -游戏玩家投诉建议申诉系统。支持 Web 端 + Paper 插件双端提交,MySQL 存储,JWT 鉴权,三级角色(服主/管理员/玩家)。 +游戏玩家投诉/建议/申诉系统。Web 端 + Paper 插件双端提交,MySQL 存储,JWT 鉴权,三级角色。 -## 功能 +## 功能模块 | 模块 | 说明 | |------|------| -| 举报 | 玩家举报违规行为,可指定目标玩家,上传截图/视频 | -| 建议 | 玩家提交改进建议 | -| 申诉 | 封禁申诉(Web端)+ 结果申诉(对处理不服) | -| 接单制 | 管理员认领工单,一人一单,可转单 | -| 审批流 | 待处理→处理中→待补充→已解决/已驳回→关闭 | -| 投票 | 分组子服定时/不定时投票,盲投制,管理员1.5票权 | -| 更新内容 | PCL风格功能投票,按时间/热度排序,分组筛选 | -| 通知 | SMTP邮件 + Discord Webhook 双通道 | -| 导出 | CSV格式导出工单/被举报人/日志(服主专享) | -| 插件 | Paper 1.20+ 插件,游戏内注册/登录/举报/追踪 | +| **举报** | 指定目标玩家或事项举报,可上传截图/视频(≤50MB),支持投诉管理员(仅服主可见) | +| **建议** | 提交改进建议,仅服主处理 | +| **申诉** | 封禁申诉 + 结果申诉(对处理结果不满可提交一次,关联原工单) | +| **审批流** | 待处理→接单→处理中→待补充→已解决/已驳回→关闭(7天自动关闭) | +| **接单制** | 管理员一人一单,可转单(带原因),处理备注(玩家不可见) | +| **投票** | 分组/子服管理,定时/不定时投票,盲投制,管理员 1.5 票权 | +| **更新内容** | Issues 风格,分状态(待定/计划中/已完成),讨论区,按时间/热度排序,分组筛选 | +| **通知** | SMTP 邮件 + Discord Webhook(SSRF 防护),邮件模板自定义(编辑变量) | +| **导出** | CSV(UTF-8 BOM),导出工单/被举报人统计/操作日志(仅服主) | +| **插件** | Paper 1.20+,统一 `/report` 命令,游戏内注册/登录/举报/追踪 | +| **找回密码** | 邮箱验证,1小时有效重置链接 | +| **安装向导** | Web 页面配置 MySQL 连接 + 创建服主账号 | ## 快速开始 ```bash -# 安装依赖 npm install - -# 启动 node backend/server.js -# → 访问 http://localhost:3100/#/install +# → 打开 http://localhost:3100/#/install ``` -安装向导两步完成:MySQL 连接配置 → 创建服主账号。重启后自动加载业务路由。 +两步安装:填 MySQL 连接信息 → 建服主账号 → 重启生效。 + +## 默认端口 + +`3100`,可通过 `PORT=8080` 环境变量修改。 ## 技术栈 | 层 | 技术 | |----|------| | 后端 | Node.js + Express + mysql2 | -| 前端 | Vanilla JS SPA + Hash路由 | -| 数据库 | MySQL 5.7+ | -| 认证 | JWT(256位随机密钥) | -| 安全 | Helmet + Rate Limit + XSS + CSP | +| 前端 | Vanilla JS SPA(Hash路由)+ Inter 字体 | +| 数据库 | MySQL ≥ 5.7 | +| 认证 | JWT(256 位随机密钥,72h 有效期) | +| 安全 | Helmet 7 + Rate Limit(分路由限速)+ XSS 过滤 + CSP + API Key 常量时间比对 + SSRF 防护 | +| 验证码 | SVG 混淆图片(svg-captcha,4 位字符 + 噪点 + 颜色变形) | +| 文件上传 | Multer + MIME + 魔数字节校验,UUID 命名,web 外存储 | | 插件 | Paper API 1.20.4 + java.net.HttpURLConnection | -## 角色权限 +## 角色权限矩阵 | 操作 | 玩家 | 管理员 | 服主 | |------|:--:|:--:|:--:| | 提交工单 | ✅ | ✅ | ✅ | -| 查看工单 | 自己 | 待处理+自己接的 | 全部 | -| 接单 | — | 举报/申诉 | 全部 | -| 处理工单 | — | 自己接的 | 全部 | +| 查看工单 | 仅自己 | 待处理 + 自己接的 | 全部 | +| 查看详情 | 仅自己 | 待处理 + 自己接的 | 全部 | +| 接单认领 | — | 举报/申诉 | 全部 | +| 更新处理备注 | — | 自己接的 | 全部 | +| 变更状态 | — | 自己接的 | 全部 | +| 转单 | — | 自己接的 | 全部 | +| 回复工单 | 自己的 | ✅ | ✅ | | 用户管理 | — | ✅ | ✅ | +| 邮件模板 | — | ✅ | ✅ | +| 通知配置 | — | ✅ | ✅ | | 系统设置 | — | ✅ | ✅ | | 导出数据 | — | — | ✅ | -| 投票管理 | — | — | ✅ | +| 投票/更新管理 | — | — | ✅ | +| 工单追加(未接单) | 自己的 | — | — | + +## 审批流状态 + +``` +待处理(pending) ──接单──→ 处理中(processing) ──→ 已解决(resolved) + │ │ ├── 已驳回(rejected) + │ 待补充(awaiting_info) └── 7天自动关闭(closed) + │ │ + └── 结果申诉 ──→ 申诉中(appealing) ──服主处理──→ 原工单关闭 +``` + +- `closed` 为终态,仅服主可操作 +- 结果申诉仅一次,关联原工单,仅服主可见 + +## 注册来源 + +| 来源 | 标识 | UID 含义 | +|------|------|---------| +| 网易端 | `netease` | 网易 UID | +| 皮肤站 | `skin` | 皮肤站 UID / Minecraft UUID | + +插件注册默认 `skin`。 + +## 工单提交流程 + +1. 填写标题 + 游戏名 + UID(登录后自动填充) +2. 举报:选目标玩家 + 原因(内置选项或自定义)+ 可投诉管理员 +3. 建议:填写建议内容 +4. 申诉:填写封禁申诉理由 +5. 上传附件(可选,图片/视频 ≤50MB,最多5个) +6. 提交 → 生成 tracking_token(Cookie 留存) + +## 外部 API + +| 端点 | 用途 | 认证 | +|------|------|------| +| `POST /api/external/auth/register` | 插件注册 | x-external-key | +| `POST /api/external/auth/login` | 插件登录 | x-external-key | +| `GET /api/external/my-tickets` | 我的工单(JWT) | x-external-key + Bearer | +| `GET /api/external/my-tickets/:id` | 工单详情(JWT) | x-external-key + Bearer | +| `POST /api/external/tickets` | 提交工单 | x-external-key | +| `GET /api/external/all-tickets` | 全部工单 | x-external-key | +| `GET /api/external/reported-players` | 被举报统计 | x-external-key | +| `GET /api/external/stats` | 统计 | x-external-key | ## 部署 ```bash -# 生产环境变量 +# 环境变量 PORT=3100 CORS_ORIGIN=https://your-domain.com node backend/server.js -# PM2 +# PM2 持久化 pm2 start backend/server.js --name mc-report -# Nginx 反代 -# include nginx.inc; +# Nginx 反代(还需 include nginx.inc) +proxy_pass http://127.0.0.1:3100; ``` -## 目录 +## 插件命令 + +``` +/report reg <密码> <邮箱> [用户名] 注册(默认用户=游戏名) +/report login <密码> [用户名] 登录 +/report report [玩家] <原因> 举报 +/report suggest <内容> 建议 +/report track [id] 追踪工单 +``` + +配置 `plugins/MCReport/config.yml` 填写 `api_url` 和 `external_key`。 + +## 目录结构 ``` ├── backend/ -│ ├── server.js # Express入口 -│ ├── db.js # MySQL连接池 + 建表 -│ ├── mailer.js # SMTP邮件 -│ ├── captcha.js # SVG验证码 -│ ├── webhook.js # Discord Webhook +│ ├── server.js # Express 入口 +│ ├── db.js # MySQL 连接池 + 建表 + 迁移 +│ ├── mailer.js # SMTP 邮件(缓存 transporter) +│ ├── webhook.js # Discord Webhook(SSRF 防护 + 10s 超时) │ ├── middleware/ -│ │ ├── auth.js # JWT认证 -│ │ ├── security.js # 限速+XSS+APIKey -│ │ └── upload.js # 文件上传安全 +│ │ ├── auth.js # JWT(惰性缓存 secret) +│ │ ├── security.js # 限速 + XSS 递归 + API Key 常量时间比对 +│ │ └── upload.js # 文件上传(仅读 256B 头验证魔数) │ └── routes/ -│ ├── auth.js # 注册/登录 -│ ├── tickets.js # 工单核心 -│ ├── install.js # 安装向导 -│ ├── external.js # 外部API(插件用) -│ └── ... # 其他路由 -├── public/ # 前端SPA -├── mc-report-plugin/ # Paper 1.20+ 插件 -├── nginx.inc # Nginx额外配置 -└── data/ # 运行时数据 +│ ├── auth.js # 注册/登录/验证/找回密码 +│ ├── tickets.js # 工单核心(自动关闭 + 状态流转) +│ ├── install.js # 安装向导(含 api_key 生成) +│ ├── external.js # 外部 API(插件 + Java 服务器) +│ ├── users.js # 用户管理(含 source 字段) +│ ├── settings.js # 站点设置 + 邮件模板编辑 +│ ├── export.js # CSV 导出(服主专享) +│ ├── polls.js # 投票(分组/子服/定时) +│ ├── features.js # 更新内容(Issues 风格 + 讨论) +│ ├── notifications.js # Webhook 配置 +│ ├── captcha.js # SVG 验证码 +│ └── uploads.js # 附件下载(鉴权) +├── public/ # 前端 SPA (16 页面) +├── mc-report-plugin/ # Paper 1.20+ 插件 (Maven) +├── nginx.inc # Nginx 额外配置 +├── data/ # config.json + uploads/ +├── README.md +└── package.json ``` + +## 安全 + +| 措施 | 实现 | +|------|------| +| JWT 秘钥 | 256 位随机,惰性缓存,无硬编码 fallback | +| API Key | 48 位随机,常量时间比对防时序攻击 | +| 限速 | 登录 8/min、注册 3/min、工单 10/min、全局 300/min | +| XSS | `sanitizeBody` 递归过滤 + `U.esc` 前端转义 | +| CSP | Helmet + `script-src-attr 'unsafe-inline'` | +| SQL 注入 | 全参数化查询 | +| 文件上传 | 扩展名 + MIME + 魔数(仅读 256B)+ UUID 命名 | +| Webhook SSRF | DNS 解析后过滤内网 IP | +| 用户名枚举 | 注册/登录统一错误信息 | +| CORS | 环境变量控制 + localhost 自动放行 | diff --git a/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java b/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java index 34ec93a..1d87222 100644 --- a/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java +++ b/mc-report-plugin/src/main/java/com/mcreport/plugin/ApiClient.java @@ -71,6 +71,7 @@ public class ApiClient { body.addProperty("email", email); body.addProperty("game_name", gameName); body.addProperty("game_uid", gameUid); + body.addProperty("source", "skin"); return request("POST", "/auth/register", body, null); }