package services import ( "context" "fmt" "time" "lokistack/models" "lokistack/pkg/crypto" "lokistack/repository" "lokistack/utils" ) type HostService struct{} func NewHostService() *HostService { return &HostService{} } func (s *HostService) Create(name, hostIP string, port int, user, authType, password, privateKey, keyPhrase, os, arch, labels string) (*models.Host, error) { if port == 0 { port = 22 } host := models.Host{ ID: repository.GetDB().NextID("hosts"), Name: name, HostIP: hostIP, Port: port, User: user, AuthType: authType, OS: os, Arch: arch, Labels: labels, Status: "pending", CreatedAt: time.Now(), UpdatedAt: time.Now(), } if password != "" { enc, err := crypto.EncryptField(password) if err != nil { return nil, fmt.Errorf("密码加密失败: %w", err) } host.Password = enc } if privateKey != "" { enc, err := crypto.EncryptField(privateKey) if err != nil { return nil, fmt.Errorf("私钥加密失败: %w", err) } host.PrivateKey = enc } if keyPhrase != "" { enc, err := crypto.EncryptField(keyPhrase) if err != nil { return nil, fmt.Errorf("私钥口令加密失败: %w", err) } host.KeyPhrase = enc } db := repository.GetDB() db.Lock() db.Hosts = append(db.Hosts, host) db.Unlock() db.RequestSave() go s.Validate(host.ID) return &host, nil } func (s *HostService) Get(id uint) (*models.Host, error) { db := repository.GetDB() db.RLock() defer db.RUnlock() for i := range db.Hosts { if db.Hosts[i].ID == id { h := db.Hosts[i] return &h, nil } } return nil, utils.ErrNotFound } func (s *HostService) List(page, pageSize int) ([]models.Host, int64, error) { db := repository.GetDB() db.RLock() defer db.RUnlock() total := int64(len(db.Hosts)) start := (page - 1) * pageSize if start < 0 || start >= len(db.Hosts) { return []models.Host{}, total, nil } end := start + pageSize if end > len(db.Hosts) { end = len(db.Hosts) } result := make([]models.Host, end-start) copy(result, db.Hosts[start:end]) return result, total, nil } func (s *HostService) Update(id uint, updates map[string]interface{}) error { db := repository.GetDB() db.Lock() defer db.Unlock() for i := range db.Hosts { if db.Hosts[i].ID == id { if v, ok := updates["name"].(string); ok { db.Hosts[i].Name = v } if v, ok := updates["status"].(string); ok { db.Hosts[i].Status = v } if v, ok := updates["statusMessage"].(string); ok { db.Hosts[i].StatusMessage = v } if v, ok := updates["os"].(string); ok { db.Hosts[i].OS = v } if v, ok := updates["arch"].(string); ok { db.Hosts[i].Arch = v } if v, ok := updates["hostIP"].(string); ok && v != "" { db.Hosts[i].HostIP = v } if v, ok := updates["port"].(float64); ok && v > 0 { db.Hosts[i].Port = int(v) } if v, ok := updates["user"].(string); ok && v != "" { db.Hosts[i].User = v } if v, ok := updates["labels"].(string); ok { db.Hosts[i].Labels = v } // 凭证编辑约定:非空才更新(留空=保留旧密文) for _, field := range []string{"password", "privateKey", "keyPhrase"} { if v, ok := updates[field].(string); ok && v != "" { if enc, err := crypto.EncryptField(v); err == nil { switch field { case "password": db.Hosts[i].Password = enc case "privateKey": db.Hosts[i].PrivateKey = enc case "keyPhrase": db.Hosts[i].KeyPhrase = enc } } } } db.Hosts[i].UpdatedAt = time.Now() db.RequestSave() return nil } } return utils.ErrNotFound } func (s *HostService) Delete(id uint) error { db := repository.GetDB() db.Lock() defer db.Unlock() for i, h := range db.Hosts { if h.ID == id { db.Hosts = append(db.Hosts[:i], db.Hosts[i+1:]...) db.RequestSave() return nil } } return utils.ErrNotFound } func (s *HostService) Validate(id uint) { host, err := s.Get(id) if err != nil { return } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() client, err := NewSSHClientWithAuth(HostSSHAuth(host)) if err != nil { s.Update(id, map[string]interface{}{"status": "error", "statusMessage": err.Error()}) return } defer client.Close() if info, err := client.GetHostInfo(ctx); err == nil { s.Update(id, map[string]interface{}{"status": "active", "os": info.OS, "arch": info.Arch}) } else { s.Update(id, map[string]interface{}{"status": "active"}) } }