package main import ( "context" "log" "net/http" "time" "spin/internal/config" "spin/internal/db" "spin/internal/httpapi" "spin/internal/seed" "spin/internal/storage" ) func main() { cfg := config.Load() gdb, err := db.Connect(cfg.DatabaseURL) if err != nil { log.Fatalf("database connection failed: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() store, err := storage.New(ctx, cfg) if err != nil { log.Printf("storage init failed (continuing without S3): %v", err) store = nil } else if err := store.EnsureBucket(ctx); err != nil { log.Printf("ensure bucket failed (retrying once): %v", err) time.Sleep(2 * time.Second) if err := store.EnsureBucket(ctx); err != nil { log.Printf("ensure bucket failed (best-effort): %v", err) } } if cfg.SeedData { if err := seed.Run(gdb); err != nil { log.Printf("seed failed (continuing): %v", err) } } else { log.Printf("seed skipped (SEED=false)") } router := httpapi.NewRouter(gdb, store, cfg) addr := ":" + cfg.Port srv := &http.Server{ Addr: addr, Handler: router, ReadTimeout: 30 * time.Second, WriteTimeout: 60 * time.Second, } log.Printf("spin backend listening on %s (devAuth=%v)", addr, cfg.DevAuth) if err := srv.ListenAndServe(); err != nil { log.Fatalf("server error: %v", err) } }