// 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/auth" "geniusrun/backend/internal/config" "geniusrun/backend/internal/garmin" "geniusrun/backend/internal/log" "geniusrun/backend/internal/store" ) func main() { envCfg, err := config.LoadEnv() if err != nil { log.Fatalf("config: %v", err) } slog.SetDefault(applog.NewLogger(envCfg.LogLevel, os.Stdout)) db, err := store.Open(envCfg.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: envCfg.OIDCIssuerURL, ClientID: envCfg.OIDCClientID, ClientSecret: envCfg.OIDCClientSecret, RedirectURL: envCfg.OIDCRedirectURL, RequiredRole: envCfg.OIDCRequiredRole, }) if err != nil { log.Fatalf("oidc: %v", err) } server := api.NewServer( db, garmin.NewClient, garmin.ClientConfig{ PythonPath: envCfg.PythonPath, TokenStorePath: envCfg.TokenStoreRoot, }, garmin.SyncConfig{}, authVerifier, api.SessionConfig{ Secret: envCfg.SessionSecret, Duration: appCfg.SessionDuration, Secure: envCfg.SessionSecure, BackendURL: envCfg.BackendURL, FrontendURL: envCfg.FrontendURL, }) for _, e := range envCfg.DisplayEnv() { server.EnvVars = append(server.EnvVars, api.EnvVar{Name: e.Name, Value: e.Value}) } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() httpServer := &http.Server{Addr: envCfg.BackendAddr, Handler: server.Router()} go func() { log.Printf("geniusrund listening on %s", envCfg.BackendAddr) 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) } }