first commit

Made-with: Cursor
This commit is contained in:
Michilis
2026-02-28 02:17:55 +00:00
commit 41f6ae916f
92 changed files with 12332 additions and 0 deletions

139
cmd/server/main.go Normal file
View 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)
}