From f4f306235276dcc211123b305dba537489f92a8c Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 9 Feb 2026 17:31:21 +0700 Subject: [PATCH] Update architecture --- FAVICON_OPTIONS.md | 46 ------------------------------------------- favicon.ico | Bin 21346 -> 0 bytes index.html | 39 ++++++++++++++++++++++++++++++++++-- main.go | 48 ++++++++++++++++++++++++--------------------- 4 files changed, 63 insertions(+), 70 deletions(-) delete mode 100644 FAVICON_OPTIONS.md delete mode 100644 favicon.ico diff --git a/FAVICON_OPTIONS.md b/FAVICON_OPTIONS.md deleted file mode 100644 index bca8075..0000000 --- a/FAVICON_OPTIONS.md +++ /dev/null @@ -1,46 +0,0 @@ -# Варианты favicon для NATS UI - -В проекте две группы иконок (32×32 PNG): дизайн «два облачка + линия» в разных цветах и альтернативные дизайны. - -## Цветовые варианты (два облачка и изогнутая линия) - -Один и тот же дизайн — разные фоны и акценты. - -| Файл | Цвета | -|------|--------| -| `favicon_variant0_original.png` | Исходный: приглушённый синий фон, белые облачка и линия | -| `favicon_variant1_purple.png` | Тёмно-фиолетовый фон, белые формы | -| `favicon_variant2_navy_cyan.png` | Тёмно-синий фон, голубые (cyan) формы | -| `favicon_variant3_teal.png` | Бирюзовый фон, белые формы | -| `favicon_variant4_black.png` | Чёрный фон, белые формы | -| `favicon_variant5_coral.png` | Кораллово-оранжевый фон, белые формы | -| `favicon_variant6_violet.png` | Фиолетовый фон, белые формы | -| `favicon_variant7_green.png` | Тёмно-зелёный фон, белые формы | - -## Другие дизайны - -| Файл | Описание | -|------|----------| -| `favicon_option1.png` | Молния/стрелка на фиолетовом | -| `favicon_option2.png` | Синий круг, три точки | -| `favicon_option3.png` | Стилизованная буква N | -| `favicon_option4.png` | Два перекрывающихся облачка/конверта | -| `favicon_option5.png` | Узлы и связи | - -## Как выбрать иконку - -1. Откройте файлы в проводнике или в браузере (если раздаёте статику). -2. Выберите понравившийся вариант. -3. Замените текущий favicon, например: - ```bash - cp favicon_variant3_teal.png favicon.ico - # или - cp favicon_option4.png favicon.ico - ``` -4. Пересоберите приложение: - ```bash - go build -o nats-ui . - ``` - Или пересоберите Docker-образ. - -Браузеры принимают PNG в качестве favicon (файл может называться `favicon.ico`). diff --git a/favicon.ico b/favicon.ico deleted file mode 100644 index a42255e4174609193312ace33b5c25d34ebceead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21346 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 diff --git a/index.html b/index.html index de38476..f15343c 100644 --- a/index.html +++ b/index.html @@ -39,13 +39,30 @@ margin-bottom: 10px; } + .header-info { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + flex-wrap: wrap; + margin-top: 12px; + font-size: 0.95em; + } + .header .status { display: inline-block; padding: 8px 16px; background: rgba(255, 255, 255, 0.2); border-radius: 20px; font-size: 0.9em; - margin-top: 10px; + } + + .header-sep { + opacity: 0.7; + } + + .header-info strong { + font-weight: 600; } .status.connected { @@ -326,7 +343,11 @@

🚀 NATS Queue Visualizer

