feat: монолит iot-service (Фаза 1) — config/store/api/bridge/consumer без k8s, устройства в PG, новые Dockerfile/Makefile

This commit is contained in:
“Naeel”
2026-08-16 10:07:51 +04:00
parent f0c36a11ee
commit 9d08473d5f
27 changed files with 3716 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
// admin.go — статистика для страницы администратора.
//
// Endpoint: GET /iot-admin/stats (Bearer ADMIN_STATS_TOKEN).
// Источники: PostgreSQL (iotpg), shared-SQS (атрибуты очереди), рантайм процесса.
// k8s-статусы подов убраны — компоненты теперь в одном процессе.
package handler
import (
"context"
"net/http"
"strings"
"time"
)
// AdminStats — GET /iot-admin/stats.
func (h *Handler) AdminStats(w http.ResponseWriter, r *http.Request) {
if h.Cfg.AdminStatsToken == "" {
writeJSON(w, http.StatusServiceUnavailable, errResp("admin stats not configured: ADMIN_STATS_TOKEN not set"))
return
}
authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") || strings.TrimPrefix(authHeader, "Bearer ") != h.Cfg.AdminStatsToken {
writeJSON(w, http.StatusUnauthorized, errResp("unauthorized"))
return
}
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
defer cancel()
result := map[string]any{
"collected_at": time.Now().UTC(),
}
// PostgreSQL: статистика по всем tenant (как в старом коде).
if h.IoTPG != nil {
pgStats, err := h.IoTPG.GetAdminStats(ctx)
if err != nil {
result["postgres"] = map[string]any{"reachable": false, "error": err.Error()}
} else {
result["postgres"] = pgStats
}
} else {
result["postgres"] = map[string]any{"reachable": false, "error": "IoTPG not configured"}
}
// SQS: approximate counts очереди телеметрии.
result["sqs"] = h.collectSQSStats(ctx)
// Рантайм процесса (вместо статусов k8s-подов).
result["runtime"] = map[string]any{
"version": h.Version,
"uptime_sec": int64(time.Since(h.StartedAt).Seconds()),
"started_at": h.StartedAt.UTC().Format(time.RFC3339),
}
writeJSON(w, http.StatusOK, result)
}