forgejo-tickets/cmd/server/main.go

123 lines
3.4 KiB
Go

package main
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
"github.com/mattnite/forgejo-tickets/internal/auth"
"github.com/mattnite/forgejo-tickets/internal/config"
"github.com/mattnite/forgejo-tickets/internal/database"
"github.com/mattnite/forgejo-tickets/internal/email"
"github.com/mattnite/forgejo-tickets/internal/forgejo"
adminhandlers "github.com/mattnite/forgejo-tickets/internal/handlers/admin"
publichandlers "github.com/mattnite/forgejo-tickets/internal/handlers/public"
"github.com/mattnite/forgejo-tickets/internal/templates"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
gin.SetMode(gin.ReleaseMode)
cfg, err := config.Load()
if err != nil {
log.Fatal().Msgf("failed to load config: %v", err)
}
db, err := database.Connect(cfg.DatabaseURL)
if err != nil {
log.Fatal().Msgf("failed to connect to database: %v", err)
}
if err := database.RunMigrations(db); err != nil {
log.Fatal().Msgf("failed to run migrations: %v", err)
}
renderer, err := templates.NewRenderer()
if err != nil {
log.Fatal().Msgf("failed to initialize templates: %v", err)
}
emailClient := email.NewClient(cfg.PostmarkServerToken, cfg.PostmarkFromEmail, cfg.BaseURL)
forgejoClient := forgejo.NewClient(cfg.ForgejoURL, cfg.ForgejoAPIToken)
sessionStore := auth.NewPGStore(db, []byte(cfg.SessionSecret))
authService := auth.NewService(db, sessionStore, emailClient)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go sessionStore.Cleanup(ctx, 30*time.Minute)
publicRouter := publichandlers.NewRouter(publichandlers.Dependencies{
DB: db,
Renderer: renderer,
Auth: authService,
SessionStore: sessionStore,
EmailClient: emailClient,
ForgejoClient: forgejoClient,
Config: cfg,
})
adminRouter := adminhandlers.NewRouter(adminhandlers.Dependencies{
DB: db,
Renderer: renderer,
Auth: authService,
EmailClient: emailClient,
Config: cfg,
})
publicServer := &http.Server{
Addr: cfg.PublicAddr,
Handler: publicRouter,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
adminServer := &http.Server{
Addr: cfg.AdminAddr,
Handler: adminRouter,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
log.Info().Msgf("Public server listening on %s", cfg.PublicAddr)
if err := publicServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal().Msgf("public server error: %v", err)
}
}()
go func() {
log.Info().Msgf("Admin server listening on %s", cfg.AdminAddr)
if err := adminServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal().Msgf("admin server error: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Info().Msg("Shutting down servers...")
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer shutdownCancel()
if err := publicServer.Shutdown(shutdownCtx); err != nil {
log.Error().Err(err).Msg("public server shutdown error")
}
if err := adminServer.Shutdown(shutdownCtx); err != nil {
log.Error().Err(err).Msg("admin server shutdown error")
}
cancel()
log.Info().Msg("Servers stopped")
}