Files
OpenFlare/internal/apps/openflare/relay/routers.go
T
ryan 2f60329886 后端与全仓代码质量清理(golangci 扩展集 · 测试质量 · 并发安全 · 文档同步)
代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。

Experiments: #2-#17, #18, #20, #21, #23
Metric: total_issues 108 → 8 (-92.6%)
2026-08-16 21:24:14 +08:00

75 lines
2.2 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package relay
import (
"net/http"
"github.com/Rain-kl/Wavelet/internal/apps/openflare/apiutil"
ofws "github.com/Rain-kl/Wavelet/internal/apps/openflare/websocket"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/Rain-kl/Wavelet/internal/shared/response"
"github.com/gin-gonic/gin"
)
// PostHeartbeat handles POST /relay/heartbeat.
// @Summary 上报 Relay 心跳
// @Description Relay 节点定期上报运行状态与 frps 观测数据,返回运行时配置
// @Tags openflare-relay
// @Accept json
// @Produce json
// @Security AgentTokenAuth
// @Param body body relay.HeartbeatPayload true "心跳载荷"
// @Success 200 {object} response.Any{data=relay.HeartbeatResponse} "心跳响应"
// @Failure 400 {object} response.Any "参数错误"
// @Failure 401 {object} response.Any "Agent Token 无效"
// @Failure 403 {object} response.Any "节点类型不匹配"
// @Router /api/v1/relay/heartbeat [post]
func PostHeartbeat(c *gin.Context) {
var payload HeartbeatPayload
if !apiutil.BindJSON(c, &payload) {
return
}
payload.IP = resolveReportedNodeIP(payload.IP, c.Request.RemoteAddr)
authNode, ok := c.Get(ctxRelayNodeKey)
if !ok {
response.AbortUnauthorized(c, errAgentTokenInvalid)
return
}
node, ok := authNode.(*model.OpenFlareNode)
if !ok {
response.AbortUnauthorized(c, errAgentTokenInvalid)
return
}
result, err := Heartbeat(c.Request.Context(), node, payload)
if apiutil.AbortBadRequestOnError(c, err) {
return
}
c.JSON(http.StatusOK, response.OK(result))
}
// GetWebSocket handles GET /relay/ws.
// @Summary 升级 Relay WebSocket 连接
// @Description 将已认证的 Relay 连接升级为 WebSocket 长连接,用于配置推送
// @Tags openflare-relay
// @Security AgentTokenAuth
// @Failure 401 {object} response.Any "Agent Token 无效"
// @Failure 403 {object} response.Any "节点类型不匹配"
// @Router /api/v1/relay/ws [get]
func GetWebSocket(c *gin.Context) {
authNode, ok := c.Get(ctxRelayNodeKey)
if !ok {
response.AbortUnauthorized(c, errAgentTokenInvalid)
return
}
node, ok := authNode.(*model.OpenFlareNode)
if !ok {
response.AbortUnauthorized(c, errAgentTokenInvalid)
return
}
ofws.ServeRelay(c, node.NodeID)
}