chore: 统一本地服务器与远程仓库源码

- 合并 main 与 master 历史并以服务器结构为基线
- 保留迁移、数据保留、AWS 账单与部署安全能力
- 建立 SDD 规格和 CI 验证闭环
This commit is contained in:
Logan.Li committed 2026-08-05 20:03:25 +08:00
commit fd60361d93
460 files changed
+37492 -23871

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
package agentauth
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"time"
"ezops/model"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// Issue 为指定主机轮换 Agent Token,旧 Token 会立即失效。 // Logan.Li
func Issue(db *gorm.DB, hostname, issuedBy string) (string, error) {
hostname = strings.TrimSpace(hostname)
if hostname == "" {
return "", fmt.Errorf("hostname is required")
}
prefix, err := randomHex(8)
if err != nil {
return "", err
}
// bcrypt 只处理前 72 字节,Token 总长度控制在 61 字节以内。 // Logan.Li
secret, err := randomHex(20)
if err != nil {
return "", err
}
token := "agt_" + prefix + "." + secret
hash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
if err != nil {
return "", err
}
now := time.Now()
credential := model.AgentCredential{Hostname: hostname}
err = db.Where("hostname = ?", hostname).Assign(map[string]interface{}{
"token_prefix": "agt_" + prefix,
"token_hash": string(hash),
"status": "active",
"issued_by": issuedBy,
"last_used_at": nil,
"expires_at": nil,
"rotated_at": now,
}).FirstOrCreate(&credential).Error
if err != nil {
return "", err
}
return token, nil
}
// Authenticate 校验 Token 并返回绑定的主机名。 // Logan.Li
func Authenticate(db *gorm.DB, token string) (*model.AgentCredential, error) {
parts := strings.SplitN(token, ".", 2)
if len(parts) != 2 || !strings.HasPrefix(parts[0], "agt_") || parts[1] == "" {
return nil, fmt.Errorf("invalid agent token")
}
var credential model.AgentCredential
if err := db.Where("token_prefix = ? AND status = ?", parts[0], "active").Take(&credential).Error; err != nil {
return nil, fmt.Errorf("invalid agent token")
}
if credential.ExpiresAt != nil && credential.ExpiresAt.Before(time.Now()) {
return nil, fmt.Errorf("agent token expired")
}
if bcrypt.CompareHashAndPassword([]byte(credential.TokenHash), []byte(token)) != nil {
return nil, fmt.Errorf("invalid agent token")
}
now := time.Now()
_ = db.Model(&credential).Update("last_used_at", now).Error
credential.LastUsedAt = &now
return &credential, nil
}
func randomHex(bytes int) (string, error) {
buf := make([]byte, bytes)
if _, err := rand.Read(buf); err != nil {
return "", err
}
return hex.EncodeToString(buf), nil
}