feat(config): 新增用户前端访问开关

新增 `frontend_enable` 配置并接入后台站点设置,
用于控制用户首页、订阅入口及用户侧 API 是否对外开放。

开关关闭时相关用户入口统一返回空 404,
同时保留节点 API、管理后台与外部回调接口可访问。

补充特性测试覆盖默认开启、关闭隐藏与节点接口白名单场景
This commit is contained in:
yinjianm
2026-04-29 16:31:33 +08:00
parent e847252e12
commit 7a1cba4553
22 changed files with 433 additions and 15 deletions
@@ -0,0 +1,168 @@
# 变更提案: user-frontend-access-toggle
## 元信息
```yaml
类型: 新功能
方案类型: implementation
优先级: P1
状态: 已规划
创建: 2026-04-29
```
---
## 1. 需求
### 背景
当前用户前端由 `routes/web.php``/` 入口渲染,用户侧接口分布在 `/api/v1/passport/*``/api/v1/user/*``/api/v1/client/*``/api/v2/client/*` 与部分 `/api/v1/guest/*`。节点通信接口位于 `/api/v1/server/*``mi-node` 需要保留这些 API。用户希望部署后默认可隐藏用户前端和用户相关 API,降低公网直接访问 `IP:端口` 时暴露用户站点特征的概率,并可在后台手动开启。
### 目标
- 新增后台可保存的 `frontend_enable` 开关,默认开启以保持升级兼容。
- 开关关闭时,用户前端首页、用户订阅入口、登录注册等用户侧 API 返回 404。
- 节点 API 白名单不受影响,`/api/v1/server/*` 继续可用。
- 管理后台路由与管理 API 不纳入本次变更范围。
### 约束条件
```yaml
兼容性约束: 默认值必须为开启,避免升级后自动关闭现有站点。
业务约束: 节点通信接口不可被误拦截;管理后台不纳入处理范围。
实现约束: 用户主题源码不在仓内,隐藏用户前端优先在 Laravel 路由和中间件层完成。
安全约束: 关闭时使用 404 隐藏响应,不输出开关状态或产品识别信息。
```
### 验收标准
- [ ] `frontend_enable` 默认开启时,用户前端、用户 API、订阅入口保持原有路由行为。
- [ ] `frontend_enable` 关闭时,`/``/{subscribe_path}/{token}`、用户登录注册、用户端 API 返回 404。
- [ ] `frontend_enable` 关闭时,`/api/v1/server/*` 节点 API 仍进入原有 `server` 中间件和控制器链路。
- [ ] 后台系统配置页可以读取、切换并保存该开关。
- [ ] 自动化测试覆盖关闭/开启两种状态下的核心路由边界。
---
## 2. 方案
### 技术方案
新增 `EnsureUserFrontendEnabled` 路由中间件,读取 `admin_setting('frontend_enable', 1)`。当开关关闭时返回空 404;开启时放行。将该中间件挂到用户前端入口、订阅入口和用户侧 API 路由;节点 API 和管理端路由不挂载。后台配置接口在 `site` 配置组返回并保存 `frontend_enable`,管理端系统配置页在站点设置中显示该开关。
### 影响范围
```yaml
涉及模块:
- Laravel Web 路由: 控制用户主题首页和订阅入口隐藏行为。
- Laravel API 路由: 控制用户登录注册、用户端、客户端和部分公开用户接口隐藏行为。
- 管理端系统配置: 暴露可保存的用户前端开关。
- 测试: 增加路由边界 feature 测试。
预计变更文件: 8-10
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 误拦截节点 API 导致 `mi-node` 无法同步 | 高 | 不在 `ServerRoute` 上挂载新中间件,并增加节点 API 不返回 404 的测试 |
| 关闭用户前端后现有订阅链接不可用 | 中 | 这是隐藏用户侧入口的预期行为;默认开启保持兼容 |
| 公开回调接口被误关导致支付/Telegram 回调异常 | 中 | 只拦截 `guest/plan``guest/comm` 这类用户展示接口,保留 webhook/notify |
| 配置值布尔转换不一致 | 低 | 中间件用 `filter_var(..., FILTER_VALIDATE_BOOLEAN)` 统一识别 `0/1/true/false` |
### 方案取舍
```yaml
唯一方案理由: 路由中间件能在 Laravel 入口统一隐藏用户前端和用户 API,改动范围清晰,不依赖用户主题源码,也不影响节点/后台路由。
放弃的替代路径:
- Nginx 路径白名单: 部署层可行但不支持后台开关,且每台服务器配置成本高。
- 修改用户主题前端: 仓库内只有用户主题编译产物,无法可靠覆盖 API 暴露问题。
- 全局 API 中间件路径判断: 容易误伤管理 API 和回调接口,边界不如路由级挂载明确。
回滚边界: 移除新增中间件、路由挂载、配置字段和测试即可恢复原行为;数据库中残留 `frontend_enable` 设置不会影响旧代码。
```
---
## 3. 技术设计
### 架构设计
```mermaid
flowchart TD
A[后台系统配置] --> B[frontend_enable 设置]
B --> C[EnsureUserFrontendEnabled]
C -->|开启| D[用户前端/API 原流程]
C -->|关闭| E[404]
F[/api/v1/server/*] --> G[server 中间件]
H[管理后台/API] --> I[原有后台保护]
```
### API 设计
#### GET /api/v2/{secure_path}/config/fetch
- **响应**: `site.frontend_enable: boolean`
#### POST /api/v2/{secure_path}/config/save
- **请求**: `{ "frontend_enable": true|false }`
- **响应**: 沿用现有 `success(true)`
### 数据模型
| 字段 | 类型 | 说明 |
|------|------|------|
| `v2_settings.frontend_enable` | string/bool | 用户前端与用户侧 API 开关,默认 `1` |
---
## 4. 核心场景
### 场景: 用户入口隐藏
**模块**: Laravel Web/API 路由
**条件**: `frontend_enable=false`
**行为**: 访问 `/`、订阅入口、用户登录注册、用户端 API
**结果**: 返回 404,不渲染用户主题,不暴露用户侧 API 响应结构。
### 场景: 节点接口保留
**模块**: 节点 API
**条件**: `frontend_enable=false`
**行为**: `mi-node` 访问 `/api/v1/server/*`
**结果**: 路由继续进入原有节点中间件和控制器,不被用户前端开关拦截。
### 场景: 后台手动开启
**模块**: 管理端系统配置
**条件**: 管理员进入系统配置站点设置
**行为**: 切换“开放用户前端”开关并保存
**结果**: 配置写入 `v2_settings`,下次请求按新开关执行。
---
## 5. 技术决策
### user-frontend-access-toggle#D001: 使用路由级中间件控制用户入口
**日期**: 2026-04-29
**状态**: ✅采纳
**背景**: 需要在应用代码内提供后台可控的隐藏能力,同时避免影响节点 API 和后台 API。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 路由级中间件 | 边界清晰、可测试、默认兼容、能避开节点和后台路由 | 需要在多个用户路由类中显式挂载 |
| B: Nginx 白名单 | 部署快、应用代码少 | 无后台开关,部署环境差异大 |
| C: 全局 API 中间件 | 集中处理 | 动态后台路径、回调接口和节点路径容易误判 |
**决策**: 选择方案 A
**理由**: 当前需求重点是“后台手动开启”和“节点 API 白名单”,路由级中间件能精确表达边界,并能用 feature test 验证。
**影响**: `routes/web.php`、V1/V2 用户路由、中间件注册、后台配置映射和管理端系统配置表单。
---
## 6. 验证策略
```yaml
verifyMode: test-first
reviewerFocus:
- app/Http/Middleware/EnsureUserFrontendEnabled.php
- app/Http/Routes/V1/*.php 与 app/Http/Routes/V2/ClientRoute.php 的挂载边界
- routes/web.php 中用户入口与管理入口隔离
testerFocus:
- vendor/bin/phpunit tests/Feature/UserFrontendAccessToggleTest.php
- php artisan route:list --path=api/v1/server
- admin-frontend npm build
uiValidation: optional
riskBoundary:
- 不修改管理后台安全路径逻辑
- 不修改节点 API Token 或节点认证逻辑
- 不执行数据库迁移或生产数据写入
```
---
## 7. 成果设计
N/A。本次只在既有系统配置表单中加入一个安全开关,复用现有 Apple 风格后台布局、Element Plus `ElSwitch` 和当前表单密度,不新增页面视觉方向。
@@ -0,0 +1,86 @@
# 任务清单: user-frontend-access-toggle
> **@status:** completed | 2026-04-29 16:20
```yaml
@feature: user-frontend-access-toggle
@created: 2026-04-29
@status: completed
@mode: R2
```
## LIVE_STATUS
```json
{"status":"completed","completed":6,"failed":0,"pending":0,"total":6,"percent":100,"current":"用户前端访问开关、路由拦截、后台配置与验证已完成","updated_at":"2026-04-29 16:16:00"}
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 6 | 0 | 0 | 6 |
---
## 任务列表
### 1. 后端访问控制
- [√] 1.1 新增 `app/Http/Middleware/EnsureUserFrontendEnabled.php`
- 预期变更: 读取 `frontend_enable` 设置,关闭时对用户入口返回 404,开启时放行。
- 完成标准: 中间件能正确识别 `true/false/1/0` 等设置值。
- 验证方式: `vendor/bin/phpunit tests/Feature/UserFrontendAccessToggleTest.php`
- depends_on: []
- [√] 1.2 修改 `app/Http/Kernel.php`
- 预期变更: 注册 `user.frontend` 路由中间件别名。
- 完成标准: 路由类可通过别名挂载新增中间件。
- 验证方式: `php artisan route:list` 不报中间件解析错误。
- depends_on: [1.1]
- [√] 1.3 修改 `routes/web.php` 与用户侧 API 路由类
- 预期变更: 用户首页、订阅入口、V1 Passport/User/Client、V2 Client、V1 Guest 的公开展示接口挂载 `user.frontend`;节点 API 与后台 API 不挂载。
- 完成标准: `frontend_enable=false` 时用户侧入口返回 404`/api/v1/server/*` 不被该中间件拦截。
- 验证方式: `vendor/bin/phpunit tests/Feature/UserFrontendAccessToggleTest.php`
- depends_on: [1.2]
### 2. 后台配置接入
- [√] 2.1 修改 `app/Http/Controllers/V2/Admin/ConfigController.php``app/Http/Requests/Admin/ConfigSave.php`
- 预期变更: `site` 配置组返回 `frontend_enable`,保存接口允许布尔值。
- 完成标准: 后台配置接口能读取和保存开关,不影响现有配置字段。
- 验证方式: 特性测试或静态检查配置映射和校验规则。
- depends_on: [1.1]
- [√] 2.2 修改 `admin-frontend/src/utils/systemConfig.ts`
- 预期变更: 在站点设置中新增“开放用户前端”开关字段。
- 完成标准: 管理端系统配置页能渲染并序列化 `frontend_enable`
- 验证方式: `npm --prefix admin-frontend run build`
- depends_on: [2.1]
### 3. 验证与同步
- [√] 3.1 新增并执行验证
- 预期变更: 增加 `tests/Feature/UserFrontendAccessToggleTest.php`,覆盖默认开启、关闭隐藏、节点 API 不被隐藏。
- 完成标准: 相关 PHPUnit 测试通过;管理端构建通过或明确记录阻断原因。
- 验证方式: `vendor/bin/phpunit tests/Feature/UserFrontendAccessToggleTest.php``npm --prefix admin-frontend run build`
- depends_on: [1.3, 2.2]
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-29 15:59 | DESIGN | in_progress | 已完成上下文收集与方案规划 |
| 2026-04-29 16:16 | DEVELOP | completed | 已完成代码实现、PHPUnit 测试与管理端构建验证 |
---
## 执行备注
- 当前方案默认 `frontend_enable=true`,避免升级后自动隐藏现有用户站点。
- 保留 `/api/v1/guest/payment/notify/*``/api/v1/guest/telegram/webhook` 这类外部回调,不将其视为用户前端展示接口。
- `php artisan route:list --path=api/v1/server` 在本地因 PHP 缺少 Redis 扩展失败;节点 API 不被前端开关拦截已由 `UserFrontendAccessToggleTest` 覆盖。
- 历史方案包 `202604250006_ticket-closed-reply-reopen` 仍标记 `in_progress`,与本次任务无依赖。
+1
View File
@@ -7,6 +7,7 @@
| 时间戳 | 名称 | 类型 | 涉及模块 | 决策 | 结果 |
|--------|------|------|---------|------|------|
| 202604291559 | user-frontend-access-toggle | implementation | user-frontend-access,admin-frontend | user-frontend-access-toggle#D001 | ✅完成 |
| 202604290153 | parent-node-auto-visibility | - | - | - | ✅完成 |
| 202604290132 | shared-node-traffic-limit | implementation | node-traffic-limit,admin-frontend | shared-node-traffic-limit#D001 | ✅完成 |
| 202604290123 | node-traffic-yesterday-stats | implementation | admin-frontend,backend-admin-api | node-traffic-yesterday-stats#D001 | ✅完成 |