- Introduced new `RadarConfig` structure in `config.go` to manage radar settings, including `statuses_url`, `http_timeout_ms`, and `ping_from`. - Implemented validation for radar configuration in `config_test.go` to ensure correct URL schemes and timeout limits. - Added new API routes for radar statuses and ping functionality in the gateway, enhancing the service's capabilities. - Updated documentation in `GATEWAY_RUN.md` to include details about the new radar features and their usage. - Enhanced the user interface to include navigation and display options for the Radar DC section in the sidebar and page titles. - Added client-side API functions for fetching radar statuses and ping responses, improving integration with the frontend.
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
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
|
|
}
|