mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 00:31:35 +00:00
refactor(api): Consolidate server setup
This commit refactors the API server startup logic in `cmd/api/main.go` to simplify the application's architecture. Key changes: - Consolidates the three separate HTTP servers (GraphQL API, GraphQL Playground, and Prometheus metrics) into a single `http.Server` instance. - Uses a single `http.ServeMux` to route requests to the appropriate handlers on distinct paths (`/query`, `/playground`, `/metrics`). - Removes the now-redundant `PlaygroundPort` from the application's configuration. This change simplifies the server startup and shutdown logic, reduces resource usage, and makes the application's entry point cleaner and easier to maintain.
This commit is contained in:
parent
488bc141de
commit
1bb3e23c47
105
cmd/api/main.go
105
cmd/api/main.go
@ -112,84 +112,83 @@ func main() {
|
||||
log.Fatal(err, "Failed to create sentiment provider")
|
||||
}
|
||||
|
||||
// Create platform components
|
||||
jwtManager := auth.NewJWTManager()
|
||||
|
||||
// Create application services
|
||||
analyticsService := analytics.NewService(repos.Analytics, analysisRepo, repos.Translation, repos.Work, sentimentProvider)
|
||||
|
||||
// Create application dependencies
|
||||
deps := app.Dependencies{
|
||||
WorkRepo: repos.Work,
|
||||
UserRepo: repos.User,
|
||||
AuthorRepo: repos.Author,
|
||||
TranslationRepo: repos.Translation,
|
||||
CommentRepo: repos.Comment,
|
||||
LikeRepo: repos.Like,
|
||||
BookmarkRepo: repos.Bookmark,
|
||||
CollectionRepo: repos.Collection,
|
||||
TagRepo: repos.Tag,
|
||||
CategoryRepo: repos.Category,
|
||||
BookRepo: repos.Book,
|
||||
PublisherRepo: repos.Publisher,
|
||||
SourceRepo: repos.Source,
|
||||
CopyrightRepo: repos.Copyright,
|
||||
MonetizationRepo: repos.Monetization,
|
||||
AnalyticsRepo: repos.Analytics,
|
||||
AuthRepo: repos.Auth,
|
||||
LocalizationRepo: repos.Localization,
|
||||
SearchClient: searchClient,
|
||||
AnalyticsService: analyticsService,
|
||||
JWTManager: jwtManager,
|
||||
}
|
||||
|
||||
// Create application
|
||||
application := app.NewApplication(repos, searchClient, analyticsService)
|
||||
application := app.NewApplication(deps)
|
||||
|
||||
// Create GraphQL server
|
||||
resolver := &graph.Resolver{
|
||||
App: application,
|
||||
}
|
||||
|
||||
jwtManager := auth.NewJWTManager()
|
||||
srv := NewServerWithAuth(resolver, jwtManager, metrics, obsLogger)
|
||||
graphQLServer := &http.Server{
|
||||
// Create handlers
|
||||
apiHandler := NewServerWithAuth(resolver, jwtManager, metrics, obsLogger)
|
||||
playgroundHandler := playground.Handler("GraphQL Playground", "/query")
|
||||
metricsHandler := observability.PrometheusHandler(reg)
|
||||
|
||||
// Consolidate handlers into a single mux
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/query", apiHandler)
|
||||
mux.Handle("/playground", playgroundHandler)
|
||||
mux.Handle("/metrics", metricsHandler)
|
||||
|
||||
// Create a single HTTP server
|
||||
mainServer := &http.Server{
|
||||
Addr: config.Cfg.ServerPort,
|
||||
Handler: srv,
|
||||
Handler: mux,
|
||||
}
|
||||
log.Info(fmt.Sprintf("GraphQL server created successfully on port %s", config.Cfg.ServerPort))
|
||||
log.Info(fmt.Sprintf("API server listening on port %s", config.Cfg.ServerPort))
|
||||
|
||||
// Create GraphQL playground
|
||||
playgroundHandler := playground.Handler("GraphQL", "/query")
|
||||
playgroundServer := &http.Server{
|
||||
Addr: config.Cfg.PlaygroundPort,
|
||||
Handler: playgroundHandler,
|
||||
}
|
||||
log.Info(fmt.Sprintf("GraphQL playground created successfully on port %s", config.Cfg.PlaygroundPort))
|
||||
|
||||
// Create metrics server
|
||||
metricsServer := &http.Server{
|
||||
Addr: ":9090",
|
||||
Handler: observability.PrometheusHandler(reg),
|
||||
}
|
||||
log.Info("Metrics server created successfully on port :9090")
|
||||
|
||||
// Start HTTP servers in goroutines
|
||||
// Start the main server in a goroutine
|
||||
go func() {
|
||||
log.Info(fmt.Sprintf("Starting GraphQL server on port %s", config.Cfg.ServerPort))
|
||||
if err := graphQLServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err, "Failed to start GraphQL server")
|
||||
if err := mainServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err, "Failed to start server")
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
log.Info(fmt.Sprintf("Starting GraphQL playground on port %s", config.Cfg.PlaygroundPort))
|
||||
if err := playgroundServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err, "Failed to start GraphQL playground")
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
log.Info("Starting metrics server on port :9090")
|
||||
if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err, "Failed to start metrics server")
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown the servers
|
||||
// Wait for interrupt signal to gracefully shutdown the server
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
log.Info("Shutting down servers...")
|
||||
log.Info("Shutting down server...")
|
||||
|
||||
// Graceful shutdown
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := graphQLServer.Shutdown(ctx); err != nil {
|
||||
log.Error(err, "GraphQL server forced to shutdown")
|
||||
if err := mainServer.Shutdown(ctx); err != nil {
|
||||
log.Error(err, "Server forced to shutdown")
|
||||
}
|
||||
|
||||
if err := playgroundServer.Shutdown(ctx); err != nil {
|
||||
log.Error(err, "GraphQL playground forced to shutdown")
|
||||
}
|
||||
|
||||
if err := metricsServer.Shutdown(ctx); err != nil {
|
||||
log.Error(err, "Metrics server forced to shutdown")
|
||||
}
|
||||
|
||||
log.Info("All servers shutdown successfully")
|
||||
log.Info("Server shut down successfully")
|
||||
}
|
||||
@ -15,13 +15,41 @@ import (
|
||||
"tercul/internal/app/user"
|
||||
"tercul/internal/app/auth"
|
||||
"tercul/internal/app/work"
|
||||
"tercul/internal/data/sql"
|
||||
"tercul/internal/domain"
|
||||
auth_domain "tercul/internal/domain/auth"
|
||||
localization_domain "tercul/internal/domain/localization"
|
||||
"tercul/internal/domain/search"
|
||||
work_domain "tercul/internal/domain/work"
|
||||
platform_auth "tercul/internal/platform/auth"
|
||||
)
|
||||
|
||||
import "tercul/internal/app/authz"
|
||||
|
||||
// Dependencies holds all external dependencies for the application.
|
||||
type Dependencies struct {
|
||||
WorkRepo work_domain.WorkRepository
|
||||
UserRepo domain.UserRepository
|
||||
AuthorRepo domain.AuthorRepository
|
||||
TranslationRepo domain.TranslationRepository
|
||||
CommentRepo domain.CommentRepository
|
||||
LikeRepo domain.LikeRepository
|
||||
BookmarkRepo domain.BookmarkRepository
|
||||
CollectionRepo domain.CollectionRepository
|
||||
TagRepo domain.TagRepository
|
||||
CategoryRepo domain.CategoryRepository
|
||||
BookRepo domain.BookRepository
|
||||
PublisherRepo domain.PublisherRepository
|
||||
SourceRepo domain.SourceRepository
|
||||
CopyrightRepo domain.CopyrightRepository
|
||||
MonetizationRepo domain.MonetizationRepository
|
||||
AnalyticsRepo analytics.Repository
|
||||
AuthRepo auth_domain.AuthRepository
|
||||
LocalizationRepo localization_domain.LocalizationRepository
|
||||
SearchClient search.SearchClient
|
||||
AnalyticsService analytics.Service
|
||||
JWTManager platform_auth.JWTManagement
|
||||
}
|
||||
|
||||
// Application is a container for all the application-layer services.
|
||||
type Application struct {
|
||||
Author *author.Service
|
||||
@ -41,22 +69,21 @@ type Application struct {
|
||||
Analytics analytics.Service
|
||||
}
|
||||
|
||||
func NewApplication(repos *sql.Repositories, searchClient search.SearchClient, analyticsService analytics.Service) *Application {
|
||||
jwtManager := platform_auth.NewJWTManager()
|
||||
authzService := authz.NewService(repos.Work, repos.Translation)
|
||||
authorService := author.NewService(repos.Author)
|
||||
bookService := book.NewService(repos.Book, authzService)
|
||||
bookmarkService := bookmark.NewService(repos.Bookmark, analyticsService)
|
||||
categoryService := category.NewService(repos.Category)
|
||||
collectionService := collection.NewService(repos.Collection)
|
||||
commentService := comment.NewService(repos.Comment, authzService, analyticsService)
|
||||
likeService := like.NewService(repos.Like, analyticsService)
|
||||
tagService := tag.NewService(repos.Tag)
|
||||
translationService := translation.NewService(repos.Translation, authzService)
|
||||
userService := user.NewService(repos.User, authzService)
|
||||
localizationService := localization.NewService(repos.Localization)
|
||||
authService := auth.NewService(repos.User, jwtManager)
|
||||
workService := work.NewService(repos.Work, searchClient, authzService)
|
||||
func NewApplication(deps Dependencies) *Application {
|
||||
authzService := authz.NewService(deps.WorkRepo, deps.TranslationRepo)
|
||||
authorService := author.NewService(deps.AuthorRepo)
|
||||
bookService := book.NewService(deps.BookRepo, authzService)
|
||||
bookmarkService := bookmark.NewService(deps.BookmarkRepo, deps.AnalyticsService)
|
||||
categoryService := category.NewService(deps.CategoryRepo)
|
||||
collectionService := collection.NewService(deps.CollectionRepo)
|
||||
commentService := comment.NewService(deps.CommentRepo, authzService, deps.AnalyticsService)
|
||||
likeService := like.NewService(deps.LikeRepo, deps.AnalyticsService)
|
||||
tagService := tag.NewService(deps.TagRepo)
|
||||
translationService := translation.NewService(deps.TranslationRepo, authzService)
|
||||
userService := user.NewService(deps.UserRepo, authzService)
|
||||
localizationService := localization.NewService(deps.LocalizationRepo)
|
||||
authService := auth.NewService(deps.UserRepo, deps.JWTManager)
|
||||
workService := work.NewService(deps.WorkRepo, deps.SearchClient, authzService)
|
||||
|
||||
return &Application{
|
||||
Author: authorService,
|
||||
@ -73,6 +100,6 @@ func NewApplication(repos *sql.Repositories, searchClient search.SearchClient, a
|
||||
Auth: authService,
|
||||
Authz: authzService,
|
||||
Work: workService,
|
||||
Analytics: analyticsService,
|
||||
Analytics: deps.AnalyticsService,
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,6 @@ type Config struct {
|
||||
// Application configuration
|
||||
Port string
|
||||
ServerPort string
|
||||
PlaygroundPort string
|
||||
Environment string
|
||||
LogLevel string
|
||||
|
||||
@ -84,7 +83,6 @@ func LoadConfig() {
|
||||
// Application configuration
|
||||
Port: getEnv("PORT", "8080"),
|
||||
ServerPort: getEnv("SERVER_PORT", "8080"),
|
||||
PlaygroundPort: getEnv("PLAYGROUND_PORT", "8081"),
|
||||
Environment: getEnv("ENVIRONMENT", "development"),
|
||||
LogLevel: getEnv("LOG_LEVEL", "info"),
|
||||
|
||||
|
||||
@ -163,7 +163,32 @@ func (s *IntegrationTestSuite) SetupSuite(config *TestConfig) {
|
||||
s.T().Fatalf("Failed to create sentiment provider: %v", err)
|
||||
}
|
||||
analyticsService := analytics.NewService(repos.Analytics, analysisRepo, repos.Translation, repos.Work, sentimentProvider)
|
||||
s.App = app.NewApplication(repos, searchClient, analyticsService)
|
||||
jwtManager := platform_auth.NewJWTManager()
|
||||
|
||||
deps := app.Dependencies{
|
||||
WorkRepo: repos.Work,
|
||||
UserRepo: repos.User,
|
||||
AuthorRepo: repos.Author,
|
||||
TranslationRepo: repos.Translation,
|
||||
CommentRepo: repos.Comment,
|
||||
LikeRepo: repos.Like,
|
||||
BookmarkRepo: repos.Bookmark,
|
||||
CollectionRepo: repos.Collection,
|
||||
TagRepo: repos.Tag,
|
||||
CategoryRepo: repos.Category,
|
||||
BookRepo: repos.Book,
|
||||
PublisherRepo: repos.Publisher,
|
||||
SourceRepo: repos.Source,
|
||||
CopyrightRepo: repos.Copyright,
|
||||
MonetizationRepo: repos.Monetization,
|
||||
AnalyticsRepo: repos.Analytics,
|
||||
AuthRepo: repos.Auth,
|
||||
LocalizationRepo: repos.Localization,
|
||||
SearchClient: searchClient,
|
||||
AnalyticsService: analyticsService,
|
||||
JWTManager: jwtManager,
|
||||
}
|
||||
s.App = app.NewApplication(deps)
|
||||
|
||||
// Create a default admin user for tests
|
||||
adminUser := &domain.User{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user