package nodeclient import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" "github.com/orohi/vpn-panel/internal/models" ) type HealthResponse struct { OK bool `json:"ok"` Name string `json:"name"` Version string `json:"version"` Uptime int64 `json:"uptime_sec"` Protocols []string `json:"protocols"` ProtocolsInstalled []string `json:"protocols_installed"` ProtocolsEnabled []string `json:"protocols_enabled"` } func Health(n *models.Node) (*HealthResponse, error) { url := fmt.Sprintf("http://%s:%d/health", n.Host, n.NodePort) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+n.SecretKey) client := &http.Client{Timeout: 8 * time.Second} res, err := client.Do(req) if err != nil { return nil, err } defer res.Body.Close() body, _ := io.ReadAll(res.Body) if res.StatusCode != http.StatusOK { return nil, fmt.Errorf("status %d: %s", res.StatusCode, string(body)) } var hr HealthResponse if err := json.Unmarshal(body, &hr); err != nil { return nil, err } // Fallback: older agents only expose protocols as enabled list. if len(hr.ProtocolsEnabled) == 0 && len(hr.Protocols) > 0 { hr.ProtocolsEnabled = hr.Protocols } if len(hr.ProtocolsInstalled) == 0 && len(hr.ProtocolsEnabled) > 0 { hr.ProtocolsInstalled = append([]string{}, hr.ProtocolsEnabled...) } return &hr, nil } func PushConfig(n *models.Node, cfg map[string]any) error { payload, err := json.Marshal(cfg) if err != nil { return err } url := fmt.Sprintf("http://%s:%d/api/v1/config", n.Host, n.NodePort) req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload)) if err != nil { return err } req.Header.Set("Authorization", "Bearer "+n.SecretKey) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 12 * time.Second} res, err := client.Do(req) if err != nil { return err } defer res.Body.Close() body, _ := io.ReadAll(res.Body) if res.StatusCode >= 300 { return fmt.Errorf("status %d: %s", res.StatusCode, string(body)) } return nil } func FetchLogs(n *models.Node, lines int) (string, error) { if lines <= 0 { lines = 300 } url := fmt.Sprintf("http://%s:%d/api/v1/logs?lines=%d", n.Host, n.NodePort, lines) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return "", err } req.Header.Set("Authorization", "Bearer "+n.SecretKey) client := &http.Client{Timeout: 10 * time.Second} res, err := client.Do(req) if err != nil { return "", err } defer res.Body.Close() body, _ := io.ReadAll(res.Body) if res.StatusCode != http.StatusOK { return "", fmt.Errorf("status %d: %s", res.StatusCode, string(body)) } var payload struct { OK bool `json:"ok"` Logs string `json:"logs"` } if err := json.Unmarshal(body, &payload); err != nil { return string(body), nil } if payload.Logs == "" { return "(empty agent logs)", nil } return payload.Logs, nil }