package server import ( "encoding/json" "io" "net" "net/http" "strconv" "strings" "time" ) const defaultRadarStatusesURL = "https://radar.telemt.top/api/v1/endpoints/statuses" // telegramDC443 — IPv4 DC Telegram для MTProto (как в ping_proxy.php исходной панели). var telegramDC443 = map[int]string{ 1: "149.154.175.50", 2: "149.154.167.51", 3: "149.154.175.100", 4: "149.154.167.91", 5: "149.154.171.5", } func (g *Gateway) serveRadar(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/api/radar/statuses": if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } g.serveRadarStatuses(w, r) case "/api/radar/ping-dc": if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } g.serveRadarPingDC(w, r) default: http.NotFound(w, r) } } // serveRadarStatuses проксирует JSON radar.telemt.top (аналог radar_proxy.php). func (g *Gateway) serveRadarStatuses(w http.ResponseWriter, r *http.Request) { req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, g.radarStatusesURL, nil) if err != nil { writeRadarFailed(w) return } resp, err := g.radarHTTPClient.Do(req) if err != nil { writeRadarFailed(w) return } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { writeRadarFailed(w) return } ct := resp.Header.Get("Content-Type") if ct == "" { ct = "application/json" } w.Header().Set("Content-Type", ct) w.WriteHeader(http.StatusOK) _, _ = io.Copy(w, resp.Body) } func writeRadarFailed(w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusInternalServerError) _ = json.NewEncoder(w).Encode(map[string]string{"error": "failed"}) } // serveRadarPingDC — TCP :443 до каждого DC с таймаутом 2 с (аналог ping_proxy.php). func (g *Gateway) serveRadarPingDC(w http.ResponseWriter, r *http.Request) { from := strings.TrimSpace(g.radarPingFrom) if from == "" { from = hostOnly(r.Host) if from == "" { from = "telemt-gateway" } } results := make(map[string]map[string]any, len(telegramDC443)) for i := 1; i <= 5; i++ { ip := telegramDC443[i] addr := net.JoinHostPort(ip, "443") t0 := time.Now() conn, err := net.DialTimeout("tcp", addr, 2*time.Second) ms := int(time.Since(t0).Milliseconds()) key := strconv.Itoa(i) if err != nil { results[key] = map[string]any{"ok": false, "ms": nil} continue } _ = conn.Close() results[key] = map[string]any{"ok": true, "ms": ms} } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(map[string]any{ "ok": true, "results": results, "from": from, }) } func hostOnly(hostPort string) string { hostPort = strings.TrimSpace(hostPort) if hostPort == "" { return "" } // IPv6 в квадратных скобках: [::1]:8080 if strings.HasPrefix(hostPort, "[") { if i := strings.IndexByte(hostPort, ']'); i > 0 { return hostPort[1:i] } } h, _, err := net.SplitHostPort(hostPort) if err != nil { return hostPort } return h }