package main import ( "embed" "fmt" "io/fs" "log/slog" "os" "os/signal" "syscall" "codeberg.org/maxwelljensen/weimar/internal/auth" "codeberg.org/maxwelljensen/weimar/internal/config" "codeberg.org/maxwelljensen/weimar/internal/db" "codeberg.org/maxwelljensen/weimar/internal/server" "github.com/spf13/cobra" "github.com/spf13/viper" ) //go:embed web/build/* var webFS embed.FS var adminEmail string var adminPassword string var serveCmd = &cobra.Command{ Use: "serve", Short: "Start the HTTP server", RunE: func(cmd *cobra.Command, args []string) error { cfg := config.FromViper() webSub, err := fs.Sub(webFS, "web/build") if err != nil { return fmt.Errorf("reading embedded web build: %w", err) } database, err := db.Open(cfg.Database.Path) if err != nil { return fmt.Errorf("opening database: %w", err) } defer database.Close() if err := database.Migrate(cmd.Context()); err != nil { return fmt.Errorf("running migrations: %w", err) } // Create admin user on first run if no users exist and credentials are provided. if adminEmail != "" && adminPassword != "" { users, err := database.ListUsers(cmd.Context()) if err != nil { return fmt.Errorf("checking for existing users: %w", err) } if len(users) == 0 { if _, err := auth.Register(cmd.Context(), database, adminEmail, adminPassword, true); err != nil { return fmt.Errorf("creating admin user: %w", err) } slog.Info("created initial admin user", "username", adminEmail) } } srv, err := server.New(&cfg, webSub, database) if err != nil { return fmt.Errorf("creating server: %w", err) } // Handle SIGHUP for config reload. sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGHUP) go func() { for range sig { slog.Info("received SIGHUP, reloading config") if err := initConfig(); err != nil { slog.Warn("config reload failed", "error", err) continue } srv.ReloadConfig(config.FromViper()) slog.Info("config reloaded") } }() ctx, stop := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM) defer stop() slog.Info("starting weimar", "host", cfg.Server.Host, "port", cfg.Server.Port) return srv.Start(ctx) }, } func init() { rootCmd.AddCommand(serveCmd) serveCmd.Flags().StringVar(&adminEmail, "admin-email", "", "admin email for first-run setup") serveCmd.Flags().StringVar(&adminPassword, "admin-password", "", "admin password for first-run setup") serveCmd.Flags().Bool("behind-proxy", false, "set Secure flag on cookies and trust X-Forwarded headers") viper.BindPFlag("server.behind_proxy", serveCmd.Flags().Lookup("behind-proxy")) viper.BindPFlag("server.host", serveCmd.Flags().Lookup("host")) viper.BindPFlag("server.port", serveCmd.Flags().Lookup("port")) }