Files
sless/shared-sqs/app/cmd/goaws.go
T

131 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// app/cmd/goaws.go
// Entry point — shared-sqs server
// Updated: 2026-04-09 — добавлены TenantStore, admin token, graceful shutdown (Trap #13)
package main
import (
"context"
"flag"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"shared-sqs/app/conf"
"shared-sqs/app/gosqs"
"shared-sqs/app/models"
"shared-sqs/app/router"
"shared-sqs/app/tenant"
log "github.com/sirupsen/logrus"
)
func main() {
var configFile string
var adminToken string
var port string
var debug bool
var loglevel string
flag.StringVar(&configFile, "config", "", "config file location")
flag.StringVar(&adminToken, "admin-token", "", "admin API bearer token")
flag.StringVar(&port, "port", "4100", "listen port")
flag.BoolVar(&debug, "debug", false, "set debug log level")
flag.StringVar(&loglevel, "loglevel", "info", "log level (info, debug, warn, error)")
flag.Parse()
log.SetFormatter(&log.JSONFormatter{})
log.SetOutput(os.Stdout)
if debug {
log.SetLevel(log.DebugLevel)
} else {
level, err := log.ParseLevel(loglevel)
if err != nil {
log.SetLevel(log.InfoLevel)
log.Warnf("Failed to parse loglevel %v, defaulting to info", loglevel)
} else {
log.SetLevel(level)
}
}
// Admin token: flag > env SHARED_SQS_ADMIN_TOKEN > fatal (Trap #13)
if adminToken == "" {
adminToken = os.Getenv("SHARED_SQS_ADMIN_TOKEN")
}
if adminToken == "" {
log.Fatal("admin token required: use --admin-token flag or SHARED_SQS_ADMIN_TOKEN env var")
}
// Загрузить конфиг (очереди, env — без SNS)
env := "Local"
if flag.NArg() > 0 {
env = flag.Arg(0)
}
conf.LoadYamlConfig(configFile, env)
if models.CurrentEnvironment.LogToFile {
filename := models.CurrentEnvironment.LogFile
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err == nil {
log.SetOutput(file)
} else {
log.Infof("Failed to log to file: %s, using default stdout", filename)
}
}
// Инициализация in-memory TenantStore
tenantStore := tenant.NewTenantStore()
// Автосид демо-данных при SHARED_SQS_SEED_DEMO=true
if os.Getenv("SHARED_SQS_SEED_DEMO") == "true" {
seedDemoData(tenantStore)
}
// Роутер с tenant auth и admin API
r := router.New(tenantStore, adminToken)
// PeriodicTasks — visibility timeout, DLQ, deduplication
quit := make(chan bool)
go gosqs.PeriodicTasks(1*time.Second, quit)
// HTTP сервер с таймаутами
srv := &http.Server{
Addr: "0.0.0.0:" + port,
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 35 * time.Second, // чуть больше чем max WaitTimeSeconds (20s)
IdleTimeout: 60 * time.Second,
}
// Запуск в горутине для graceful shutdown
serverErr := make(chan error, 1)
go func() {
log.Infof("shared-sqs listening on 0.0.0.0:%s", port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
serverErr <- err
}
}()
// Graceful shutdown по SIGTERM/SIGINT (Trap #13)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
select {
case sig := <-sigCh:
log.Infof("Received signal %s, shutting down", sig)
case err := <-serverErr:
log.Fatalf("Server error: %v", err)
}
// Остановить PeriodicTasks
close(quit)
// Дать 10 секунд на завершение текущих HTTP запросов
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Errorf("Server shutdown error: %v", err)
}
log.Info("shared-sqs stopped")
}