package main import ( "context" "encoding/json" "flag" "fmt" "log" "net/http" "os" "os/signal" "syscall" "fn-registry/functions/infra" ) const syncLayerVersion = "v0.1.0" func main() { flags := flag.NewFlagSet("kanban_cpp_backend", flag.ExitOnError) port := flags.Int("port", 8403, "HTTP port") dbPath := flags.String("db", "operations.db", "SQLite database path") flagsPath := flags.String("flags", "dev/feature_flags.json", "Feature flags JSON path (missing file → all disabled)") flags.Parse(os.Args[1:]) featureFlags, err := loadFeatureFlags(*flagsPath) if err != nil { log.Fatalf("load feature flags: %v", err) } for name, fl := range featureFlags.Flags { log.Printf("feature flag %q enabled=%v", name, fl.Enabled) } db, err := openDB(*dbPath) if err != nil { log.Fatalf("open db: %v", err) } defer db.Close() // SSE: hub + fsnotify watcher for dev/issues + dev/flows. globalHub = NewHub() startBoardsWatcher(globalHub) mux := infra.HTTPRouter(apiRoutes(db, &featureFlags)) mux.HandleFunc("/health", handleHealth(*port)) chain := infra.HTTPMiddlewareChain( infra.HTTPLoggerMiddleware(os.Stdout), infra.HTTPCORSMiddleware([]string{"*"}, []string{"GET", "POST", "PATCH", "DELETE", "OPTIONS"}), ) handler := chain(mux) addr := fmt.Sprintf(":%d", *port) log.Printf("kanban_cpp_backend starting on http://0.0.0.0%s (sync layer %s)", addr, syncLayerVersion) log.Printf("database: %s", *dbPath) ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() if err := infra.HTTPServe(addr, handler, ctx); err != nil { log.Fatalf("server: %v", err) } } // handleHealth returns 200 with a small JSON describing the service. No auth. func handleHealth(port int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "status": "ok", "port": port, "sync_layer": syncLayerVersion, }) } }