server: 平台抽象层+bilibili、服务端加密凭据库、角色中间件;web: 精简页面/路由、macOS 客户端(Swift)与多份方案文档;移除误入库的编译产物
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -52,18 +55,45 @@ func (h *AgentHandler) PairCode(c *gin.Context) {
|
||||
// Pair 设备配对(无鉴权,凭一次性码;注册 Ed25519 公钥)
|
||||
func (h *AgentHandler) Pair(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
DeviceName string `json:"deviceName" binding:"required"`
|
||||
Code string `json:"code"`
|
||||
DeviceName string `json:"deviceName"`
|
||||
OS string `json:"os"`
|
||||
Version string `json:"version"`
|
||||
PublicKey string `json:"publicKey" binding:"required"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "参数不合法")
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "请求体必须是 JSON")
|
||||
return
|
||||
}
|
||||
req.Code = strings.ToUpper(strings.TrimSpace(req.Code))
|
||||
req.DeviceName = strings.TrimSpace(req.DeviceName)
|
||||
req.PublicKey = strings.TrimSpace(req.PublicKey)
|
||||
if req.Code == "" {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "连接码不能为空")
|
||||
return
|
||||
}
|
||||
if !validPairCode(req.Code) {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "配对码应为 6 位大写字母或数字")
|
||||
return
|
||||
}
|
||||
if req.PublicKey == "" {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "设备公钥不能为空,请重启客户端后重试")
|
||||
return
|
||||
}
|
||||
publicKey, keyErr := base64.StdEncoding.DecodeString(req.PublicKey)
|
||||
if keyErr != nil || len(publicKey) != ed25519.PublicKeySize {
|
||||
response.Fail(c, http.StatusBadRequest, 1001, "设备公钥格式不合法")
|
||||
return
|
||||
}
|
||||
if req.DeviceName == "" {
|
||||
req.DeviceName = "macOS Agent"
|
||||
}
|
||||
var pc models.PairingCode
|
||||
if err := h.DB.Where("code = ? and used = ?", req.Code, false).First(&pc).Error; err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.Fail(c, http.StatusInternalServerError, 5000, "查询配对码失败")
|
||||
return
|
||||
}
|
||||
response.Fail(c, http.StatusBadRequest, 2006, "配对码无效")
|
||||
return
|
||||
}
|
||||
@@ -84,7 +114,7 @@ func (h *AgentHandler) Pair(c *gin.Context) {
|
||||
// 原子占用:仅当仍 unused 且未过期时置 used=true;RowsAffected==1 才视为抢到,
|
||||
// 防止并发用同一码配出多台设备。
|
||||
res := tx.Model(&models.PairingCode{}).
|
||||
Where("id = ? and used = ?", pc.ID, false).
|
||||
Where("id = ? and used = ? and expires_at > ?", pc.ID, false, time.Now()).
|
||||
Update("used", true)
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
@@ -112,6 +142,18 @@ func (h *AgentHandler) Pair(c *gin.Context) {
|
||||
response.OK(c, gin.H{"deviceId": device.ID, "workspaceId": device.WorkspaceID})
|
||||
}
|
||||
|
||||
func validPairCode(code string) bool {
|
||||
if len(code) != 6 {
|
||||
return false
|
||||
}
|
||||
for _, r := range code {
|
||||
if !strings.ContainsRune(pairAlphabet, r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Devices 设备列表(在线状态来自 Hub,此处以 DB 状态兜底)
|
||||
func (h *AgentHandler) Devices(c *gin.Context) {
|
||||
var list []models.AgentDevice
|
||||
|
||||
Reference in New Issue
Block a user