Update architecture
This commit is contained in:
parent
8217818036
commit
f4f3062352
@ -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`).
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
39
index.html
39
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 @@
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🚀 NATS Queue Visualizer</h1>
|
||||
<div id="status" class="status disconnected">Отключено</div>
|
||||
<div class="header-info">
|
||||
<span id="status" class="status disconnected">Отключено</span>
|
||||
<span class="header-sep">•</span>
|
||||
<span>Подписки: <strong id="subscribedList">—</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
@ -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);
|
||||
</script>
|
||||
|
||||
46
main.go
46
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,15 +204,22 @@ 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 parse NATS connz from %s: %v, body sample: %s", url, err, truncate(string(body), 200))
|
||||
}
|
||||
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)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(connz); 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 {
|
||||
@ -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))
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user