cmd/weimar/serve.go (view raw)
1package main
2
3import (
4 "embed"
5 "fmt"
6 "io/fs"
7 "log/slog"
8 "os/signal"
9 "syscall"
10
11 "github.com/mjensen/weimar/internal/auth"
12 "github.com/mjensen/weimar/internal/config"
13 "github.com/mjensen/weimar/internal/db"
14 "github.com/mjensen/weimar/internal/server"
15 "github.com/spf13/cobra"
16 "github.com/spf13/viper"
17)
18
19//go:embed web/build/*
20var webFS embed.FS
21
22var adminEmail string
23var adminPassword string
24
25var serveCmd = &cobra.Command{
26 Use: "serve",
27 Short: "Start the HTTP server",
28 RunE: func(cmd *cobra.Command, args []string) error {
29 cfg := config.FromViper()
30
31 webSub, err := fs.Sub(webFS, "web/build")
32 if err != nil {
33 return fmt.Errorf("reading embedded web build: %w", err)
34 }
35
36 database, err := db.Open(cfg.Database.Path)
37 if err != nil {
38 return fmt.Errorf("opening database: %w", err)
39 }
40 defer database.Close()
41
42 if err := database.Migrate(cmd.Context()); err != nil {
43 return fmt.Errorf("running migrations: %w", err)
44 }
45
46 // Create admin user on first run if no users exist and credentials are provided.
47 if adminEmail != "" && adminPassword != "" {
48 users, err := database.ListUsers(cmd.Context())
49 if err != nil {
50 return fmt.Errorf("checking for existing users: %w", err)
51 }
52 if len(users) == 0 {
53 if _, err := auth.Register(cmd.Context(), database, adminEmail, adminPassword, true); err != nil {
54 return fmt.Errorf("creating admin user: %w", err)
55 }
56 slog.Info("created initial admin user", "username", adminEmail)
57 }
58 }
59
60 srv, err := server.New(cfg, webSub, database)
61 if err != nil {
62 return fmt.Errorf("creating server: %w", err)
63 }
64
65 ctx, stop := signal.NotifyContext(cmd.Context(), syscall.SIGINT, syscall.SIGTERM)
66 defer stop()
67
68 slog.Info("starting weimar", "host", cfg.Server.Host, "port", cfg.Server.Port)
69 if err := srv.Start(ctx); err != nil {
70 return fmt.Errorf("server error: %w", err)
71 }
72
73 return nil
74 },
75}
76
77func init() {
78 rootCmd.AddCommand(serveCmd)
79 serveCmd.Flags().StringVar(&adminEmail, "admin-email", "", "admin email for first-run setup")
80 serveCmd.Flags().StringVar(&adminPassword, "admin-password", "", "admin password for first-run setup")
81 serveCmd.Flags().Bool("behind-proxy", false, "set Secure flag on cookies and trust X-Forwarded headers")
82 viper.BindPFlag("server.behind_proxy", serveCmd.Flags().Lookup("behind-proxy"))
83 viper.BindPFlag("server.host", serveCmd.Flags().Lookup("host"))
84 viper.BindPFlag("server.port", serveCmd.Flags().Lookup("port"))
85}