Initial commit

This commit is contained in:
2026-06-30 15:14:37 +08:00
commit 15dab96872
311 changed files with 95639 additions and 0 deletions
+318
View File
@@ -0,0 +1,318 @@
package service
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"net"
"git.zkcoi.com/zkcoi/meshray/internal/ctr"
"git.zkcoi.com/zkcoi/meshray/internal/model"
"git.zkcoi.com/zkcoi/meshray/internal/store/sqlite"
"git.zkcoi.com/zkcoi/meshray/pkg/idutil"
"go.uber.org/zap"
"gorm.io/gorm"
)
// NetworkService 网络管理服务
type NetworkService struct {
store *sqlite.Store
ctrClient ctr.Client // meshray-ctr 客户端
logger *zap.Logger
}
// NewNetworkService 创建网络服务实例
func NewNetworkService(store *sqlite.Store, ctrClient ctr.Client, logger *zap.Logger) *NetworkService {
return &NetworkService{
store: store,
ctrClient: ctrClient,
logger: logger,
}
}
// ListNetworks 获取网络列表(Service 层方法)
func (s *NetworkService) ListNetworks() ([]model.Network, error) {
var networks []model.Network
err := s.store.DB().Preload("Devices").Find(&networks).Error
return networks, err
}
// GetNetworkByID 根据 ID 获取网络
func (s *NetworkService) GetNetworkByID(id uint64) (*model.Network, error) {
var network model.Network
err := s.store.DB().Preload("Devices").First(&network, id).Error
if err != nil {
return nil, err
}
return &network, nil
}
// CreateNetwork 创建网络
func (s *NetworkService) CreateNetwork(req *model.Network) (*model.Network, error) {
// 验证子网格式
if err := s.validateSubnet(req.SubnetIPv4); err != nil {
return nil, err
}
// 检查网络名称是否重复
var existing model.Network
if err := s.store.DB().Where("name = ?", req.Name).First(&existing).Error; err == nil {
return nil, errors.New("网络名称已存在")
}
// 生成雪花算法 ID
if req.ID == 0 {
gen, err := idutil.GetGenerator()
if err != nil {
return nil, fmt.Errorf("failed to get snowflake generator: %w", err)
}
req.ID, err = gen.NextID()
if err != nil {
return nil, fmt.Errorf("failed to generate network id: %w", err)
}
}
// 计算 listenPort51820 + hash(networkID)(先不保存,用于 ctr 调用)
listenPort := 51820 + hashUint64(req.ID)%1000
// 根据组网模式决定是否启动 Core
// 原生模式:仅创建 WG 设备
// 增强模式:创建 WG 设备 + 启动 Core 实例
if s.ctrClient != nil {
// P2 阶段 - 先调用 meshray-ctr 创建 WireGuard 设备(失败则不回滚数据库)
// 策略:宽松模式 - ctr 失败只记录警告,不影响数据库操作
if err := s.ctrClient.CreateNetwork(req.ID, req.SubnetIPv4, listenPort, req.Mode); err != nil {
// 记录错误但不影响数据库操作(允许降级)
s.logger.Warn("调用 ctr 创建网络失败,将手动启动",
zap.Uint64("network_id", req.ID),
zap.Error(err))
}
}
// 保存到数据库
if err := s.store.DB().Create(req).Error; err != nil {
return nil, err
}
s.logger.Info("网络创建成功",
zap.Uint64("network_id", req.ID),
zap.String("mode", req.Mode))
return req, nil
}
// GetNetwork 获取网络详情
func (s *NetworkService) GetNetwork(id uint64) (*model.Network, error) {
var network model.Network
if err := s.store.DB().First(&network, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("网络不存在")
}
return nil, err
}
return &network, nil
}
// UpdateNetwork 更新网络
func (s *NetworkService) UpdateNetwork(id uint64, updates map[string]interface{}) (*model.Network, error) {
var network model.Network
if err := s.store.DB().First(&network, id).Error; err != nil {
return nil, errors.New("网络不存在")
}
if err := s.store.DB().Model(&network).Updates(updates).Error; err != nil {
return nil, err
}
return &network, nil
}
// DeleteNetwork 删除网络
func (s *NetworkService) DeleteNetwork(id uint64, force bool) error {
// ✅ 使用事务保证数据一致性
tx := s.store.DB().Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
var network model.Network
if err := tx.First(&network, id).Error; err != nil {
tx.Rollback()
return errors.New("网络不存在")
}
// 检查是否有关联的设备
var deviceCount int64
tx.Model(&model.Device{}).Where("network_id = ?", id).Count(&deviceCount)
if deviceCount > 0 {
if !force {
tx.Rollback()
return errors.New("该网络下仍有设备,为避免误操作,请确认后强制删除")
}
// 级联删除所有关联设备
if err := tx.Where("network_id = ?", id).Delete(&model.Device{}).Error; err != nil {
tx.Rollback()
return fmt.Errorf("级联删除设备失败:%w", err)
}
s.logger.Info("级联删除了关联设备", zap.Uint64("network_id", id), zap.Int64("device_count", deviceCount))
}
// 先调用 ctr 删除 WG 设备(如果存在)
if s.ctrClient != nil {
if err := s.ctrClient.DeleteNetwork(id); err != nil {
// 记录错误但不中断删除流程(设备可能已经不存在)
s.logger.Warn("删除 WG 设备失败(可能已不存在)",
zap.Uint64("network_id", id),
zap.Error(err))
// 继续删除数据库记录
}
}
// 再删除数据库记录
if err := tx.Delete(&network, id).Error; err != nil {
tx.Rollback()
return err
}
// 提交事务
if err := tx.Commit().Error; err != nil {
return err
}
return nil
}
// validateSubnet 验证子网格式
func (s *NetworkService) validateSubnet(subnet string) error {
// 只调用一次 ParseCIDR
ip, ipNet, err := net.ParseCIDR(subnet)
if err != nil {
return fmt.Errorf("无效的子网格式:%s", subnet)
}
// 检查是否是有效的 WireGuard 子网(至少 /24 或更小)
ones, bits := ipNet.Mask.Size()
if bits == 32 && ones > 24 {
return errors.New("IPv4 子网掩码不能大于 /24")
}
if bits == 128 && ones > 64 {
return errors.New("IPv6 子网掩码不能大于 /64")
}
// 检查是否是私有地址段
if !isPrivateSubnet(ip, ipNet) {
return errors.New("请使用私有地址段(如 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16")
}
return nil
}
// isPrivateSubnet 检查是否是私有地址段
func isPrivateSubnet(ip net.IP, ipNet *net.IPNet) bool {
// IPv4 私有地址段
privateRanges := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"100.64.0.0/10", // CGNAT
}
for _, privRange := range privateRanges {
_, privNet, _ := net.ParseCIDR(privRange)
if privNet.Contains(ip) {
return true
}
}
return false
}
// generateNetworkSecret 生成 Network Secret
func generateNetworkSecret() string {
// 使用加密安全的随机数生成器
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
// ✅ 随机数生成失败时 panic,而不是返回弱密码
panic(fmt.Sprintf("生成安全随机数失败:%v", err))
}
return hex.EncodeToString(bytes)
}
// hashUint64 计算 uint64 哈希值
func hashUint64(id uint64) int {
h := sha256.Sum256([]byte(fmt.Sprintf("%d", id)))
// 取前 4 字节转换为 int
return int(h[0])<<24 | int(h[1])<<16 | int(h[2])<<8 | int(h[3])
}
// StartNetwork 启动网络
func (s *NetworkService) StartNetwork(id uint64) error {
network, err := s.GetNetworkByID(id)
if err != nil {
return err
}
// 如果已经有 ctr,直接调用 CreateNetwork
if s.ctrClient != nil {
listenPort := 51820 + hashUint64(network.ID)%1000
if err := s.ctrClient.CreateNetwork(network.ID, network.SubnetIPv4, listenPort, network.Mode); err != nil {
s.logger.Error("启动网络失败",
zap.Uint64("network_id", network.ID),
zap.Error(err))
return fmt.Errorf("启动网络失败:%w", err)
}
s.logger.Info("网络已启动",
zap.Uint64("network_id", network.ID),
zap.Int("listen_port", listenPort))
}
return nil
}
// StopNetwork 停止网络
func (s *NetworkService) StopNetwork(id uint64) error {
network, err := s.GetNetworkByID(id)
if err != nil {
return err
}
// 调用 ctr 删除网络(清理 WG 设备)
if s.ctrClient != nil {
if err := s.ctrClient.DeleteNetwork(network.ID); err != nil {
s.logger.Error("停止网络失败",
zap.Uint64("network_id", network.ID),
zap.Error(err))
return fmt.Errorf("停止网络失败:%w", err)
}
s.logger.Info("网络已停止",
zap.Uint64("network_id", network.ID))
}
return nil
}
// SwitchMode 切换组网模式
func (s *NetworkService) SwitchMode(id uint64, meshMode string) error {
network, err := s.GetNetworkByID(id)
if err != nil {
return err
}
// 更新数据库
if err := s.store.DB().Model(network).Update("mode", meshMode).Error; err != nil {
return err
}
s.logger.Info("组网模式已切换",
zap.Uint64("network_id", network.ID),
zap.String("mode", meshMode))
return nil
}