package main import ( "embed" "io/fs" "log" "net/http" "strings" "github.com/squid/MigaduAdmin/internal/api" "github.com/squid/MigaduAdmin/internal/auth" "github.com/squid/MigaduAdmin/internal/config" "github.com/squid/MigaduAdmin/internal/migadu" "github.com/squid/MigaduAdmin/internal/store" ) //go:embed all:ui var uiEmbed embed.FS func main() { cfg, err := config.Load() if err != nil { log.Fatalf("config: %v", err) } st, err := store.Open(cfg.DatabasePath) if err != nil { log.Fatalf("database: %v", err) } defer st.Close() authService := auth.NewService(st, cfg) migaduClient := migadu.NewClient(cfg.MigaduBaseURL, cfg.MigaduUser, cfg.MigaduAPIKey) server := api.New(cfg, authService, st, migaduClient) staticHandler, err := spaHandler() if err != nil { log.Fatalf("ui assets: %v", err) } handler := server.Router(staticHandler) log.Printf("MigaduAdmin listening on %s", cfg.ListenAddr) log.Printf( "Migadu credentials: user=%q api_key=%s base=%s", cfg.MigaduUser, cfg.MaskedMigaduKey(), cfg.MigaduBaseURL, ) if err := http.ListenAndServe(cfg.ListenAddr, handler); err != nil { log.Fatalf("server: %v", err) } } func spaHandler() (http.Handler, error) { sub, err := fs.Sub(uiEmbed, "ui") if err != nil { return nil, err } fileServer := http.FileServer(http.FS(sub)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, "/") if path == "" { path = "index.html" } if _, err := fs.Stat(sub, path); err == nil && !strings.HasSuffix(path, "/") { fileServer.ServeHTTP(w, r) return } // SPA fallback for client-side routes index, err := fs.ReadFile(sub, "index.html") if err != nil { http.Error(w, "ui not available", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write(index) }), nil }