package main import ( "context" "embed" "flag" "fmt" "io/fs" "log" "net/http" "os" "os/signal" "path/filepath" "syscall" "fn-registry/functions/infra" ) //go:embed all:frontend/dist var frontendDist embed.FS func main() { flags := flag.NewFlagSet("kanban", flag.ExitOnError) port := flags.Int("port", 8095, "HTTP port") dbPath := flags.String("db", "operations.db", "SQLite database path") flags.Parse(os.Args[1:]) db, err := openDB(*dbPath) if err != nil { log.Fatalf("open db: %v", err) } defer db.Close() wd := chatWorkdir(*dbPath) logger := newChatLogger(filepath.Join(wd, "chat.log")) log.Printf("chat tool log: %s", logger.path) mux := infra.HTTPRouter(apiRoutes(db, wd, logger)) feHandler := frontendHandler() if feHandler != nil { mux.Handle("/", feHandler) log.Printf("serving frontend from embedded dist/") } else { log.Printf("no frontend build found, API-only mode") } 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 server starting on http://0.0.0.0%s", addr) 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) } } func frontendHandler() http.Handler { sub, err := fs.Sub(frontendDist, "frontend/dist") if err != nil { return nil } entries, _ := fs.ReadDir(sub, ".") if len(entries) == 0 { return nil } return infra.SPAHandler(sub, "index.html") }