From 3d51aa455006903345f554a2dd90034993796114 Mon Sep 17 00:00:00 2001 From: sergei Date: Tue, 14 Apr 2026 06:23:55 +0400 Subject: vpnem: VPN infrastructure with load-balanced multi-protocol nodes - Multi-protocol VPS nodes (VLESS-REALITY + Hysteria2 + SOCKS5) - Smart load balancing via recommendation API - Windows/Linux client (Go + Wails + sing-box) - Server API with RealIP detection and connection tracking - Auto-deployment via vpnui control plane - Silent Windows installer with UAC elevation - Load-based server recommendation (no sticky sessions) - Best Server one-click connection workflow --- internal/control/state.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 internal/control/state.go (limited to 'internal/control/state.go') diff --git a/internal/control/state.go b/internal/control/state.go new file mode 100644 index 0000000..7fc7827 --- /dev/null +++ b/internal/control/state.go @@ -0,0 +1,71 @@ +package control + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" + "sort" + "time" +) + +type NodeState struct { + NodeID string `json:"node_id"` + BootstrapStatus string `json:"bootstrap_status"` + LastBootstrapAt *time.Time `json:"last_bootstrap_at,omitempty"` + LastHealthCheckAt *time.Time `json:"last_health_check_at,omitempty"` + LastDNSSyncAt *time.Time `json:"last_dns_sync_at,omitempty"` + PublicHost string `json:"public_host,omitempty"` + Services []ServiceStatus `json:"services,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` +} + +type ServiceStatus struct { + Type string `json:"type"` + Status string `json:"status"` + Port int `json:"port"` +} + +func LoadNodeState(dir, nodeID string) (*NodeState, error) { + data, err := os.ReadFile(filepath.Join(dir, nodeID+".json")) + if err != nil { + return nil, err + } + + var state NodeState + if err := json.Unmarshal(data, &state); err != nil { + return nil, err + } + return &state, nil +} + +func SaveNodeState(dir string, state NodeState) error { + if err := os.MkdirAll(dir, 0o755); err != nil { + return err + } + + sort.Slice(state.Services, func(i, j int) bool { + return state.Services[i].Type < state.Services[j].Type + }) + + data, err := json.MarshalIndent(state, "", " ") + if err != nil { + return err + } + data = append(data, '\n') + + tmpPath := filepath.Join(dir, state.NodeID+".json.tmp") + finalPath := filepath.Join(dir, state.NodeID+".json") + if err := os.WriteFile(tmpPath, data, 0o600); err != nil { + return err + } + return os.Rename(tmpPath, finalPath) +} + +func DeleteNodeState(dir, nodeID string) error { + err := os.Remove(filepath.Join(dir, nodeID+".json")) + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err +} -- cgit v1.2.3