summaryrefslogtreecommitdiff
path: root/internal/models
diff options
context:
space:
mode:
authorsergei <sergei@em-sysadmin.xyz>2026-04-14 06:23:55 +0400
committersergei <sergei@em-sysadmin.xyz>2026-04-14 06:23:55 +0400
commit3d51aa455006903345f554a2dd90034993796114 (patch)
tree62a7be2faf047f5eb7886feebc3b815556f03d7f /internal/models
downloadvpnem-main.tar.gz
vpnem-main.tar.bz2
vpnem-main.zip
vpnem: VPN infrastructure with load-balanced multi-protocol nodesHEADmain
- 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
Diffstat (limited to 'internal/models')
-rw-r--r--internal/models/catalog.go35
-rw-r--r--internal/models/client.go59
-rw-r--r--internal/models/policy.go23
-rw-r--r--internal/models/ruleset.go23
-rw-r--r--internal/models/server.go46
5 files changed, 186 insertions, 0 deletions
diff --git a/internal/models/catalog.go b/internal/models/catalog.go
new file mode 100644
index 0000000..eda61cd
--- /dev/null
+++ b/internal/models/catalog.go
@@ -0,0 +1,35 @@
+package models
+
+type CatalogV2 struct {
+ Version string `json:"version"`
+ Nodes []CatalogNode `json:"nodes"`
+}
+
+type CatalogNode struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Provider string `json:"provider,omitempty"`
+ Region string `json:"region"`
+ Host string `json:"host"`
+ Domain string `json:"domain,omitempty"`
+ PublicHost string `json:"public_host"`
+ Protocols []CatalogProtocol `json:"protocols"`
+ Status string `json:"status,omitempty"`
+ Tags []string `json:"tags,omitempty"`
+ Metadata map[string]any `json:"metadata,omitempty"`
+}
+
+type CatalogProtocol struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+ Port int `json:"port"`
+ TLS *TLS `json:"tls,omitempty"`
+ Auth *CatalogAuth `json:"auth,omitempty"`
+ Extra map[string]any `json:"extra,omitempty"`
+}
+
+type CatalogAuth struct {
+ UUID string `json:"uuid,omitempty"`
+ Method string `json:"method,omitempty"`
+ Password string `json:"password,omitempty"`
+}
diff --git a/internal/models/client.go b/internal/models/client.go
new file mode 100644
index 0000000..f086594
--- /dev/null
+++ b/internal/models/client.go
@@ -0,0 +1,59 @@
+package models
+
+import "time"
+
+// ActiveSession tracks a currently active VPN connection.
+type ActiveSession struct {
+ ClientIP string `json:"client_ip"` // real public IP of client (from X-Forwarded-For)
+ ServerIP string `json:"server_ip"` // VPN server IP they connected to
+ NodeID string `json:"node_id"` // catalog node ID
+ OS string `json:"os"`
+ Version string `json:"version"`
+ ConnectedAt time.Time `json:"connected_at"`
+ LastHeartbeat time.Time `json:"last_seen"`
+}
+
+// StudioRecord tracks a studio's home server assignment.
+// A studio = all clients sharing the same public IP.
+type StudioRecord struct {
+ ClientIP string `json:"client_ip"` // public IP = studio identifier
+ HomeServerIP string `json:"home_server_ip"` // assigned "home" server
+ HomeNodeID string `json:"home_node_id"`
+ HomeAssignedAt time.Time `json:"home_assigned_at"` // when home was assigned
+ TotalClients int `json:"total_clients"` // lifetime client count from this studio
+ LastSeen time.Time `json:"last_seen"`
+}
+
+// ConnectRequest is sent when a client connects.
+// Server determines client_ip from X-Forwarded-For — no client_ip field needed.
+type ConnectRequest struct {
+ ServerIP string `json:"server_ip"`
+ NodeID string `json:"node_id"`
+ OS string `json:"os"`
+ Version string `json:"version"`
+}
+
+// DisconnectRequest is sent when a client disconnects.
+type DisconnectRequest struct {
+ ServerIP string `json:"server_ip"`
+ NodeID string `json:"node_id"`
+}
+
+// RecommendationResponse is returned by the recommendation endpoint.
+type RecommendationResponse struct {
+ RecommendedServerIP string `json:"recommended_server_ip"`
+ RecommendedNodeID string `json:"recommended_node_id"`
+ RecommendedTag string `json:"recommended_tag,omitempty"`
+ Reason string `json:"reason"`
+ IsRebalance bool `json:"is_rebalance"` // true if recommending different server than home
+ LoadInfo string `json:"load_info"` // human-readable load summary
+ StudioClients int `json:"studio_clients"` // active clients from same studio
+}
+
+// ServerLoadInfo contains load data for all servers.
+type ServerLoadInfo struct {
+ ServerIP string `json:"server_ip"`
+ ActiveClients int `json:"active_clients"`
+ LoadPercent int `json:"load_percent"` // 0-100
+ MaxCapacity int `json:"max_capacity"`
+}
diff --git a/internal/models/policy.go b/internal/models/policy.go
new file mode 100644
index 0000000..09e15e1
--- /dev/null
+++ b/internal/models/policy.go
@@ -0,0 +1,23 @@
+package models
+
+type RoutingPolicy struct {
+ Version string `json:"version"`
+ AlwaysDirectProcesses []string `json:"always_direct_processes,omitempty"`
+ PreferDirectProcesses []string `json:"prefer_direct_processes,omitempty"`
+ ProxyableBrowserProcesses []string `json:"proxyable_browser_processes,omitempty"`
+ LovenseProcessRegex []string `json:"lovense_process_regex,omitempty"`
+ StaticBypassIPs []string `json:"static_bypass_ips,omitempty"`
+ ReservedCIDRs []string `json:"reserved_cidrs,omitempty"`
+ LocalDomainSuffixes []string `json:"local_domain_suffixes,omitempty"`
+ WindowsNCSIDomains []string `json:"windows_ncsi_domains,omitempty"`
+ InfraBypassDomains []string `json:"infra_bypass_domains,omitempty"`
+ ForcedProxyIPs []string `json:"forced_proxy_ips,omitempty"`
+ TelegramProcesses []string `json:"telegram_processes,omitempty"`
+ TelegramProcessRegex []string `json:"telegram_process_regex,omitempty"`
+ TelegramDomains []string `json:"telegram_domains,omitempty"`
+ TelegramDomainRegex []string `json:"telegram_domain_regex,omitempty"`
+ TelegramIPs []string `json:"telegram_ips,omitempty"`
+ BlockedDomains []string `json:"blocked_domains,omitempty"`
+ ProxyDNSDomains []string `json:"proxy_dns_domains,omitempty"`
+ IPCheckDomains []string `json:"ip_check_domains,omitempty"`
+}
diff --git a/internal/models/ruleset.go b/internal/models/ruleset.go
new file mode 100644
index 0000000..8599322
--- /dev/null
+++ b/internal/models/ruleset.go
@@ -0,0 +1,23 @@
+package models
+
+type RuleSet struct {
+ Tag string `json:"tag"`
+ Description string `json:"description"`
+ URL string `json:"url"`
+ LocalPath string `json:"-"`
+ Format string `json:"format"` // binary, source
+ Type string `json:"type"` // domain, ip
+ Optional bool `json:"optional"`
+ SHA256 string `json:"sha256,omitempty"`
+}
+
+type RuleSetManifest struct {
+ RuleSets []RuleSet `json:"rule_sets"`
+}
+
+type VersionResponse struct {
+ Version string `json:"version"`
+ URL string `json:"url"`
+ SHA256 string `json:"sha256,omitempty"`
+ Changelog string `json:"changelog,omitempty"`
+}
diff --git a/internal/models/server.go b/internal/models/server.go
new file mode 100644
index 0000000..f440c03
--- /dev/null
+++ b/internal/models/server.go
@@ -0,0 +1,46 @@
+package models
+
+type TLS struct {
+ Enabled bool `json:"enabled"`
+ ServerName string `json:"server_name,omitempty"`
+ Insecure bool `json:"insecure,omitempty"`
+ ALPN []string `json:"alpn,omitempty"`
+ MinVersion string `json:"min_version,omitempty"`
+ MaxVersion string `json:"max_version,omitempty"`
+ Reality *Reality `json:"reality,omitempty"`
+}
+
+type Transport struct {
+ Type string `json:"type,omitempty"`
+ Path string `json:"path,omitempty"`
+}
+
+type Reality struct {
+ Enabled bool `json:"enabled,omitempty"`
+ PublicKey string `json:"public_key,omitempty"`
+ PrivateKey string `json:"private_key,omitempty"`
+ ShortID string `json:"short_id,omitempty"`
+ Fingerprint string `json:"fingerprint,omitempty"`
+}
+
+type Server struct {
+ Tag string `json:"tag"`
+ Region string `json:"region"`
+ Type string `json:"type"` // socks, vless, vless-reality, shadowsocks, vmess, hysteria2
+ Server string `json:"server"`
+ ServerPort int `json:"server_port"`
+ UDPOverTCP bool `json:"udp_over_tcp,omitempty"`
+ UUID string `json:"uuid,omitempty"`
+ Method string `json:"method,omitempty"`
+ Password string `json:"password,omitempty"`
+ ObfsPassword string `json:"obfs_password,omitempty"`
+ UpMbps int `json:"up_mbps,omitempty"`
+ DownMbps int `json:"down_mbps,omitempty"`
+ TLS *TLS `json:"tls,omitempty"`
+ Transport *Transport `json:"transport,omitempty"`
+ Companions []Server `json:"companions,omitempty"`
+}
+
+type ServersResponse struct {
+ Servers []Server `json:"servers"`
+}