summaryrefslogtreecommitdiff
path: root/internal/api/router.go
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/api/router.go
downloadvpnem-3d51aa455006903345f554a2dd90034993796114.tar.gz
vpnem-3d51aa455006903345f554a2dd90034993796114.tar.bz2
vpnem-3d51aa455006903345f554a2dd90034993796114.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/api/router.go')
-rw-r--r--internal/api/router.go80
1 files changed, 80 insertions, 0 deletions
diff --git a/internal/api/router.go b/internal/api/router.go
new file mode 100644
index 0000000..1cb9ff6
--- /dev/null
+++ b/internal/api/router.go
@@ -0,0 +1,80 @@
+package api
+
+import (
+ "net/http"
+
+ "vpnem/internal/rules"
+)
+
+func NewRouter(store *rules.Store) http.Handler {
+ h := NewHandler(store)
+ mux := http.NewServeMux()
+
+ mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"status":"ok"}`))
+ })
+ mux.HandleFunc("/vpnui", func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ http.Redirect(w, r, "/vpnui/", http.StatusTemporaryRedirect)
+ })
+ mux.HandleFunc("/vpnui/", methodHandler(http.MethodGet, h.VPNUI))
+ mux.HandleFunc("/api/v1/servers", methodHandler(http.MethodGet, h.Servers))
+ mux.HandleFunc("/api/v2/catalog", methodHandler(http.MethodGet, h.CatalogV2))
+ mux.HandleFunc("/api/v1/routing-policy", methodHandler(http.MethodGet, h.RoutingPolicy))
+ mux.HandleFunc("/api/v1/subscribe", methodHandler(http.MethodGet, h.Subscribe))
+ mux.HandleFunc("/api/v1/ruleset/manifest", methodHandler(http.MethodGet, h.RuleSetManifest))
+ mux.HandleFunc("/api/v1/version", methodHandler(http.MethodGet, h.Version))
+ mux.HandleFunc("/api/v1/control/nodes", func(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case http.MethodGet:
+ h.ControlNodes(w, r)
+ case http.MethodPost:
+ h.UpsertControlNode(w, r)
+ default:
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ }
+ })
+ mux.HandleFunc("/api/v1/control/preflight", methodHandler(http.MethodPost, h.QuickPreflightControlNode))
+ mux.HandleFunc("/api/v1/control/quick-provision", methodHandler(http.MethodPost, h.QuickProvisionControlNode))
+ mux.HandleFunc("/api/v1/control/nodes/", h.ControlNodeAction)
+ mux.HandleFunc("/api/v1/control/catalog/publish", methodHandler(http.MethodPost, h.PublishControlCatalog))
+
+ // Static file serving for .srs and .txt rule files
+ rulesFS := http.StripPrefix("/rules/", http.FileServer(http.Dir(store.RulesDir())))
+ mux.Handle("/rules/", rulesFS)
+
+ // Static file serving for client releases
+ releasesFS := http.StripPrefix("/releases/", http.FileServer(http.Dir(store.ReleasesDir())))
+ mux.Handle("/releases/", releasesFS)
+
+ // Client error log endpoint (obscure URL, no auth needed — just writes to file)
+ mux.HandleFunc("/logs2026vpnem/errors", methodHandler(http.MethodPost, h.ClientLog))
+
+ // Web viewer for client logs (admin-protected via env var)
+ mux.HandleFunc("/client-logs", methodHandler(http.MethodGet, h.ClientLogsViewer))
+
+ // Client connection report and recommendation (RealIP middleware auto-detects client IP)
+ mux.HandleFunc("/api/v1/connect", RealIP(h.ClientConnect))
+ mux.HandleFunc("/api/v1/disconnect", RealIP(h.ClientDisconnect))
+ mux.HandleFunc("/api/v1/recommend", RealIP(h.Recommend))
+
+ return mux
+}
+
+func methodHandler(method string, next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != method {
+ http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+ next(w, r)
+ }
+}