package main import ( "context" "log" "net/http" "os" "path/filepath" "maisym.com/hsa/internal/classify" "maisym.com/hsa/internal/config" "maisym.com/hsa/internal/storage" "maisym.com/hsa/internal/web" ) func main() { // Best-effort local dev convenience: load .env if present. if err := config.LoadDotEnv(".env"); err != nil { log.Fatalf("loading .env: %v", err) } cfg, err := config.Load() if err != nil { log.Fatalf("config: %v", err) } if err := os.MkdirAll(filepath.Dir(cfg.DBPath), 0o700); err != nil { log.Fatalf("create db dir: %v", err) } store, err := storage.Open(cfg.DBPath) if err != nil { log.Fatalf("open store: %v", err) } defer store.Close() // Load the people/categories catalog and seed it into the DB (insert-if-absent). catalog, err := config.LoadCatalog(cfg.ConfigPath) if err != nil { log.Fatalf("config catalog: %v", err) } if err := store.Seed(catalog.CategoryNames(), catalog.PersonLabels()); err != nil { log.Fatalf("seed catalog: %v", err) } // Receipt auto-classification is best-effort: only enabled when an API key is set. var classifier *classify.Classifier if cfg.ClassifyAPIKey != "" { classifier = classify.New(cfg.ClassifyAPIKey, cfg.ClassifyModel, catalog.Persons, catalog.Categories) log.Printf("receipt classification enabled (model %s)", cfg.ClassifyModel) } else { log.Printf("receipt classification disabled (CLAUDE_API_KEY not set)") } srv, err := web.NewServer(context.Background(), cfg, store, classifier) if err != nil { log.Fatalf("server init: %v", err) } log.Printf("HSA listening on %s (issuer %s)", cfg.ListenAddr, cfg.IssuerURL) if err := http.ListenAndServe(cfg.ListenAddr, srv.Routes()); err != nil { log.Fatalf("listen: %v", err) } }