Initial commit
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.zkcoi.com/zkcoi/meshray/internal/model"
|
||||
sqlite "git.zkcoi.com/zkcoi/meshray/internal/store/sqlite"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MeshSeedService MeshSeed 服务
|
||||
type MeshSeedService struct {
|
||||
store *sqlite.Store
|
||||
logger *zap.Logger
|
||||
signingKey ed25519.PrivateKey // Ed25519 签名密钥
|
||||
issuerNodeID string // 签发节点 ID
|
||||
}
|
||||
|
||||
// NewMeshSeedService 创建 MeshSeed 服务
|
||||
func NewMeshSeedService(store *sqlite.Store, logger *zap.Logger, signingKey ed25519.PrivateKey, issuerNodeID string) *MeshSeedService {
|
||||
return &MeshSeedService{
|
||||
store: store,
|
||||
logger: logger,
|
||||
signingKey: signingKey,
|
||||
issuerNodeID: issuerNodeID,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateMeshSeed 生成 MeshSeed
|
||||
func (s *MeshSeedService) GenerateMeshSeed(networkID uint64, maxUses int, expiresAt time.Time, ddnsEnabled bool) (*model.MeshSeed, error) {
|
||||
// 1. 验证网络是否存在
|
||||
var network model.Network
|
||||
if err := s.store.DB().First(&network, networkID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("网络不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询网络失败:%w", err)
|
||||
}
|
||||
|
||||
// 2. 生成随机 SeedID(16 字节随机数)
|
||||
seedBytes := make([]byte, 16)
|
||||
if _, err := rand.Read(seedBytes); err != nil {
|
||||
return nil, fmt.Errorf("生成随机数失败:%w", err)
|
||||
}
|
||||
seedID := base64.RawURLEncoding.EncodeToString(seedBytes)
|
||||
|
||||
// 3. 构建 JoinToken(包含网络信息)
|
||||
joinTokenData := map[string]interface{}{
|
||||
"seed_id": seedID,
|
||||
"network_id": networkID,
|
||||
"network_name": network.Name,
|
||||
"subnet_ipv4": network.SubnetIPv4,
|
||||
"mode": network.Mode,
|
||||
"ddns_enabled": ddnsEnabled,
|
||||
"expires_at": expiresAt.Unix(),
|
||||
"max_uses": maxUses,
|
||||
}
|
||||
|
||||
// 序列化为 JSON
|
||||
tokenJSON, err := json.Marshal(joinTokenData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化 Token 失败:%w", err)
|
||||
}
|
||||
|
||||
// Base64 编码
|
||||
joinToken := base64.StdEncoding.EncodeToString(tokenJSON)
|
||||
|
||||
// 4. Ed25519 签名
|
||||
signature := ed25519.Sign(s.signingKey, []byte(joinToken))
|
||||
signatureStr := base64.StdEncoding.EncodeToString(signature)
|
||||
|
||||
// 5. 创建 MeshSeed 记录
|
||||
meshSeed := &model.MeshSeed{
|
||||
SeedID: seedID,
|
||||
NetworkID: networkID,
|
||||
JoinToken: joinToken,
|
||||
Signature: signatureStr,
|
||||
IssuerNodeID: s.issuerNodeID,
|
||||
MaxUses: maxUses,
|
||||
UsedCount: 0,
|
||||
ExpiresAt: expiresAt,
|
||||
DDNSEnabled: ddnsEnabled,
|
||||
UpdateVersion: 0,
|
||||
Revoked: false,
|
||||
}
|
||||
|
||||
if err := s.store.DB().Create(meshSeed).Error; err != nil {
|
||||
return nil, fmt.Errorf("创建 MeshSeed 失败:%w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("MeshSeed 已生成",
|
||||
zap.String("seed_id", seedID),
|
||||
zap.Uint64("network_id", networkID),
|
||||
zap.Int("max_uses", maxUses),
|
||||
zap.Time("expires_at", expiresAt))
|
||||
|
||||
return meshSeed, nil
|
||||
}
|
||||
|
||||
// VerifyMeshSeed 验证 MeshSeed
|
||||
func (s *MeshSeedService) VerifyMeshSeed(joinToken, signature string) (*model.MeshSeed, error) {
|
||||
// 1. 解码 JoinToken
|
||||
tokenBytes, err := base64.StdEncoding.DecodeString(joinToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解码 Token 失败:%w", err)
|
||||
}
|
||||
|
||||
// 2. 解码签名
|
||||
sigBytes, err := base64.StdEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解码签名失败:%w", err)
|
||||
}
|
||||
|
||||
// 3. 验证 Ed25519 签名
|
||||
publicKey := s.signingKey.Public()
|
||||
if !ed25519.Verify(publicKey.(ed25519.PublicKey), tokenBytes, sigBytes) {
|
||||
return nil, fmt.Errorf("签名验证失败")
|
||||
}
|
||||
|
||||
// 4. 解析 Token 内容
|
||||
var tokenData map[string]interface{}
|
||||
if err := json.Unmarshal(tokenBytes, &tokenData); err != nil {
|
||||
return nil, fmt.Errorf("解析 Token 失败:%w", err)
|
||||
}
|
||||
|
||||
seedID, ok := tokenData["seed_id"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Token 格式错误")
|
||||
}
|
||||
|
||||
// 5. 查询 MeshSeed
|
||||
var meshSeed model.MeshSeed
|
||||
if err := s.store.DB().Where("seed_id = ?", seedID).First(&meshSeed).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("MeshSeed 不存在")
|
||||
}
|
||||
return nil, fmt.Errorf("查询 MeshSeed 失败:%w", err)
|
||||
}
|
||||
|
||||
// 6. 检查是否被吊销
|
||||
if meshSeed.Revoked {
|
||||
return nil, fmt.Errorf("MeshSeed 已被吊销")
|
||||
}
|
||||
|
||||
// 7. 检查使用次数
|
||||
if meshSeed.UsedCount >= meshSeed.MaxUses {
|
||||
return nil, fmt.Errorf("MeshSeed 使用次数已用尽")
|
||||
}
|
||||
|
||||
// 8. 检查过期时间
|
||||
if time.Now().After(meshSeed.ExpiresAt) {
|
||||
return nil, fmt.Errorf("MeshSeed 已过期")
|
||||
}
|
||||
|
||||
return &meshSeed, nil
|
||||
}
|
||||
|
||||
// IncrementUseCount 增加使用次数
|
||||
func (s *MeshSeedService) IncrementUseCount(seedID string) error {
|
||||
return s.store.DB().Transaction(func(tx *gorm.DB) error {
|
||||
var meshSeed model.MeshSeed
|
||||
if err := tx.Where("seed_id = ?", seedID).First(&meshSeed).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Model(&meshSeed).UpdateColumn("used_count", meshSeed.UsedCount+1).Error
|
||||
})
|
||||
}
|
||||
|
||||
// RevokeMeshSeed 吊销 MeshSeed
|
||||
func (s *MeshSeedService) RevokeMeshSeed(seedID string) error {
|
||||
result := s.store.DB().Model(&model.MeshSeed{}).
|
||||
Where("seed_id = ?", seedID).
|
||||
Update("revoked", true)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("吊销 MeshSeed 失败:%w", result.Error)
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("MeshSeed 不存在")
|
||||
}
|
||||
|
||||
s.logger.Info("MeshSeed 已吊销", zap.String("seed_id", seedID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListMeshSeeds 获取网络的 MeshSeed 列表
|
||||
func (s *MeshSeedService) ListMeshSeeds(networkID uint) ([]model.MeshSeed, error) {
|
||||
var seeds []model.MeshSeed
|
||||
err := s.store.DB().Where("network_id = ? AND revoked = ?", networkID, false).
|
||||
Order("created_at DESC").
|
||||
Find(&seeds).Error
|
||||
|
||||
return seeds, err
|
||||
}
|
||||
Reference in New Issue
Block a user