-
Отключено
+
+ Отключено + + Подписки: +
@@ -565,6 +586,19 @@ updateMessages(); }); + function loadSubscribed() { + fetch('/api/subscribed') + .then(res => res.json()) + .then(data => { + const list = data.subscribed || []; + const el = document.getElementById('subscribedList'); + el.textContent = list.length ? list.join(', ') : '—'; + }) + .catch(() => { + document.getElementById('subscribedList').textContent = '—'; + }); + } + function loadConnections() { fetch('/api/connections') .then(res => res.json()) @@ -613,6 +647,7 @@ // Инициализация - только WebSocket, все сообщения придут автоматически connectWebSocket(); + loadSubscribed(); loadConnections(); setInterval(loadConnections, 5000); diff --git a/main.go b/main.go index dcfb4c1..7296afa 100644 --- a/main.go +++ b/main.go @@ -146,10 +146,11 @@ type Config struct { type connzResponse struct { Connections []connInfo `json:"connections"` + Conns []connInfo `json:"conns"` } type connInfo struct { - CID int `json:"cid"` + CID uint64 `json:"cid"` IP string `json:"ip"` Port int `json:"port"` Name string `json:"name"` @@ -170,6 +171,7 @@ func connectionsHandler(w http.ResponseWriter, _ *http.Request, cfg *Config) { return } url := strings.TrimSuffix(cfg.NatsMonitorURL, "/") + "/connz" + log.Printf("Fetching NATS connections from %s", url) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -181,7 +183,7 @@ func connectionsHandler(w http.ResponseWriter, _ *http.Request, cfg *Config) { client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Do(req) if err != nil { - log.Printf("Failed to fetch NATS connz: %v", err) + log.Printf("Failed to fetch NATS connz from %s: %v", url, err) if err := json.NewEncoder(w).Encode(connzResponse{Connections: []connInfo{}}); err != nil { log.Printf("Failed to encode connections: %v", err) } @@ -194,7 +196,7 @@ func connectionsHandler(w http.ResponseWriter, _ *http.Request, cfg *Config) { return } if resp.StatusCode != http.StatusOK { - log.Printf("NATS connz returned status %d", resp.StatusCode) + log.Printf("NATS connz %s returned status %d, body: %s", url, resp.StatusCode, truncate(string(body), 300)) if err := json.NewEncoder(w).Encode(connzResponse{Connections: []connInfo{}}); err != nil { log.Printf("Failed to encode connections: %v", err) } @@ -202,17 +204,24 @@ func connectionsHandler(w http.ResponseWriter, _ *http.Request, cfg *Config) { } var connz connzResponse if err := json.Unmarshal(body, &connz); err != nil { - log.Printf("Failed to parse connz: %v", err) - if err := json.NewEncoder(w).Encode(connzResponse{Connections: []connInfo{}}); err != nil { - log.Printf("Failed to encode connections: %v", err) - } - return + log.Printf("Failed to parse NATS connz from %s: %v, body sample: %s", url, err, truncate(string(body), 200)) } - if err := json.NewEncoder(w).Encode(connz); err != nil { + if len(connz.Connections) == 0 && len(connz.Conns) > 0 { + connz.Connections = connz.Conns + } + out := connzResponse{Connections: connz.Connections} + if err := json.NewEncoder(w).Encode(out); err != nil { log.Printf("Failed to encode connections: %v", err) } } +func truncate(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max] + "..." +} + func extractProducerFromPayload(data []byte) string { var m map[string]interface{} if err := json.Unmarshal(data, &m); err != nil { @@ -264,7 +273,6 @@ func main() { } log.Fatalf("Failed to load config: %v", err) } - log.Printf("Loaded configuration from %s", configFile) store := NewMessageStore(config.MaxMessages) hub := NewHub() go hub.Run() @@ -272,17 +280,14 @@ func main() { opts = append(opts, nats.Name("nats-ui")) if config.Token != "" { opts = append(opts, nats.Token(config.Token)) - log.Printf("Using token authentication") } else if config.Username != "" || config.Password != "" { opts = append(opts, nats.UserInfo(config.Username, config.Password)) - log.Printf("Using username/password authentication") } nc, err := nats.Connect(config.NatsURL, opts...) if err != nil { log.Fatalf("Failed to connect to NATS: %v", err) } defer nc.Close() - log.Printf("Connected to NATS at %s", config.NatsURL) subjectsList := parseSubjects(config.Subjects) for _, subject := range subjectsList { subj := subject @@ -299,12 +304,10 @@ func main() { } store.Add(message) hub.broadcast <- message - log.Printf("Received message on %s: %d bytes", msg.Subject, len(msg.Data)) }) if err != nil { log.Fatalf("Failed to subscribe to %s: %v", subj, err) } - log.Printf("Subscribed to: %s", subj) defer func() { if err := sub.Unsubscribe(); err != nil { log.Printf("Failed to unsubscribe from %s: %v", subj, err) @@ -347,6 +350,13 @@ func main() { log.Printf("Failed to encode subjects: %v", err) } }) + http.HandleFunc("/api/subscribed", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + list := parseSubjects(config.Subjects) + if err := json.NewEncoder(w).Encode(map[string]interface{}{"subscribed": list}); err != nil { + log.Printf("Failed to encode subscribed: %v", err) + } + }) http.HandleFunc("/api/connections", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") connectionsHandler(w, r, config) @@ -358,18 +368,13 @@ func main() { return } hub.register <- conn - - // Отправляем все существующие сообщения одним массивом при подключении go func() { messages := store.GetAll() - // Отправляем массив всех сообщений if err := conn.WriteJSON(messages); err != nil { log.Printf("Error sending initial messages: %v", err) return } }() - - // Читаем сообщения от клиента (для keep-alive) go func() { for { _, _, err := conn.ReadMessage() @@ -380,8 +385,7 @@ func main() { } }() }) - log.Printf("Starting HTTP server on port %s", config.Port) - log.Printf("Open http://localhost:%s in your browser", config.Port) + log.Printf("NATS UI: http://0.0.0.0:%s", config.Port) addr := ":" + config.Port log.Fatal(http.ListenAndServe(addr, nil)) }