Files
zoe-shaldon/server/main.go
T
warkanum f96465b798 feat(cli): add CLI for managing RSVPs and photo uploads
* implement runCLI function to handle subcommands
* add runRSVPCLI and runPhotosCLI for RSVP and photo management
* include usage instructions for CLI commands
* integrate CLI execution in main function
2026-08-23 11:45:48 +02:00

113 lines
2.9 KiB
Go

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)
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)
})
}