package main import ( "context" "embed" "fmt" "io/fs" "log" "net/http" "os" "path/filepath" "strings" originclient "git.warky.dev/wdevs/origin_client/go" "wedding-server/internal/api" "wedding-server/internal/config" "wedding-server/internal/mail" "wedding-server/internal/store" "wedding-server/internal/webhook" ) //go:embed all:web/dist var embeddedWeb embed.FS func main() { cfgPath := config.Path() cfg, err := config.Load(cfgPath) if err != nil { log.Fatalf("config: %v", err) } if err := os.MkdirAll(cfg.Storage.DataDir, 0o755); err != nil { log.Fatalf("data dir: %v", err) } st, err := store.Open(cfg.Storage.DataDir) if err != nil { log.Fatalf("store: %v", err) } if len(os.Args) > 1 { code := runCLI(cfg.Storage.DataDir, st, os.Args[1:]) st.Close() os.Exit(code) } defer st.Close() mailer := mail.New(cfg.Email, cfg.Site.URL) wh := webhook.New(cfg.Webhook) if cfg.Origin.ServiceKey == "" { log.Printf("origin registration disabled: origin.service_key is not configured") } else { origin, err := originclient.New(originclient.Config{ URL: cfg.Origin.URL, ServiceKey: cfg.Origin.ServiceKey, Type: "generic", Version: cfg.Origin.Version, Name: "ZoeShaldon", AppType: "go", Port: cfg.Server.Port, BaseURL: cfg.Site.URL, Description: "CronoCraft App - ZoeShaldon wedding site", }) if err != nil { log.Fatalf("origin client: %v", err) } go origin.Run(context.Background()) } mux := http.NewServeMux() api.New(cfg, st, mailer, wh).Routes(mux) uploadsDir := filepath.Join(cfg.Storage.DataDir, "photos") mux.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir(uploadsDir)))) webFS, err := fs.Sub(embeddedWeb, "web/dist") if err != nil { log.Fatalf("embedded web assets: %v", err) } mux.Handle("/", staticHandler(webFS)) addr := fmt.Sprintf(":%d", cfg.Server.Port) log.Printf("listening on %s", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatal(err) } } // staticHandler serves the prerendered SvelteKit build, where routes are // written as flat files (e.g. "rsvp.html") rather than "rsvp/index.html". func staticHandler(fsys fs.FS) http.Handler { fileServer := http.FileServer(http.FS(fsys)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { p := strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, "/"), "/") if p == "" { p = "index.html" } // A route name (e.g. "photos") can collide with a static asset // directory of the same name; prefer the prerendered page in // that case instead of falling through to a directory listing. info, statErr := fs.Stat(fsys, p) if statErr != nil || info.IsDir() { if hinfo, err := fs.Stat(fsys, p+".html"); err == nil && !hinfo.IsDir() { r2 := *r r2.URL.Path = "/" + p + ".html" fileServer.ServeHTTP(w, &r2) return } } fileServer.ServeHTTP(w, r) }) }