first commit
Made-with: Cursor
This commit is contained in:
139
cmd/server/main.go
Normal file
139
cmd/server/main.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/calendarapi/internal/api"
|
||||
"github.com/calendarapi/internal/api/handlers"
|
||||
"github.com/calendarapi/internal/auth"
|
||||
"github.com/calendarapi/internal/config"
|
||||
"github.com/calendarapi/internal/middleware"
|
||||
"github.com/calendarapi/internal/repository"
|
||||
"github.com/calendarapi/internal/scheduler"
|
||||
"github.com/calendarapi/internal/service"
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
pool, err := pgxpool.New(ctx, cfg.DatabaseURL)
|
||||
if err != nil {
|
||||
log.Fatalf("connect to database: %v", err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
log.Fatalf("ping database: %v", err)
|
||||
}
|
||||
log.Println("connected to PostgreSQL")
|
||||
|
||||
runMigrations(cfg.DatabaseURL)
|
||||
|
||||
queries := repository.New(pool)
|
||||
|
||||
jwtManager := auth.NewJWTManager(cfg.JWTSecret)
|
||||
|
||||
var sched service.ReminderScheduler
|
||||
if cfg.RedisAddr != "" {
|
||||
realSched := scheduler.NewScheduler(cfg.RedisAddr)
|
||||
defer realSched.Close()
|
||||
sched = realSched
|
||||
|
||||
worker := scheduler.NewReminderWorker(queries)
|
||||
asynqSrv := scheduler.StartWorker(cfg.RedisAddr, worker)
|
||||
defer asynqSrv.Shutdown()
|
||||
|
||||
log.Println("Redis connected, background jobs enabled")
|
||||
} else {
|
||||
sched = scheduler.NoopScheduler{}
|
||||
log.Println("Redis not configured, background jobs disabled")
|
||||
}
|
||||
|
||||
auditSvc := service.NewAuditService(queries)
|
||||
authSvc := service.NewAuthService(pool, queries, jwtManager, auditSvc)
|
||||
userSvc := service.NewUserService(pool, queries, auditSvc)
|
||||
calSvc := service.NewCalendarService(pool, queries, auditSvc)
|
||||
eventSvc := service.NewEventService(pool, queries, calSvc, auditSvc, sched)
|
||||
contactSvc := service.NewContactService(queries, auditSvc)
|
||||
availSvc := service.NewAvailabilityService(queries, calSvc, eventSvc)
|
||||
bookingSvc := service.NewBookingService(pool, queries, calSvc, eventSvc)
|
||||
apiKeySvc := service.NewAPIKeyService(queries)
|
||||
reminderSvc := service.NewReminderService(queries, calSvc, sched)
|
||||
attendeeSvc := service.NewAttendeeService(queries, calSvc)
|
||||
|
||||
h := api.Handlers{
|
||||
Auth: handlers.NewAuthHandler(authSvc, userSvc),
|
||||
User: handlers.NewUserHandler(userSvc),
|
||||
Calendar: handlers.NewCalendarHandler(calSvc),
|
||||
Sharing: handlers.NewSharingHandler(calSvc),
|
||||
Event: handlers.NewEventHandler(eventSvc),
|
||||
Reminder: handlers.NewReminderHandler(reminderSvc),
|
||||
Attendee: handlers.NewAttendeeHandler(attendeeSvc),
|
||||
Contact: handlers.NewContactHandler(contactSvc),
|
||||
Availability: handlers.NewAvailabilityHandler(availSvc),
|
||||
Booking: handlers.NewBookingHandler(bookingSvc),
|
||||
APIKey: handlers.NewAPIKeyHandler(apiKeySvc),
|
||||
ICS: handlers.NewICSHandler(calSvc, eventSvc, queries),
|
||||
}
|
||||
|
||||
authMW := middleware.NewAuthMiddleware(jwtManager, queries)
|
||||
rateLimiter := middleware.NewRateLimiter(100, 200)
|
||||
|
||||
router := api.NewRouter(h, authMW, rateLimiter)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: ":" + cfg.ServerPort,
|
||||
Handler: router,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Printf("server starting on port %s (env=%s)", cfg.ServerPort, cfg.Env)
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
log.Println("shutting down server...")
|
||||
|
||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer shutdownCancel()
|
||||
|
||||
if err := srv.Shutdown(shutdownCtx); err != nil {
|
||||
log.Fatalf("server shutdown error: %v", err)
|
||||
}
|
||||
log.Println("server stopped")
|
||||
}
|
||||
|
||||
func runMigrations(databaseURL string) {
|
||||
m, err := migrate.New("file://migrations", databaseURL)
|
||||
if err != nil {
|
||||
log.Printf("migration init: %v (skipping)", err)
|
||||
return
|
||||
}
|
||||
defer m.Close()
|
||||
|
||||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||
log.Fatalf("migration error: %v", err)
|
||||
}
|
||||
v, dirty, _ := m.Version()
|
||||
log.Printf("migrations applied (version=%d, dirty=%v)", v, dirty)
|
||||
}
|
||||
Reference in New Issue
Block a user