feat: монолит iot-service (Фаза 1) — config/store/api/bridge/consumer без k8s, устройства в PG, новые Dockerfile/Makefile
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
// Package config — конфигурация монолита iot-service из env-переменных.
|
||||
//
|
||||
// Принцип: несекретные значения имеют дефолты (продублированы ENV в Dockerfile),
|
||||
// секреты — обязательны и задаются в deck-UI при создании контейнера.
|
||||
// Env-переменные после деплоя не меняются — поэтому дефолты вшиты в образ.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config — вся конфигурация iot-service.
|
||||
type Config struct {
|
||||
// HTTP API
|
||||
APIPort string
|
||||
|
||||
// PostgreSQL (managed, общий для устройств и телеметрии)
|
||||
IOTPGDSN string
|
||||
|
||||
// shared-SQS
|
||||
SQSEndpoint string
|
||||
SQSAccessKey string
|
||||
SQSSecretKey string
|
||||
SQSQueueName string
|
||||
SQSRegion string
|
||||
|
||||
// SQS-потребитель
|
||||
SQSLongPollSeconds int
|
||||
SQSVisibilityTimeout int
|
||||
|
||||
// MQTT (EMQX)
|
||||
MQTTBrokerURL string
|
||||
MQTTUsername string
|
||||
MQTTPassword string
|
||||
MQTTClientID string
|
||||
|
||||
// Админ-статистика
|
||||
AdminStatsToken string
|
||||
|
||||
// Безопасность
|
||||
AuthTestMode bool
|
||||
|
||||
// Логирование
|
||||
LogLevel slog.Level
|
||||
}
|
||||
|
||||
// getEnv возвращает значение env или дефолт.
|
||||
func getEnv(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// getEnvInt возвращает целое значение env или дефолт (неположительное — дефолт).
|
||||
func getEnvInt(key string, def int) int {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if n, err := strconv.Atoi(v); err == nil && n > 0 {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// getEnvBool разбирает булево значение env.
|
||||
func getEnvBool(key string, def bool) bool {
|
||||
v := os.Getenv(key)
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
switch strings.ToLower(v) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
case "0", "false", "no", "off":
|
||||
return false
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
// parseLogLevel разбирает уровень логирования.
|
||||
func parseLogLevel(v string, def slog.Level) slog.Level {
|
||||
switch strings.ToLower(v) {
|
||||
case "debug":
|
||||
return slog.LevelDebug
|
||||
case "warn", "warning":
|
||||
return slog.LevelWarn
|
||||
case "error":
|
||||
return slog.LevelError
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
// Load читает конфигурацию из env, подставляет дефолты
|
||||
// и проверяет обязательные (секретные) переменные.
|
||||
func Load() (*Config, error) {
|
||||
cfg := &Config{
|
||||
APIPort: getEnv("API_PORT", DefaultAPIPort),
|
||||
IOTPGDSN: os.Getenv("IOT_PG_DSN"),
|
||||
SQSEndpoint: getEnv("SQS_ENDPOINT", DefaultSQSEndpoint),
|
||||
SQSAccessKey: os.Getenv("SQS_ACCESS_KEY"),
|
||||
SQSSecretKey: os.Getenv("SQS_SECRET_KEY"),
|
||||
SQSQueueName: getEnv("SQS_QUEUE_NAME", DefaultSQSQueueName),
|
||||
SQSRegion: getEnv("SQS_REGION", DefaultSQSRegion),
|
||||
SQSLongPollSeconds: getEnvInt("SQS_LONG_POLL_SECONDS", 20),
|
||||
SQSVisibilityTimeout: getEnvInt("SQS_VISIBILITY_TIMEOUT", 30),
|
||||
MQTTUsername: os.Getenv("MQTT_USERNAME"),
|
||||
MQTTPassword: os.Getenv("MQTT_PASSWORD"),
|
||||
MQTTClientID: getEnv("MQTT_CLIENT_ID", DefaultMQTTClientID),
|
||||
AdminStatsToken: os.Getenv("ADMIN_STATS_TOKEN"),
|
||||
AuthTestMode: getEnvBool("AUTH_TEST_MODE", false),
|
||||
LogLevel: parseLogLevel(os.Getenv("LOG_LEVEL"), slog.LevelInfo),
|
||||
}
|
||||
|
||||
// MQTT broker URL: либо полный MQTT_BROKER_URL, либо из HOST/PORT/WS_PATH
|
||||
cfg.MQTTBrokerURL = os.Getenv("MQTT_BROKER_URL")
|
||||
if cfg.MQTTBrokerURL == "" {
|
||||
host := getEnv("MQTT_HOST", DefaultMQTTHost)
|
||||
port := getEnv("MQTT_PORT", DefaultMQTTPort)
|
||||
path := getEnv("MQTT_WS_PATH", DefaultMQTTWSPath)
|
||||
cfg.MQTTBrokerURL = "ws://" + host + ":" + port + path
|
||||
}
|
||||
|
||||
var missing []string
|
||||
if cfg.IOTPGDSN == "" {
|
||||
missing = append(missing, "IOT_PG_DSN")
|
||||
}
|
||||
if cfg.SQSAccessKey == "" {
|
||||
missing = append(missing, "SQS_ACCESS_KEY")
|
||||
}
|
||||
if cfg.SQSSecretKey == "" {
|
||||
missing = append(missing, "SQS_SECRET_KEY")
|
||||
}
|
||||
if cfg.MQTTUsername == "" {
|
||||
missing = append(missing, "MQTT_USERNAME")
|
||||
}
|
||||
if cfg.MQTTPassword == "" {
|
||||
missing = append(missing, "MQTT_PASSWORD")
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
return nil, fmt.Errorf("required env vars not set: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package config
|
||||
|
||||
// Defaults — дефолтные значения env-переменных (не секреты).
|
||||
// Продублированы в Dockerfile ENV.
|
||||
const (
|
||||
// DefaultAPIPort — единственный порт контейнера: API + /health + /metrics.
|
||||
DefaultAPIPort = "9090"
|
||||
|
||||
// DefaultSQSEndpoint — внутренний адрес shared-sqs в том же кластере Nubes.
|
||||
// Обходит внешний шлюз платформы (таймауты 31-33с, MSS 1448, ~150с).
|
||||
DefaultSQSEndpoint = "http://containerk8s.f1ffb134-7d16-45bd-8bef-69f6ec8ab33c.svc.cluster.local:4100"
|
||||
|
||||
// DefaultSQSQueueName — очередь телеметрии IoT.
|
||||
DefaultSQSQueueName = "iot-telemetry"
|
||||
|
||||
// DefaultSQSRegion — регион для self-hosted SQS (не важен, оставлен для SDK).
|
||||
DefaultSQSRegion = "us-east-1"
|
||||
|
||||
// DefaultMQTTHost — внутреннее имя EMQX-контейнера на платформе.
|
||||
// Реальный UUID namespace EMQX задаётся в deck-UI (MQTT_HOST) при создании.
|
||||
DefaultMQTTHost = "emqx"
|
||||
|
||||
// DefaultMQTTPort — WebSocket-листенер EMQX (wss внутри платформы).
|
||||
DefaultMQTTPort = "8083"
|
||||
|
||||
// DefaultMQTTWSPath — путь MQTT-over-WebSocket на EMQX.
|
||||
DefaultMQTTWSPath = "/mqtt"
|
||||
|
||||
// DefaultMQTTClientID — clientID bridge-подключения к EMQX.
|
||||
// Используется и в ACL-логике MQTT-auth.
|
||||
DefaultMQTTClientID = "iot-bridge"
|
||||
)
|
||||
Reference in New Issue
Block a user