[优化] 更新认证机制,使用 OPENFLARE_TOKEN 替代 Bearer Token

This commit is contained in:
ryan
2026-06-04 11:10:06 +08:00
parent bdc96f6d8e
commit 6aa71a4da8
38 changed files with 462 additions and 387 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ docker run -d \
启动参数说明: 启动参数说明:
* **`-p 3000:3000`**:映射宿主机 `3000` 端口到容器内 `3000` 端口。 * **`-p 3000:3000`**:映射宿主机 `3000` 端口到容器内 `3000` 端口。
* **`-v $(pwd)/openflare-data:/data`**:挂载本地目录到容器的 `/data`,确保数据库文件 `openflare.db` 在重启或重建容器时不丢失。 * **`-v $(pwd)/openflare-data:/data`**:挂载本地目录到容器的 `/data`,确保数据库文件 `openflare.db` 在重启或重建容器时不丢失。
* **`SESSION_SECRET`**:必须配置的 Session 密钥签名哈希。 * **`SESSION_SECRET`**:建议配置的临时 Session 签名密钥,主要用于 OAuth 状态等非管理端 API 鉴权流程;管理端 API 登录凭证通过 `OPENFLARE_TOKEN` 请求头传递。
--- ---
+1 -1
View File
@@ -79,7 +79,7 @@ Agent 本地 Pages 部署目录
* Gin 提供 HTTP 服务。 * Gin 提供 HTTP 服务。
* GORM 访问 SQLite 或 PostgreSQL。 * GORM 访问 SQLite 或 PostgreSQL。
* 现有登录体系提供管理端 Session。 * 现有登录体系签发管理端用户 Token,管理端 API 通过 `OPENFLARE_TOKEN` 请求头鉴权。
* 认证源与外部账号绑定支持 GitHub OAuth 和标准 OIDC。 * 认证源与外部账号绑定支持 GitHub OAuth 和标准 OIDC。
* Go Server 托管 `openflare_server/web` 静态构建产物。 * Go Server 托管 `openflare_server/web` 静态构建产物。
+3 -3
View File
@@ -9,7 +9,7 @@
| 现象 | 先看哪里 | | 现象 | 先看哪里 |
| --- | --- | | --- | --- |
| 管理端打不开 | Server 容器或进程日志、端口监听 | | 管理端打不开 | Server 容器或进程日志、端口监听 |
| 登录异常 | 默认账号、Session Secret、浏览器请求、Server 日志 | | 登录异常 | 默认账号、OPENFLARE_TOKEN、浏览器请求、Server 日志 |
| 数据无法保存 | 数据库连接、SQLite 文件权限、PostgreSQL 健康状态 | | 数据无法保存 | 数据库连接、SQLite 文件权限、PostgreSQL 健康状态 |
| Agent 离线 | Agent 日志、Token、Server 地址、网络连通性 | | Agent 离线 | Agent 日志、Token、Server 地址、网络连通性 |
| 发布后节点未更新 | 激活版本、节点 heartbeat、应用记录 | | 发布后节点未更新 | 激活版本、节点 heartbeat、应用记录 |
@@ -85,8 +85,8 @@ NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev
1. 确认连接的是预期数据库,避免 `SQLITE_PATH` 或 `DSN` 指向了另一个环境。 1. 确认连接的是预期数据库,避免 `SQLITE_PATH` 或 `DSN` 指向了另一个环境。
2. 查看 Server 日志中使用的是 `sqlite` 还是 `postgres`。 2. 查看 Server 日志中使用的是 `sqlite` 还是 `postgres`。
3. 如果部署在多副本或反向代理后,确认 `SESSION_SECRET` 固定且各实例一致。 3. 在浏览器开发者工具中确认管理端 API 请求携带 `OPENFLARE_TOKEN` 请求头。
4. 清理浏览器 Cookie 后重新登录。 4. 清理浏览器本地存储中的旧 `openflare_token` 后重新登录。
### 应急重置管理员密码 ### 应急重置管理员密码
+4 -4
View File
@@ -167,19 +167,19 @@ v1-v7 视为历史初始基线,不再维护逐版本升级文件。v8-v17 是
- Client 心跳返回 tunnel 配置版本摘要。 - Client 心跳返回 tunnel 配置版本摘要。
- Client 可拉取完整配置(relay 列表 + frpc 代理定义)。 - Client 可拉取完整配置(relay 列表 + frpc 代理定义)。
- Client 上报配置应用结果。 - Client 上报配置应用结果。
* **Admin Tunnel 管理 API** - `/api/tunnels/*`,要求 Admin Session。 * **Admin Tunnel 管理 API** - `/api/tunnels/*`,要求管理端 `OPENFLARE_TOKEN`。
- CRUD tunnel 实体(创建、查询、更新、删除)。 - CRUD tunnel 实体(创建、查询、更新、删除)。
- Token 管理(生成、轮换)。 - Token 管理(生成、轮换)。
- 强制同步(触发 Client 立即拉取新配置)。 - 强制同步(触发 Client 立即拉取新配置)。
* **Admin Pages 管理 API** - `/api/pages/*`,要求 Admin Session。 * **Admin Pages 管理 API** - `/api/pages/*`,要求管理端 `OPENFLARE_TOKEN`。
- CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。 - CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。
- 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。 - 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。
* **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。 * **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。
- Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。 - Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。
* 总览与节点详情优先使用专用聚合接口。 * 总览与节点详情优先使用专用聚合接口。
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。 * 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
* 管理端继续复用现有登录、角色与 Session。 * 管理端登录成功后返回用户 token;管理端 API 只允许从 `OPENFLARE_TOKEN` 请求头读取登录凭证,不得通过 Cookie Session 放行。
* 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root Session。 * 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root 级 `OPENFLARE_TOKEN`。
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。 * `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
* 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。 * 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。
* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。 * Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。
+9 -3
View File
@@ -20,7 +20,7 @@ OpenFlare 的管理端 API 与 Agent API 都使用 JSON。
| 类型 | 约定 | | 类型 | 约定 |
| --- | --- | | --- | --- |
| 管理端 API | 由管理端 Session 鉴权 | | 管理端 API | 由 `OPENFLARE_TOKEN` 请求头鉴权 |
| Agent API | 固定放在 `/api/agent/*` | | Agent API | 固定放在 `/api/agent/*` |
| Relay API | 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 鉴权(与 Agent 复用同一 token) | | Relay API | 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 鉴权(与 Agent 复用同一 token) |
| OpenFlared API | 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 鉴权(独立的 tunnel_token) | | OpenFlared API | 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 鉴权(独立的 tunnel_token) |
@@ -29,7 +29,7 @@ OpenFlare 的管理端 API 与 Agent API 都使用 JSON。
## WAF IP 组接口 ## WAF IP 组接口
管理端 WAF IP 组接口统一要求管理端 Session 鉴权: 管理端 WAF IP 组接口统一要求管理端 `OPENFLARE_TOKEN` 鉴权:
| 方法 | 路径 | 说明 | | 方法 | 路径 | 说明 |
| --- | --- | --- | | --- | --- | --- |
@@ -47,7 +47,13 @@ IP 组 `type` 支持 `manual`、`automatic`、`subscription`。自动型 IP 组
## 鉴权 ## 鉴权
管理端继续复用现有登录、角色与 Session。 管理端登录成功后返回用户 token,后续所有管理端 API 必须在请求头中携带:
```http
OPENFLARE_TOKEN: <token>
```
Server 只从 `OPENFLARE_TOKEN` 读取管理端登录凭证,不再通过 Cookie Session 放行管理端 API。角色和用户状态仍以数据库中的当前用户记录为准。
Agent 正式请求统一使用节点专属 `agent_token`,首次接入可使用全局 `discovery_token`。Agent 请求头固定为: Agent 正式请求统一使用节点专属 `agent_token`,首次接入可使用全局 `discovery_token`。Agent 请求头固定为:
+2 -2
View File
@@ -64,7 +64,7 @@ go run . --port 3000 --log-dir ./logs
| `PORT` | Server 监听端口 | `3000` | | `PORT` | Server 监听端口 | `3000` |
| `GIN_MODE` | Gin 运行模式 | 非 `debug` 时按 release | | `GIN_MODE` | Gin 运行模式 | 非 `debug` 时按 release |
| `LOG_LEVEL` | 日志等级 | `info` | | `LOG_LEVEL` | 日志等级 | `info` |
| `SESSION_SECRET` | Session 签名密钥 | 启动时随机生成 | | `SESSION_SECRET` | 临时 Session 签名密钥,主要用于 OAuth 状态等非管理端 API 鉴权流程 | 启动时随机生成 |
| `SQLITE_PATH` | SQLite 数据库文件路径 | `openflare.db` | | `SQLITE_PATH` | SQLite 数据库文件路径 | `openflare.db` |
| `DSN` | PostgreSQL DSN,设置后优先于 SQLite | 空 | | `DSN` | PostgreSQL DSN,设置后优先于 SQLite | 空 |
| `SQL_DSN` | 兼容旧命名的 PostgreSQL DSN,优先级低于 `DSN` | 空 | | `SQL_DSN` | 兼容旧命名的 PostgreSQL DSN,优先级低于 `DSN` | 空 |
@@ -76,7 +76,7 @@ go run . --port 3000 --log-dir ./logs
* `DSN` 与 `SQL_DSN` 同时存在时优先使用 `DSN`。 * `DSN` 与 `SQL_DSN` 同时存在时优先使用 `DSN`。
* `DSN` 或 `SQL_DSN` 与 `SQLITE_PATH` 同时存在时优先使用 PostgreSQL。 * `DSN` 或 `SQL_DSN` 与 `SQLITE_PATH` 同时存在时优先使用 PostgreSQL。
* 当目标 PostgreSQL 数据库为空且本地 `SQLITE_PATH` 文件存在时,Server 启动阶段会自动迁移 SQLite 数据,并在日志中输出按表迁移进度。 * 当目标 PostgreSQL 数据库为空且本地 `SQLITE_PATH` 文件存在时,Server 启动阶段会自动迁移 SQLite 数据,并在日志中输出按表迁移进度。
* `SESSION_SECRET` 生产环境必须显式配置。 * `SESSION_SECRET` 生产环境建议显式配置,避免 OAuth 授权状态等临时会话在重启后失效;管理端 API 登录凭证不再通过 Cookie Session 传递,而是使用 `OPENFLARE_TOKEN` 请求头。
* `REDIS_CONN_STRING` 未配置时,相关能力回退为进程内实现。 * `REDIS_CONN_STRING` 未配置时,相关能力回退为进程内实现。
## 运行时 Option ## 运行时 Option
+6 -6
View File
@@ -11,7 +11,7 @@ import (
// @Summary List access logs // @Summary List access logs
// @Tags AccessLogs // @Tags AccessLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Param remote_addr query string false "Remote address" // @Param remote_addr query string false "Remote address"
// @Param host query string false "Host" // @Param host query string false "Host"
@@ -35,7 +35,7 @@ func GetAccessLogs(c *gin.Context) {
// @Summary List folded access logs // @Summary List folded access logs
// @Tags AccessLogs // @Tags AccessLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Param remote_addr query string false "Remote address" // @Param remote_addr query string false "Remote address"
// @Param host query string false "Host" // @Param host query string false "Host"
@@ -62,7 +62,7 @@ func GetFoldedAccessLogs(c *gin.Context) {
// @Summary List folded access log IP summaries // @Summary List folded access log IP summaries
// @Tags AccessLogs // @Tags AccessLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Param remote_addr query string false "Remote address" // @Param remote_addr query string false "Remote address"
// @Param host query string false "Host" // @Param host query string false "Host"
@@ -99,7 +99,7 @@ func GetFoldedAccessLogIPs(c *gin.Context) {
// @Summary List access log IP summaries // @Summary List access log IP summaries
// @Tags AccessLogs // @Tags AccessLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Param remote_addr query string false "Remote address" // @Param remote_addr query string false "Remote address"
// @Param host query string false "Host" // @Param host query string false "Host"
@@ -130,7 +130,7 @@ func GetAccessLogIPSummaries(c *gin.Context) {
// @Summary Get access log IP trend // @Summary Get access log IP trend
// @Tags AccessLogs // @Tags AccessLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Param remote_addr query string true "Remote address" // @Param remote_addr query string true "Remote address"
// @Param host query string false "Host" // @Param host query string false "Host"
@@ -158,7 +158,7 @@ func GetAccessLogIPTrend(c *gin.Context) {
// @Tags AccessLogs // @Tags AccessLogs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/access-logs/cleanup [post] // @Router /api/access-logs/cleanup [post]
func CleanupAccessLogs(c *gin.Context) { func CleanupAccessLogs(c *gin.Context) {
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// @Summary Get default ACME account // @Summary Get default ACME account
// @Tags AcmeAccounts // @Tags AcmeAccounts
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/acme-accounts/default [get] // @Router /api/acme-accounts/default [get]
func GetDefaultAcmeAccount(c *gin.Context) { func GetDefaultAcmeAccount(c *gin.Context) {
+3 -3
View File
@@ -296,7 +296,7 @@ func handleAgentWSStatus(c *gin.Context, node *model.Node, message service.Agent
// @Summary List nodes // @Summary List nodes
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/nodes/ [get] // @Router /api/nodes/ [get]
func GetNodes(c *gin.Context) { func GetNodes(c *gin.Context) {
@@ -312,7 +312,7 @@ func GetNodes(c *gin.Context) {
// @Summary List apply logs // @Summary List apply logs
// @Tags ApplyLogs // @Tags ApplyLogs
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param node_id query string false "Node ID" // @Param node_id query string false "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/apply-logs/ [get] // @Router /api/apply-logs/ [get]
@@ -334,7 +334,7 @@ func GetApplyLogs(c *gin.Context) {
// @Tags ApplyLogs // @Tags ApplyLogs
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/apply-logs/cleanup [post] // @Router /api/apply-logs/cleanup [post]
func CleanupApplyLogs(c *gin.Context) { func CleanupApplyLogs(c *gin.Context) {
+4 -6
View File
@@ -199,10 +199,8 @@ func OAuthCallback(c *gin.Context) {
return return
} }
var currentUserID *int var currentUserID *int
if value := session.Get("id"); value != nil { if currentUser := currentUserFromOpenFlareToken(c); currentUser != nil {
if idValue, ok := value.(int); ok { currentUserID = &currentUser.Id
currentUserID = &idValue
}
} }
result, pending, err := service.CompleteOAuthLogin(source, profile, currentUserID) result, pending, err := service.CompleteOAuthLogin(source, profile, currentUserID)
if err != nil { if err != nil {
@@ -224,7 +222,7 @@ func OAuthCallback(c *gin.Context) {
return return
} }
if result.User != nil { if result.User != nil {
cleanUser, err := setLoginSession(result.User, c) cleanUser, err := setLoginToken(result.User)
if err != nil { if err != nil {
respondFailure(c, "无法保存会话信息,请重试") respondFailure(c, "无法保存会话信息,请重试")
return return
@@ -261,7 +259,7 @@ func LinkExistingOAuthAccount(c *gin.Context) {
respondFailure(c, "无法更新会话信息,请重试") respondFailure(c, "无法更新会话信息,请重试")
return return
} }
cleanUser, err := setLoginSession(user, c) cleanUser, err := setLoginToken(user)
if err != nil { if err != nil {
respondFailure(c, "无法保存会话信息,请重试") respondFailure(c, "无法保存会话信息,请重试")
return return
@@ -10,7 +10,7 @@ import (
// @Summary List config versions // @Summary List config versions
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/config-versions/ [get] // @Router /api/config-versions/ [get]
func GetConfigVersions(c *gin.Context) { func GetConfigVersions(c *gin.Context) {
@@ -26,7 +26,7 @@ func GetConfigVersions(c *gin.Context) {
// @Summary Get config version detail // @Summary Get config version detail
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Version ID" // @Param id path int true "Version ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -48,7 +48,7 @@ func GetConfigVersion(c *gin.Context) {
// @Summary Get active config version // @Summary Get active config version
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/config-versions/active [get] // @Router /api/config-versions/active [get]
func GetActiveConfigVersion(c *gin.Context) { func GetActiveConfigVersion(c *gin.Context) {
@@ -64,7 +64,7 @@ func GetActiveConfigVersion(c *gin.Context) {
// @Summary Preview config rendering // @Summary Preview config rendering
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/config-versions/preview [get] // @Router /api/config-versions/preview [get]
func PreviewConfigVersion(c *gin.Context) { func PreviewConfigVersion(c *gin.Context) {
@@ -80,7 +80,7 @@ func PreviewConfigVersion(c *gin.Context) {
// @Summary Diff current draft against active version // @Summary Diff current draft against active version
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/config-versions/diff [get] // @Router /api/config-versions/diff [get]
func DiffConfigVersion(c *gin.Context) { func DiffConfigVersion(c *gin.Context) {
@@ -96,7 +96,7 @@ func DiffConfigVersion(c *gin.Context) {
// @Summary Publish a new config version // @Summary Publish a new config version
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/config-versions/publish [post] // @Router /api/config-versions/publish [post]
func PublishConfigVersion(c *gin.Context) { func PublishConfigVersion(c *gin.Context) {
@@ -114,7 +114,7 @@ func PublishConfigVersion(c *gin.Context) {
// @Summary Activate an existing config version // @Summary Activate an existing config version
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Version ID" // @Param id path int true "Version ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -140,7 +140,7 @@ type CleanupConfigVersionRequest struct {
// @Summary Cleanup old config versions // @Summary Cleanup old config versions
// @Tags ConfigVersions // @Tags ConfigVersions
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param request body CleanupConfigVersionRequest true "Cleanup request" // @Param request body CleanupConfigVersionRequest true "Cleanup request"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
+1 -1
View File
@@ -33,7 +33,7 @@ type dashboardTrendsPayload struct {
// @Summary Get dashboard overview // @Summary Get dashboard overview
// @Tags Dashboard // @Tags Dashboard
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
// @Router /api/dashboard/overview [get] // @Router /api/dashboard/overview [get]
+1 -1
View File
@@ -11,7 +11,7 @@ import (
// @Tags Options // @Tags Options
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/option/database/cleanup [post] // @Router /api/option/database/cleanup [post]
func CleanupDatabaseObservability(c *gin.Context) { func CleanupDatabaseObservability(c *gin.Context) {
+4 -4
View File
@@ -16,7 +16,7 @@ type DnsAccountInput struct {
// @Summary List DNS accounts // @Summary List DNS accounts
// @Tags DnsAccounts // @Tags DnsAccounts
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/dns-accounts/ [get] // @Router /api/dns-accounts/ [get]
func GetDnsAccounts(c *gin.Context) { func GetDnsAccounts(c *gin.Context) {
@@ -33,7 +33,7 @@ func GetDnsAccounts(c *gin.Context) {
// @Tags DnsAccounts // @Tags DnsAccounts
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body DnsAccountInput true "DNS account payload" // @Param payload body DnsAccountInput true "DNS account payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/dns-accounts/ [post] // @Router /api/dns-accounts/ [post]
@@ -62,7 +62,7 @@ func CreateDnsAccount(c *gin.Context) {
// @Tags DnsAccounts // @Tags DnsAccounts
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "DNS Account ID" // @Param id path int true "DNS Account ID"
// @Param payload body DnsAccountInput true "DNS account payload" // @Param payload body DnsAccountInput true "DNS account payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -100,7 +100,7 @@ func UpdateDnsAccount(c *gin.Context) {
// @Summary Delete DNS account // @Summary Delete DNS account
// @Tags DnsAccounts // @Tags DnsAccounts
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "DNS Account ID" // @Param id path int true "DNS Account ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/dns-accounts/{id}/delete [post] // @Router /api/dns-accounts/{id}/delete [post]
+7 -8
View File
@@ -11,7 +11,6 @@ import (
"openflare/model" "openflare/model"
"time" "time"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -79,9 +78,7 @@ func getGitHubUserInfoByCode(code string) (*GitHubUser, error) {
} }
func GitHubOAuth(c *gin.Context) { func GitHubOAuth(c *gin.Context) {
session := sessions.Default(c) if currentUserFromOpenFlareToken(c) != nil {
username := session.Get("username")
if username != nil {
GitHubBind(c) GitHubBind(c)
return return
} }
@@ -135,10 +132,12 @@ func GitHubBind(c *gin.Context) {
respondFailure(c, "该 GitHub 账户已被绑定") respondFailure(c, "该 GitHub 账户已被绑定")
return return
} }
session := sessions.Default(c) currentUser := currentUserFromOpenFlareToken(c)
id := session.Get("id") if currentUser == nil {
// id := c.GetInt("id") // critical bug! respondFailure(c, "无权进行此操作,未登录或 token 无效")
user.Id = id.(int) return
}
user.Id = currentUser.Id
err = user.FillUserById() err = user.FillUserById()
if err != nil { if err != nil {
respondFailure(c, err.Error()) respondFailure(c, err.Error())
@@ -11,7 +11,7 @@ import (
// @Summary List managed domains // @Summary List managed domains
// @Tags ManagedDomains // @Tags ManagedDomains
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/managed-domains/ [get] // @Router /api/managed-domains/ [get]
func GetManagedDomains(c *gin.Context) { func GetManagedDomains(c *gin.Context) {
@@ -28,7 +28,7 @@ func GetManagedDomains(c *gin.Context) {
// @Tags ManagedDomains // @Tags ManagedDomains
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body service.ManagedDomainInput true "Managed domain payload" // @Param payload body service.ManagedDomainInput true "Managed domain payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -51,7 +51,7 @@ func CreateManagedDomain(c *gin.Context) {
// @Tags ManagedDomains // @Tags ManagedDomains
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Managed domain ID" // @Param id path int true "Managed domain ID"
// @Param payload body service.ManagedDomainInput true "Managed domain payload" // @Param payload body service.ManagedDomainInput true "Managed domain payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -78,7 +78,7 @@ func UpdateManagedDomain(c *gin.Context) {
// @Summary Delete managed domain // @Summary Delete managed domain
// @Tags ManagedDomains // @Tags ManagedDomains
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Managed domain ID" // @Param id path int true "Managed domain ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -99,7 +99,7 @@ func DeleteManagedDomain(c *gin.Context) {
// @Summary Match certificate for domain // @Summary Match certificate for domain
// @Tags ManagedDomains // @Tags ManagedDomains
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param domain query string true "Domain" // @Param domain query string true "Domain"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/managed-domains/match [get] // @Router /api/managed-domains/match [get]
+11 -11
View File
@@ -21,7 +21,7 @@ type nodeObservabilityQuery struct {
// @Tags Nodes // @Tags Nodes
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body service.NodeInput true "Node payload" // @Param payload body service.NodeInput true "Node payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -44,7 +44,7 @@ func CreateNode(c *gin.Context) {
// @Summary Get global discovery token // @Summary Get global discovery token
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/nodes/bootstrap-token [get] // @Router /api/nodes/bootstrap-token [get]
func GetNodeBootstrapToken(c *gin.Context) { func GetNodeBootstrapToken(c *gin.Context) {
@@ -60,7 +60,7 @@ func GetNodeBootstrapToken(c *gin.Context) {
// @Summary Rotate global discovery token // @Summary Rotate global discovery token
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/nodes/bootstrap-token/rotate [post] // @Router /api/nodes/bootstrap-token/rotate [post]
func RotateNodeBootstrapToken(c *gin.Context) { func RotateNodeBootstrapToken(c *gin.Context) {
@@ -77,7 +77,7 @@ func RotateNodeBootstrapToken(c *gin.Context) {
// @Tags Nodes // @Tags Nodes
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Param payload body service.NodeInput true "Node payload" // @Param payload body service.NodeInput true "Node payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -106,7 +106,7 @@ func UpdateNode(c *gin.Context) {
// @Summary Delete node // @Summary Delete node
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -128,7 +128,7 @@ func DeleteNode(c *gin.Context) {
// @Summary Request agent self-update on node // @Summary Request agent self-update on node
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -162,7 +162,7 @@ func RequestNodeAgentUpdate(c *gin.Context) {
// @Summary Request openresty restart on node // @Summary Request openresty restart on node
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -185,7 +185,7 @@ func RequestNodeOpenrestyRestart(c *gin.Context) {
// @Summary Request force sync config on node // @Summary Request force sync config on node
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -208,7 +208,7 @@ func RequestNodeForceSync(c *gin.Context) {
// @Summary Check latest agent release for node // @Summary Check latest agent release for node
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Param channel query string false "stable or preview" // @Param channel query string false "stable or preview"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -232,7 +232,7 @@ func GetNodeAgentRelease(c *gin.Context) {
// @Summary Get node observability details // @Summary Get node observability details
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Param hours query int false "Lookback window in hours" // @Param hours query int false "Lookback window in hours"
// @Param limit query int false "Max records per section" // @Param limit query int false "Max records per section"
@@ -266,7 +266,7 @@ func GetNodeObservability(c *gin.Context) {
// @Summary Cleanup node health events // @Summary Cleanup node health events
// @Tags Nodes // @Tags Nodes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Node ID" // @Param id path int true "Node ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
+5 -5
View File
@@ -10,7 +10,7 @@ import (
// @Summary List proxy routes // @Summary List proxy routes
// @Tags ProxyRoutes // @Tags ProxyRoutes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/proxy-routes/ [get] // @Router /api/proxy-routes/ [get]
func GetProxyRoutes(c *gin.Context) { func GetProxyRoutes(c *gin.Context) {
@@ -26,7 +26,7 @@ func GetProxyRoutes(c *gin.Context) {
// @Summary Get proxy route detail // @Summary Get proxy route detail
// @Tags ProxyRoutes // @Tags ProxyRoutes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Route ID" // @Param id path int true "Route ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -49,7 +49,7 @@ func GetProxyRoute(c *gin.Context) {
// @Tags ProxyRoutes // @Tags ProxyRoutes
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body service.ProxyRouteInput true "Proxy route payload" // @Param payload body service.ProxyRouteInput true "Proxy route payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -72,7 +72,7 @@ func CreateProxyRoute(c *gin.Context) {
// @Tags ProxyRoutes // @Tags ProxyRoutes
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Route ID" // @Param id path int true "Route ID"
// @Param payload body service.ProxyRouteInput true "Proxy route payload" // @Param payload body service.ProxyRouteInput true "Proxy route payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -99,7 +99,7 @@ func UpdateProxyRoute(c *gin.Context) {
// @Summary Delete proxy route // @Summary Delete proxy route
// @Tags ProxyRoutes // @Tags ProxyRoutes
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Route ID" // @Param id path int true "Route ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
+11 -11
View File
@@ -10,7 +10,7 @@ import (
// @Summary List TLS certificates // @Summary List TLS certificates
// @Tags TLSCertificates // @Tags TLSCertificates
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/tls-certificates/ [get] // @Router /api/tls-certificates/ [get]
func GetTLSCertificates(c *gin.Context) { func GetTLSCertificates(c *gin.Context) {
@@ -26,7 +26,7 @@ func GetTLSCertificates(c *gin.Context) {
// @Summary Get TLS certificate detail // @Summary Get TLS certificate detail
// @Tags TLSCertificates // @Tags TLSCertificates
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -49,7 +49,7 @@ func GetTLSCertificate(c *gin.Context) {
// @Summary Get TLS certificate PEM content // @Summary Get TLS certificate PEM content
// @Tags TLSCertificates // @Tags TLSCertificates
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -73,7 +73,7 @@ func GetTLSCertificateContent(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body service.TLSCertificateInput true "TLS certificate payload" // @Param payload body service.TLSCertificateInput true "TLS certificate payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -96,7 +96,7 @@ func CreateTLSCertificate(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Param payload body service.TLSCertificateInput true "TLS certificate payload" // @Param payload body service.TLSCertificateInput true "TLS certificate payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -126,7 +126,7 @@ func UpdateTLSCertificate(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept multipart/form-data // @Accept multipart/form-data
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param name formData string true "Certificate name" // @Param name formData string true "Certificate name"
// @Param remark formData string false "Remark" // @Param remark formData string false "Remark"
// @Param cert_file formData file true "Certificate file" // @Param cert_file formData file true "Certificate file"
@@ -159,7 +159,7 @@ func ImportTLSCertificateFile(c *gin.Context) {
// @Summary Delete TLS certificate // @Summary Delete TLS certificate
// @Tags TLSCertificates // @Tags TLSCertificates
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -181,7 +181,7 @@ func DeleteTLSCertificate(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param payload body service.TLSApplyInput true "TLS apply payload" // @Param payload body service.TLSApplyInput true "TLS apply payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
@@ -204,7 +204,7 @@ func ApplyTLSCertificate(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Param payload body service.TLSApplyInput true "TLS apply payload" // @Param payload body service.TLSApplyInput true "TLS apply payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -233,7 +233,7 @@ func UpdateAcmeCertificate(c *gin.Context) {
// @Tags TLSCertificates // @Tags TLSCertificates
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Param payload body service.TLSApplyInput true "TLS apply payload" // @Param payload body service.TLSApplyInput true "TLS apply payload"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
@@ -261,7 +261,7 @@ func ConvertTLSCertificateToAcme(c *gin.Context) {
// @Summary Renew TLS certificate // @Summary Renew TLS certificate
// @Tags TLSCertificates // @Tags TLSCertificates
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Param id path int true "Certificate ID" // @Param id path int true "Certificate ID"
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{} // @Failure 400 {object} map[string]interface{}
+1 -1
View File
@@ -21,7 +21,7 @@ type serverUpgradeRequest struct {
// @Summary Get latest GitHub release // @Summary Get latest GitHub release
// @Tags Update // @Tags Update
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/update/latest-release [get] // @Router /api/update/latest-release [get]
func GetLatestRelease(c *gin.Context) { func GetLatestRelease(c *gin.Context) {
+1 -1
View File
@@ -11,7 +11,7 @@ import (
// @Tags UptimeKuma // @Tags UptimeKuma
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth // @Security OpenFlareTokenAuth
// @Success 200 {object} map[string]interface{} // @Success 200 {object} map[string]interface{}
// @Router /api/uptimekuma/sync [post] // @Router /api/uptimekuma/sync [post]
func SyncUptimeKuma(c *gin.Context) { func SyncUptimeKuma(c *gin.Context) {
+41 -16
View File
@@ -1,6 +1,7 @@
package controller package controller
import ( import (
"errors"
"openflare/common" "openflare/common"
"openflare/model" "openflare/model"
"openflare/utils/security" "openflare/utils/security"
@@ -8,7 +9,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
) )
@@ -45,14 +45,9 @@ func Login(c *gin.Context) {
setupLogin(&user, c) setupLogin(&user, c)
} }
// setup session & cookies and then return user info // setup token and then return user info
func setLoginSession(user *model.User, c *gin.Context) (*model.User, error) { func setLoginToken(user *model.User) (*model.User, error) {
session := sessions.Default(c) token, err := ensureUserOpenFlareToken(user)
session.Set("id", user.Id)
session.Set("username", user.Username)
session.Set("role", user.Role)
session.Set("status", user.Status)
err := session.Save()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -62,12 +57,13 @@ func setLoginSession(user *model.User, c *gin.Context) (*model.User, error) {
DisplayName: user.DisplayName, DisplayName: user.DisplayName,
Role: user.Role, Role: user.Role,
Status: user.Status, Status: user.Status,
Token: token,
} }
return cleanUser, nil return cleanUser, nil
} }
func setupLogin(user *model.User, c *gin.Context) { func setupLogin(user *model.User, c *gin.Context) {
cleanUser, err := setLoginSession(user, c) cleanUser, err := setLoginToken(user)
if err != nil { if err != nil {
respondFailure(c, "无法保存会话信息,请重试") respondFailure(c, "无法保存会话信息,请重试")
return return
@@ -76,16 +72,45 @@ func setupLogin(user *model.User, c *gin.Context) {
} }
func Logout(c *gin.Context) { func Logout(c *gin.Context) {
session := sessions.Default(c) token := c.GetHeader("OPENFLARE_TOKEN")
session.Clear() if token != "" {
err := session.Save() user := model.ValidateUserToken(token)
if err != nil { if user != nil && user.Id != 0 {
respondFailure(c, err.Error()) if err := model.DB.Model(user).Update("token", "").Error; err != nil {
return respondFailure(c, err.Error())
return
}
}
} }
respondSuccessMessage(c, "") respondSuccessMessage(c, "")
} }
func ensureUserOpenFlareToken(user *model.User) (string, error) {
if user.Token != "" {
return user.Token, nil
}
for i := 0; i < 3; i++ {
token := strings.Replace(uuid.New().String(), "-", "", -1)
if model.DB.Where("token = ?", token).First(&model.User{}).RowsAffected != 0 {
continue
}
if err := model.DB.Model(user).Update("token", token).Error; err != nil {
return "", err
}
user.Token = token
return token, nil
}
return "", errors.New("生成登录凭证失败,请重试")
}
func currentUserFromOpenFlareToken(c *gin.Context) *model.User {
token := c.GetHeader("OPENFLARE_TOKEN")
if token == "" {
return nil
}
return model.ValidateUserToken(token)
}
func Register(c *gin.Context) { func Register(c *gin.Context) {
respondFailure(c, "非法请求") respondFailure(c, "非法请求")
} }
+60 -60
View File
@@ -19,7 +19,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -94,7 +94,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -122,7 +122,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -203,7 +203,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -292,7 +292,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -361,7 +361,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -419,7 +419,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -667,7 +667,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -700,7 +700,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -728,7 +728,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -753,7 +753,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -778,7 +778,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -821,7 +821,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -846,7 +846,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -871,7 +871,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -896,7 +896,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -937,7 +937,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -978,7 +978,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1010,7 +1010,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1033,7 +1033,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1072,7 +1072,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1106,7 +1106,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1276,7 +1276,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1299,7 +1299,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1345,7 +1345,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1379,7 +1379,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1420,7 +1420,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1473,7 +1473,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1496,7 +1496,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1542,7 +1542,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1567,7 +1567,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1592,7 +1592,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1639,7 +1639,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1680,7 +1680,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1721,7 +1721,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1762,7 +1762,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1815,7 +1815,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1856,7 +1856,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1897,7 +1897,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1970,7 +1970,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2121,7 +2121,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2144,7 +2144,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2190,7 +2190,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2231,7 +2231,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2272,7 +2272,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2405,7 +2405,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2428,7 +2428,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2474,7 +2474,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2520,7 +2520,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2584,7 +2584,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2625,7 +2625,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2666,7 +2666,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2719,7 +2719,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2760,7 +2760,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2801,7 +2801,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2854,7 +2854,7 @@ const docTemplate = `{
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2907,7 +2907,7 @@ const docTemplate = `{
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -3761,10 +3761,10 @@ const docTemplate = `{
"name": "X-Agent-Token", "name": "X-Agent-Token",
"in": "header" "in": "header"
}, },
"BearerAuth": { "OpenFlareTokenAuth": {
"description": "管理端可使用 Bearer Token,例如:Bearer \u003ctoken\u003e", "description": "管理端 API 使用登录后返回的用户 Token",
"type": "apiKey", "type": "apiKey",
"name": "Authorization", "name": "OPENFLARE_TOKEN",
"in": "header" "in": "header"
} }
} }
+60 -60
View File
@@ -16,7 +16,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -91,7 +91,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -119,7 +119,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -200,7 +200,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -289,7 +289,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -358,7 +358,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -416,7 +416,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -664,7 +664,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -697,7 +697,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -725,7 +725,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -750,7 +750,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -775,7 +775,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -818,7 +818,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -843,7 +843,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -868,7 +868,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -893,7 +893,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -934,7 +934,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -975,7 +975,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1007,7 +1007,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1030,7 +1030,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1069,7 +1069,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1103,7 +1103,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1273,7 +1273,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1296,7 +1296,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1342,7 +1342,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1376,7 +1376,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1417,7 +1417,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1470,7 +1470,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1493,7 +1493,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1539,7 +1539,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1564,7 +1564,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1589,7 +1589,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1636,7 +1636,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1677,7 +1677,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1718,7 +1718,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1759,7 +1759,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1812,7 +1812,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1853,7 +1853,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -1894,7 +1894,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -1967,7 +1967,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2118,7 +2118,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2141,7 +2141,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2187,7 +2187,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2228,7 +2228,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2269,7 +2269,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2402,7 +2402,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2425,7 +2425,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2471,7 +2471,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2517,7 +2517,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2581,7 +2581,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2622,7 +2622,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2663,7 +2663,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2716,7 +2716,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2757,7 +2757,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -2798,7 +2798,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2851,7 +2851,7 @@
"post": { "post": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"consumes": [ "consumes": [
@@ -2904,7 +2904,7 @@
"get": { "get": {
"security": [ "security": [
{ {
"BearerAuth": [] "OpenFlareTokenAuth": []
} }
], ],
"produces": [ "produces": [
@@ -3758,10 +3758,10 @@
"name": "X-Agent-Token", "name": "X-Agent-Token",
"in": "header" "in": "header"
}, },
"BearerAuth": { "OpenFlareTokenAuth": {
"description": "管理端可使用 Bearer Token,例如:Bearer \u003ctoken\u003e", "description": "管理端 API 使用登录后返回的用户 Token",
"type": "apiKey", "type": "apiKey",
"name": "Authorization", "name": "OPENFLARE_TOKEN",
"in": "header" "in": "header"
} }
} }
+60 -60
View File
@@ -541,7 +541,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List access logs summary: List access logs
tags: tags:
- AccessLogs - AccessLogs
@@ -558,7 +558,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Cleanup access logs by retention days summary: Cleanup access logs by retention days
tags: tags:
- AccessLogs - AccessLogs
@@ -610,7 +610,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List folded access logs summary: List folded access logs
tags: tags:
- AccessLogs - AccessLogs
@@ -668,7 +668,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List folded access log IP summaries summary: List folded access log IP summaries
tags: tags:
- AccessLogs - AccessLogs
@@ -712,7 +712,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List access log IP summaries summary: List access log IP summaries
tags: tags:
- AccessLogs - AccessLogs
@@ -749,7 +749,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get access log IP trend summary: Get access log IP trend
tags: tags:
- AccessLogs - AccessLogs
@@ -764,7 +764,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get default ACME account summary: Get default ACME account
tags: tags:
- AcmeAccounts - AcmeAccounts
@@ -923,7 +923,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List apply logs summary: List apply logs
tags: tags:
- ApplyLogs - ApplyLogs
@@ -940,7 +940,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Cleanup apply logs summary: Cleanup apply logs
tags: tags:
- ApplyLogs - ApplyLogs
@@ -955,7 +955,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List config versions summary: List config versions
tags: tags:
- ConfigVersions - ConfigVersions
@@ -981,7 +981,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get config version detail summary: Get config version detail
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1007,7 +1007,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Activate an existing config version summary: Activate an existing config version
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1022,7 +1022,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get active config version summary: Get active config version
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1049,7 +1049,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Cleanup old config versions summary: Cleanup old config versions
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1064,7 +1064,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Diff current draft against active version summary: Diff current draft against active version
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1079,7 +1079,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Preview config rendering summary: Preview config rendering
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1094,7 +1094,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Publish a new config version summary: Publish a new config version
tags: tags:
- ConfigVersions - ConfigVersions
@@ -1114,7 +1114,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get dashboard overview summary: Get dashboard overview
tags: tags:
- Dashboard - Dashboard
@@ -1129,7 +1129,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List DNS accounts summary: List DNS accounts
tags: tags:
- DnsAccounts - DnsAccounts
@@ -1152,7 +1152,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Create DNS account summary: Create DNS account
tags: tags:
- DnsAccounts - DnsAccounts
@@ -1173,7 +1173,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Delete DNS account summary: Delete DNS account
tags: tags:
- DnsAccounts - DnsAccounts
@@ -1202,7 +1202,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update DNS account summary: Update DNS account
tags: tags:
- DnsAccounts - DnsAccounts
@@ -1293,7 +1293,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List managed domains summary: List managed domains
tags: tags:
- ManagedDomains - ManagedDomains
@@ -1321,7 +1321,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Create managed domain summary: Create managed domain
tags: tags:
- ManagedDomains - ManagedDomains
@@ -1347,7 +1347,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Delete managed domain summary: Delete managed domain
tags: tags:
- ManagedDomains - ManagedDomains
@@ -1381,7 +1381,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update managed domain summary: Update managed domain
tags: tags:
- ManagedDomains - ManagedDomains
@@ -1402,7 +1402,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Match certificate for domain summary: Match certificate for domain
tags: tags:
- ManagedDomains - ManagedDomains
@@ -1417,7 +1417,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List nodes summary: List nodes
tags: tags:
- Nodes - Nodes
@@ -1445,7 +1445,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Create node summary: Create node
tags: tags:
- Nodes - Nodes
@@ -1475,7 +1475,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Check latest agent release for node summary: Check latest agent release for node
tags: tags:
- Nodes - Nodes
@@ -1501,7 +1501,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Request agent self-update on node summary: Request agent self-update on node
tags: tags:
- Nodes - Nodes
@@ -1527,7 +1527,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Delete node summary: Delete node
tags: tags:
- Nodes - Nodes
@@ -1553,7 +1553,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Request force sync config on node summary: Request force sync config on node
tags: tags:
- Nodes - Nodes
@@ -1587,7 +1587,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get node observability details summary: Get node observability details
tags: tags:
- Nodes - Nodes
@@ -1613,7 +1613,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Cleanup node health events summary: Cleanup node health events
tags: tags:
- Nodes - Nodes
@@ -1639,7 +1639,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Request openresty restart on node summary: Request openresty restart on node
tags: tags:
- Nodes - Nodes
@@ -1673,7 +1673,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update node summary: Update node
tags: tags:
- Nodes - Nodes
@@ -1688,7 +1688,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get global discovery token summary: Get global discovery token
tags: tags:
- Nodes - Nodes
@@ -1703,7 +1703,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Rotate global discovery token summary: Rotate global discovery token
tags: tags:
- Nodes - Nodes
@@ -1733,7 +1733,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Cleanup observability tables summary: Cleanup observability tables
tags: tags:
- Options - Options
@@ -1829,7 +1829,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List proxy routes summary: List proxy routes
tags: tags:
- ProxyRoutes - ProxyRoutes
@@ -1857,7 +1857,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Create proxy route summary: Create proxy route
tags: tags:
- ProxyRoutes - ProxyRoutes
@@ -1883,7 +1883,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get proxy route detail summary: Get proxy route detail
tags: tags:
- ProxyRoutes - ProxyRoutes
@@ -1909,7 +1909,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Delete proxy route summary: Delete proxy route
tags: tags:
- ProxyRoutes - ProxyRoutes
@@ -1943,7 +1943,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update proxy route summary: Update proxy route
tags: tags:
- ProxyRoutes - ProxyRoutes
@@ -2008,7 +2008,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: List TLS certificates summary: List TLS certificates
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2036,7 +2036,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Create TLS certificate from PEM summary: Create TLS certificate from PEM
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2062,7 +2062,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get TLS certificate detail summary: Get TLS certificate detail
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2088,7 +2088,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get TLS certificate PEM content summary: Get TLS certificate PEM content
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2122,7 +2122,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Convert uploaded TLS certificate to ACME managed certificate summary: Convert uploaded TLS certificate to ACME managed certificate
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2148,7 +2148,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Delete TLS certificate summary: Delete TLS certificate
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2174,7 +2174,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Renew TLS certificate summary: Renew TLS certificate
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2208,7 +2208,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update TLS certificate from PEM summary: Update TLS certificate from PEM
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2242,7 +2242,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Update ACME TLS certificate summary: Update ACME TLS certificate
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2271,7 +2271,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Apply TLS certificate via ACME summary: Apply TLS certificate via ACME
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2313,7 +2313,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Import TLS certificate from files summary: Import TLS certificate from files
tags: tags:
- TLSCertificates - TLSCertificates
@@ -2328,7 +2328,7 @@ paths:
additionalProperties: true additionalProperties: true
type: object type: object
security: security:
- BearerAuth: [] - OpenFlareTokenAuth: []
summary: Get latest GitHub release summary: Get latest GitHub release
tags: tags:
- Update - Update
@@ -2390,9 +2390,9 @@ securityDefinitions:
in: header in: header
name: X-Agent-Token name: X-Agent-Token
type: apiKey type: apiKey
BearerAuth: OpenFlareTokenAuth:
description: 管理端可使用 Bearer Token,例如:Bearer <token> description: 管理端 API 使用登录后返回的用户 Token
in: header in: header
name: Authorization name: OPENFLARE_TOKEN
type: apiKey type: apiKey
swagger: "2.0" swagger: "2.0"
+3 -3
View File
@@ -33,10 +33,10 @@ var indexPage []byte
// @description OpenFlare Server 管理端与 Agent API 文档。 // @description OpenFlare Server 管理端与 Agent API 文档。
// @BasePath / // @BasePath /
// @schemes http https // @schemes http https
// @securityDefinitions.apikey BearerAuth // @securityDefinitions.apikey OpenFlareTokenAuth
// @in header // @in header
// @name Authorization // @name OPENFLARE_TOKEN
// @description 管理端可使用 Bearer Token,例如:Bearer <token> // @description 管理端 API 使用登录后返回的用户 Token
// @securityDefinitions.apikey AccessTokenAuth // @securityDefinitions.apikey AccessTokenAuth
// @in header // @in header
// @name X-Agent-Token // @name X-Agent-Token
+28 -60
View File
@@ -1,49 +1,35 @@
package middleware package middleware
import ( import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"openflare/common" "openflare/common"
"openflare/model" "openflare/model"
) )
const OpenFlareTokenHeader = "OPENFLARE_TOKEN"
func authHelper(c *gin.Context, minRole int) { func authHelper(c *gin.Context, minRole int) {
session := sessions.Default(c) token := c.GetHeader(OpenFlareTokenHeader)
username := session.Get("username") if token == "" {
role := session.Get("role") c.JSON(http.StatusUnauthorized, gin.H{
id := session.Get("id") "success": false,
status := session.Get("status") "message": "无权进行此操作,未登录或 token 无效",
authByToken := false })
if username == nil { c.Abort()
// Check token return
token := c.Request.Header.Get("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "无权进行此操作,未登录或 token 无效",
})
c.Abort()
return
}
user := model.ValidateUserToken(token)
if user != nil && user.Username != "" {
// Token is valid
username = user.Username
role = user.Role
id = user.Id
status = user.Status
} else {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无权进行此操作,token 无效",
})
c.Abort()
return
}
authByToken = true
} }
if status.(int) == common.UserStatusDisabled {
user := model.ValidateUserToken(token)
if user == nil || user.Username == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "无权进行此操作,token 无效",
})
c.Abort()
return
}
if user.Status == common.UserStatusDisabled {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "用户已被封禁", "message": "用户已被封禁",
@@ -51,7 +37,7 @@ func authHelper(c *gin.Context, minRole int) {
c.Abort() c.Abort()
return return
} }
if role.(int) < minRole { if user.Role < minRole {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "无权进行此操作,权限不足", "message": "无权进行此操作,权限不足",
@@ -59,10 +45,10 @@ func authHelper(c *gin.Context, minRole int) {
c.Abort() c.Abort()
return return
} }
c.Set("username", username) c.Set("username", user.Username)
c.Set("role", role) c.Set("role", user.Role)
c.Set("id", id) c.Set("id", user.Id)
c.Set("authByToken", authByToken) c.Set("authByToken", true)
c.Next() c.Next()
} }
@@ -84,34 +70,16 @@ func RootAuth() func(c *gin.Context) {
} }
} }
// NoTokenAuth You should always use this after normal auth middlewares. // NoTokenAuth is kept as a compatibility no-op because admin APIs now always use OPENFLARE_TOKEN.
func NoTokenAuth() func(c *gin.Context) { func NoTokenAuth() func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
authByToken := c.GetBool("authByToken")
if authByToken {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "本接口不支持使用 token 进行验证",
})
c.Abort()
return
}
c.Next() c.Next()
} }
} }
// TokenOnlyAuth You should always use this after normal auth middlewares. // TokenOnlyAuth is kept as a compatibility no-op because admin APIs now always use OPENFLARE_TOKEN.
func TokenOnlyAuth() func(c *gin.Context) { func TokenOnlyAuth() func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
authByToken := c.GetBool("authByToken")
if !authByToken {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "本接口仅支持使用 token 进行验证",
})
c.Abort()
return
}
c.Next() c.Next()
} }
} }
+1 -1
View File
@@ -11,7 +11,7 @@ import (
func CORS() gin.HandlerFunc { func CORS() gin.HandlerFunc {
config := cors.DefaultConfig() config := cors.DefaultConfig()
config.AllowCredentials = true config.AllowCredentials = true
config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Agent-Token", "Accept"} config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "OPENFLARE_TOKEN", "X-Agent-Token", "Accept"}
config.AllowOriginFunc = func(origin string) bool { config.AllowOriginFunc = func(origin string) bool {
serverAddr := strings.TrimRight(common.ServerAddress, "/") serverAddr := strings.TrimRight(common.ServerAddress, "/")
if serverAddr == "" { if serverAddr == "" {
+4 -4
View File
@@ -95,7 +95,7 @@ func TestPhase1PublishLifecycle(t *testing.T) {
} }
repeatPublishReq := httptest.NewRequest(http.MethodPost, "/api/config-versions/publish", nil) repeatPublishReq := httptest.NewRequest(http.MethodPost, "/api/config-versions/publish", nil)
repeatPublishReq.Header.Set("Authorization", "Bearer "+token) repeatPublishReq.Header.Set("OPENFLARE_TOKEN", token)
repeatPublishRecorder := httptest.NewRecorder() repeatPublishRecorder := httptest.NewRecorder()
engine.ServeHTTP(repeatPublishRecorder, repeatPublishReq) engine.ServeHTTP(repeatPublishRecorder, repeatPublishReq)
if repeatPublishRecorder.Code != http.StatusOK { if repeatPublishRecorder.Code != http.StatusOK {
@@ -519,7 +519,7 @@ func performJSONRequest(t *testing.T, engine http.Handler, token string, method
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("OPENFLARE_TOKEN", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK { if recorder.Code != http.StatusOK {
@@ -549,7 +549,7 @@ func performJSONRequestNoFatal(t *testing.T, engine http.Handler, token string,
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("OPENFLARE_TOKEN", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK && recorder.Code != http.StatusBadRequest { if recorder.Code != http.StatusOK && recorder.Code != http.StatusBadRequest {
@@ -596,7 +596,7 @@ func performMultipartRequest(t *testing.T, engine http.Handler, token string, pa
} }
req := httptest.NewRequest(http.MethodPost, path, &body) req := httptest.NewRequest(http.MethodPost, path, &body)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("OPENFLARE_TOKEN", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK { if recorder.Code != http.StatusOK {
+11 -10
View File
@@ -127,7 +127,7 @@ func TestPhase2BatchOptionUpdateIsAtomic(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/option/update-batch", bytes.NewReader(payload)) req := httptest.NewRequest(http.MethodPost, "/api/option/update-batch", bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.AddCookie(loginCookie) req.Header.Set("OPENFLARE_TOKEN", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -320,7 +320,7 @@ func TestExternalAccountBindingsCanBeListedAndDeleted(t *testing.T) {
} }
} }
func loginAsRoot(t *testing.T, engine http.Handler) *http.Cookie { func loginAsRoot(t *testing.T, engine http.Handler) string {
t.Helper() t.Helper()
payload, err := json.Marshal(map[string]any{ payload, err := json.Marshal(map[string]any{
"username": "root", "username": "root",
@@ -346,16 +346,17 @@ func loginAsRoot(t *testing.T, engine http.Handler) *http.Cookie {
t.Fatalf("root login failed: %s", resp.Message) t.Fatalf("root login failed: %s", resp.Message)
} }
for _, cookie := range recorder.Result().Cookies() { var user model.User
if cookie.Name == "session" { if err = json.Unmarshal(resp.Data, &user); err != nil {
return cookie t.Fatalf("failed to decode login user: %v", err)
}
} }
t.Fatal("expected session cookie after root login") if user.Token == "" {
return nil t.Fatal("expected OPENFLARE_TOKEN after root login")
}
return user.Token
} }
func performSessionJSONRequest(t *testing.T, engine http.Handler, sessionCookie *http.Cookie, method string, path string, body any) apiResponse { func performSessionJSONRequest(t *testing.T, engine http.Handler, token string, method string, path string, body any) apiResponse {
t.Helper() t.Helper()
var payload []byte var payload []byte
var err error var err error
@@ -370,7 +371,7 @@ func performSessionJSONRequest(t *testing.T, engine http.Handler, sessionCookie
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.AddCookie(sessionCookie) req.Header.Set("OPENFLARE_TOKEN", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -133,7 +133,7 @@ func TestUptimeKumaSyncDisabled(t *testing.T) {
// Request sync, should fail // Request sync, should fail
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.AddCookie(loginCookie) req.Header.Set("OPENFLARE_TOKEN", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -265,7 +265,7 @@ func TestUptimeKumaSyncSuccess(t *testing.T) {
loginCookie := loginAsRoot(t, engine) loginCookie := loginAsRoot(t, engine)
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.AddCookie(loginCookie) req.Header.Set("OPENFLARE_TOKEN", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -405,7 +405,7 @@ func TestUptimeKumaSyncSelectedScope(t *testing.T) {
loginCookie := loginAsRoot(t, engine) loginCookie := loginAsRoot(t, engine)
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.AddCookie(loginCookie) req.Header.Set("OPENFLARE_TOKEN", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
+34 -20
View File
@@ -77,13 +77,22 @@ func TestLatestReleaseProxy(t *testing.T) {
if loginRecorder.Code != http.StatusOK { if loginRecorder.Code != http.StatusOK {
t.Fatalf("unexpected login status code: %d", loginRecorder.Code) t.Fatalf("unexpected login status code: %d", loginRecorder.Code)
} }
loginResult := loginRecorder.Result() var loginResp apiResponse
defer loginResult.Body.Close() if err = json.Unmarshal(loginRecorder.Body.Bytes(), &loginResp); err != nil {
t.Fatalf("failed to decode login response: %v", err)
}
var loginUser struct {
Token string `json:"token"`
}
if err = json.Unmarshal(loginResp.Data, &loginUser); err != nil {
t.Fatalf("failed to decode login user: %v", err)
}
if loginUser.Token == "" {
t.Fatal("expected OPENFLARE_TOKEN after login")
}
req := httptest.NewRequest(http.MethodGet, "/api/update/latest-release", nil) req := httptest.NewRequest(http.MethodGet, "/api/update/latest-release", nil)
for _, cookieValue := range loginResult.Cookies() { req.Header.Set("OPENFLARE_TOKEN", loginUser.Token)
req.AddCookie(cookieValue)
}
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -111,7 +120,7 @@ func TestLatestReleaseProxy(t *testing.T) {
} }
} }
func loginRootAndBuildEngine(t *testing.T) (*gin.Engine, []*http.Cookie) { func loginRootAndBuildEngine(t *testing.T) (*gin.Engine, string) {
t.Helper() t.Helper()
gin.SetMode(gin.TestMode) gin.SetMode(gin.TestMode)
common.RedisEnabled = false common.RedisEnabled = false
@@ -135,10 +144,21 @@ func loginRootAndBuildEngine(t *testing.T) (*gin.Engine, []*http.Cookie) {
if loginRecorder.Code != http.StatusOK { if loginRecorder.Code != http.StatusOK {
t.Fatalf("unexpected login status code: %d", loginRecorder.Code) t.Fatalf("unexpected login status code: %d", loginRecorder.Code)
} }
loginResult := loginRecorder.Result() var loginResp apiResponse
defer loginResult.Body.Close() if err = json.Unmarshal(loginRecorder.Body.Bytes(), &loginResp); err != nil {
t.Fatalf("failed to decode login response: %v", err)
}
var loginUser struct {
Token string `json:"token"`
}
if err = json.Unmarshal(loginResp.Data, &loginUser); err != nil {
t.Fatalf("failed to decode login user: %v", err)
}
if loginUser.Token == "" {
t.Fatal("expected OPENFLARE_TOKEN after login")
}
return engine, loginResult.Cookies() return engine, loginUser.Token
} }
func fakeManualServerBinary(version string) (string, []byte) { func fakeManualServerBinary(version string) (string, []byte) {
@@ -157,7 +177,7 @@ func TestManualUploadRoute(t *testing.T) {
service.SetServerUpgradeDispatchDelayForTest(500 * time.Millisecond) service.SetServerUpgradeDispatchDelayForTest(500 * time.Millisecond)
}) })
engine, cookies := loginRootAndBuildEngine(t) engine, token := loginRootAndBuildEngine(t)
fileName, content := fakeManualServerBinary("v0.5.0") fileName, content := fakeManualServerBinary("v0.5.0")
body := &bytes.Buffer{} body := &bytes.Buffer{}
@@ -175,9 +195,7 @@ func TestManualUploadRoute(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body) req := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
for _, cookieValue := range cookies { req.Header.Set("OPENFLARE_TOKEN", token)
req.AddCookie(cookieValue)
}
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -225,7 +243,7 @@ func TestManualUpgradeConfirmRoute(t *testing.T) {
service.SetServerUpgradeDispatchDelayForTest(originalDelay) service.SetServerUpgradeDispatchDelayForTest(originalDelay)
}) })
engine, cookies := loginRootAndBuildEngine(t) engine, token := loginRootAndBuildEngine(t)
fileName, content := fakeManualServerBinary("v0.5.0") fileName, content := fakeManualServerBinary("v0.5.0")
body := &bytes.Buffer{} body := &bytes.Buffer{}
@@ -243,9 +261,7 @@ func TestManualUpgradeConfirmRoute(t *testing.T) {
uploadReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body) uploadReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body)
uploadReq.Header.Set("Content-Type", writer.FormDataContentType()) uploadReq.Header.Set("Content-Type", writer.FormDataContentType())
for _, cookieValue := range cookies { uploadReq.Header.Set("OPENFLARE_TOKEN", token)
uploadReq.AddCookie(cookieValue)
}
uploadRecorder := httptest.NewRecorder() uploadRecorder := httptest.NewRecorder()
engine.ServeHTTP(uploadRecorder, uploadReq) engine.ServeHTTP(uploadRecorder, uploadReq)
@@ -276,9 +292,7 @@ func TestManualUpgradeConfirmRoute(t *testing.T) {
} }
confirmReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upgrade", bytes.NewReader(confirmBody)) confirmReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upgrade", bytes.NewReader(confirmBody))
confirmReq.Header.Set("Content-Type", "application/json") confirmReq.Header.Set("Content-Type", "application/json")
for _, cookieValue := range cookies { confirmReq.Header.Set("OPENFLARE_TOKEN", token)
confirmReq.AddCookie(cookieValue)
}
confirmRecorder := httptest.NewRecorder() confirmRecorder := httptest.NewRecorder()
engine.ServeHTTP(confirmRecorder, confirmReq) engine.ServeHTTP(confirmRecorder, confirmReq)
@@ -14,6 +14,7 @@ import {
logout as logoutRequest, logout as logoutRequest,
getCurrentUser, getCurrentUser,
} from '@/features/auth/api/auth'; } from '@/features/auth/api/auth';
import { clearStoredOpenFlareToken } from '@/lib/api/auth-token';
import type { AuthUser } from '@/types/auth'; import type { AuthUser } from '@/types/auth';
interface AuthContextValue { interface AuthContextValue {
@@ -41,6 +42,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
setUserState(nextUser); setUserState(nextUser);
return nextUser; return nextUser;
} catch { } catch {
clearStoredOpenFlareToken();
setUserState(null); setUserState(null);
return null; return null;
} finally { } finally {
+31 -3
View File
@@ -1,4 +1,8 @@
import { apiRequest } from '@/lib/api/client'; import { apiRequest } from '@/lib/api/client';
import {
clearStoredOpenFlareToken,
setStoredOpenFlareToken,
} from '@/lib/api/auth-token';
import type { import type {
AuthUser, AuthUser,
LoginPayload, LoginPayload,
@@ -14,11 +18,18 @@ export function login(payload: LoginPayload) {
return apiRequest<AuthUser>('/user/login', { return apiRequest<AuthUser>('/user/login', {
method: 'POST', method: 'POST',
body: JSON.stringify(payload), body: JSON.stringify(payload),
}).then((user) => {
if (user.token) {
setStoredOpenFlareToken(user.token);
}
return user;
}); });
} }
export function logout() { export function logout() {
return apiRequest<void>('/user/logout'); return apiRequest<void>('/user/logout').finally(() => {
clearStoredOpenFlareToken();
});
} }
export function register(payload: RegisterPayload) { export function register(payload: RegisterPayload) {
@@ -48,7 +59,14 @@ export function resetPassword(payload: PasswordResetRequestPayload) {
} }
export function exchangeGitHubCode(code: string) { export function exchangeGitHubCode(code: string) {
return apiRequest<AuthUser>(`/oauth/github?code=${encodeURIComponent(code)}`); return apiRequest<AuthUser>(
`/oauth/github?code=${encodeURIComponent(code)}`,
).then((user) => {
if (user.token) {
setStoredOpenFlareToken(user.token);
}
return user;
});
} }
export interface OAuthAuthorizeResult { export interface OAuthAuthorizeResult {
@@ -79,12 +97,22 @@ export function exchangeOAuthCode(
const searchParams = new URLSearchParams({ code, state }); const searchParams = new URLSearchParams({ code, state });
return apiRequest<OAuthCallbackResult>( return apiRequest<OAuthCallbackResult>(
`/oauth/${encodeURIComponent(String(source))}/callback?${searchParams.toString()}`, `/oauth/${encodeURIComponent(String(source))}/callback?${searchParams.toString()}`,
); ).then((result) => {
if (result.user?.token) {
setStoredOpenFlareToken(result.user.token);
}
return result;
});
} }
export function linkExistingOAuthAccount(payload: LinkExistingOAuthPayload) { export function linkExistingOAuthAccount(payload: LinkExistingOAuthPayload) {
return apiRequest<OAuthCallbackResult>('/oauth/link-existing', { return apiRequest<OAuthCallbackResult>('/oauth/link-existing', {
method: 'POST', method: 'POST',
body: JSON.stringify(payload), body: JSON.stringify(payload),
}).then((result) => {
if (result.user?.token) {
setStoredOpenFlareToken(result.user.token);
}
return result;
}); });
} }
@@ -1,4 +1,5 @@
import { apiRequest } from '@/lib/api/client'; import { apiRequest } from '@/lib/api/client';
import { setStoredOpenFlareToken } from '@/lib/api/auth-token';
import type { import type {
BootstrapTokenPayload, BootstrapTokenPayload,
@@ -109,7 +110,10 @@ export function updateSelf(payload: UpdateSelfPayload) {
} }
export function generateAccessToken() { export function generateAccessToken() {
return apiRequest<string>('/user/token'); return apiRequest<string>('/user/token').then((token) => {
setStoredOpenFlareToken(token);
return token;
});
} }
export function bindWeChat(code: string) { export function bindWeChat(code: string) {
@@ -0,0 +1,22 @@
const openFlareTokenStorageKey = 'openflare_token';
export function getStoredOpenFlareToken() {
if (typeof window === 'undefined') {
return '';
}
return window.localStorage.getItem(openFlareTokenStorageKey) || '';
}
export function setStoredOpenFlareToken(token: string) {
if (typeof window === 'undefined') {
return;
}
window.localStorage.setItem(openFlareTokenStorageKey, token);
}
export function clearStoredOpenFlareToken() {
if (typeof window === 'undefined') {
return;
}
window.localStorage.removeItem(openFlareTokenStorageKey);
}
+7
View File
@@ -1,4 +1,5 @@
import { publicEnv } from '@/lib/env/public-env'; import { publicEnv } from '@/lib/env/public-env';
import { getStoredOpenFlareToken } from '@/lib/api/auth-token';
import type { ApiEnvelope } from '@/types/api'; import type { ApiEnvelope } from '@/types/api';
export class ApiError extends Error { export class ApiError extends Error {
@@ -23,6 +24,12 @@ export async function apiRequest<T>(path: string, init?: RequestInit) {
if (!(init?.body instanceof FormData) && !headers.has('Content-Type')) { if (!(init?.body instanceof FormData) && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json'); headers.set('Content-Type', 'application/json');
} }
if (!headers.has('OPENFLARE_TOKEN')) {
const token = getStoredOpenFlareToken();
if (token) {
headers.set('OPENFLARE_TOKEN', token);
}
}
const response = await fetch(getApiUrl(path), { const response = await fetch(getApiUrl(path), {
credentials: 'include', credentials: 'include',
+1
View File
@@ -4,6 +4,7 @@ export interface AuthUser {
display_name: string; display_name: string;
role: number; role: number;
status: number; status: number;
token?: string;
email?: string; email?: string;
github_id?: string; github_id?: string;
wechat_id?: string; wechat_id?: string;