tercul-backend/cmd/api/main.go

118 lines
3.2 KiB
Go

package main
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"tercul/internal/app"
"tercul/internal/platform/config"
"tercul/internal/platform/log"
"time"
"github.com/hibiken/asynq"
)
// main is the entry point for the Tercul application.
// It uses the ApplicationBuilder and ServerFactory to initialize all components
// and start the servers in a clean, maintainable way.
func main() {
// Load configuration from environment variables
config.LoadConfig()
// Initialize structured logger with appropriate log level
log.SetDefaultLevel(log.InfoLevel)
log.LogInfo("Starting Tercul application",
log.F("environment", config.Cfg.Environment),
log.F("version", "1.0.0"))
// Build application components
appBuilder := app.NewApplicationBuilder()
if err := appBuilder.Build(); err != nil {
log.LogFatal("Failed to build application",
log.F("error", err))
}
defer appBuilder.Close()
// Create server factory
serverFactory := app.NewServerFactory(appBuilder)
// Create servers
graphQLServer, err := serverFactory.CreateGraphQLServer()
if err != nil {
log.LogFatal("Failed to create GraphQL server",
log.F("error", err))
}
backgroundServers, err := serverFactory.CreateBackgroundJobServers()
if err != nil {
log.LogFatal("Failed to create background job servers",
log.F("error", err))
}
playgroundServer := serverFactory.CreatePlaygroundServer()
// Start HTTP servers in goroutines
go func() {
log.LogInfo("Starting GraphQL server",
log.F("port", config.Cfg.ServerPort))
if err := graphQLServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.LogFatal("Failed to start GraphQL server",
log.F("error", err))
}
}()
go func() {
log.LogInfo("Starting GraphQL playground",
log.F("port", config.Cfg.PlaygroundPort))
if err := playgroundServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.LogFatal("Failed to start GraphQL playground",
log.F("error", err))
}
}()
// Start background job servers in goroutines
for i, server := range backgroundServers {
go func(serverIndex int, srv *asynq.Server) {
log.LogInfo("Starting background job server",
log.F("serverIndex", serverIndex))
if err := srv.Run(asynq.NewServeMux()); err != nil {
log.LogError("Background job server failed",
log.F("serverIndex", serverIndex),
log.F("error", err))
}
}(i, server)
}
// Wait for interrupt signal to gracefully shutdown the servers
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.LogInfo("Shutting down servers...")
// Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := graphQLServer.Shutdown(ctx); err != nil {
log.LogError("GraphQL server forced to shutdown",
log.F("error", err))
}
if err := playgroundServer.Shutdown(ctx); err != nil {
log.LogError("GraphQL playground forced to shutdown",
log.F("error", err))
}
// Shutdown background job servers
for i, server := range backgroundServers {
server.Shutdown()
log.LogInfo("Background job server shutdown",
log.F("serverIndex", i))
}
log.LogInfo("All servers shutdown successfully")
}