Add Radar DC Telegram functionality and configuration
- 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.
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user