// Command geniusrund is geniusrun's backend server: syncs runs from Garmin, // classifies them into workout kinds, and serves the REST API the frontend // talks to. package main import ( "context" "log" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "geniusrun/backend/internal/api" "geniusrun/backend/internal/applog" "geniusrun/backend/internal/auth" "geniusrun/backend/internal/config" "geniusrun/backend/internal/garmin" "geniusrun/backend/internal/store" appsync "geniusrun/backend/internal/sync" ) func main() { cfg, err := config.Load() if err != nil { log.Fatalf("config: %v", err) } slog.SetDefault(applog.NewLogger(cfg.LogLevel, os.Stdout)) db, err := store.Open(cfg.DBPath) if err != nil { log.Fatalf("open database: %v", err) } defer db.Close() overrides, err := db.ConfigValues(context.Background()) if err != nil { log.Fatalf("app config: %v", err) } appCfg, err := config.LoadApp(overrides) if err != nil { log.Fatalf("app config: %v", err) } authVerifier, err := auth.NewOIDCVerifier(context.Background(), auth.OIDCConfig{ IssuerURL: cfg.OIDCIssuerURL, ClientID: cfg.OIDCClientID, ClientSecret: cfg.OIDCClientSecret, RedirectURL: cfg.OIDCRedirectURL, RequiredRole: cfg.OIDCRequiredRole, }) if err != nil { log.Fatalf("oidc: %v", err) } server := api.NewServer(db, garmin.NewClient, garmin.Config{ PythonPath: cfg.GarminPythonPath, TokenStorePath: cfg.GarminTokenStoreRoot, }, appsync.Config{}, authVerifier, api.SessionConfig{ Secret: cfg.SessionSecret, Duration: appCfg.SessionDuration, Secure: cfg.SessionSecure, BackendURL: cfg.BackendURL, FrontendURL: cfg.FrontendURL, }) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() httpServer := &http.Server{Addr: cfg.Addr, Handler: server.Router()} go func() { log.Printf("geniusrund listening on %s", cfg.Addr) if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("http server: %v", err) } }() <-ctx.Done() log.Println("shutting down...") shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := httpServer.Shutdown(shutdownCtx); err != nil { log.Printf("http server shutdown: %v", err) } }