package app import ( "database/sql" "net/http" "path/filepath" "forge.cadoles.com/arcad/edge/pkg/bus/memory" appHTTP "forge.cadoles.com/arcad/edge/pkg/http" "forge.cadoles.com/arcad/edge/pkg/module" "forge.cadoles.com/arcad/edge/pkg/module/cast" "forge.cadoles.com/arcad/edge/pkg/storage/sqlite" "gitlab.com/wpetit/goweb/logger" "forge.cadoles.com/arcad/edge/pkg/bundle" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/pkg/errors" "github.com/urfave/cli/v2" _ "modernc.org/sqlite" ) func RunCommand() *cli.Command { return &cli.Command{ Name: "run", Usage: "Run the specified app bundle", Flags: []cli.Flag{ &cli.StringFlag{ Name: "path", Usage: "use `PATH` as app bundle (zipped bundle or directory)", Aliases: []string{"p"}, Value: ".", }, &cli.StringFlag{ Name: "address", Usage: "use `ADDRESS` as http server listening address", Aliases: []string{"a"}, Value: ":8080", }, &cli.StringFlag{ Name: "log-format", Usage: "use `LOG-FORMAT` ('json' or 'human')", Value: "human", }, &cli.IntFlag{ Name: "log-level", Usage: "use `LOG-LEVEL` (0: debug -> 5: fatal)", Value: 0, }, &cli.StringFlag{ Name: "storage-file", Usage: "use `FILE` for SQLite storage database", Value: "data.sqlite", }, }, Action: func(ctx *cli.Context) error { address := ctx.String("address") path := ctx.String("path") logFormat := ctx.String("log-format") logLevel := ctx.Int("log-level") storageFile := ctx.String("storage-file") logger.SetFormat(logger.Format(logFormat)) logger.SetLevel(logger.Level(logLevel)) cmdCtx := ctx.Context absPath, err := filepath.Abs(path) if err != nil { return errors.Wrapf(err, "could not resolve path '%s'", path) } logger.Info(cmdCtx, "opening app bundle", logger.F("path", absPath)) bundle, err := bundle.FromPath(path) if err != nil { return errors.Wrapf(err, "could not open path '%s' as an app bundle", path) } mux := chi.NewMux() mux.Use(middleware.Logger) bus := memory.NewBus() db, err := sql.Open("sqlite", storageFile) if err != nil { return errors.Wrapf(err, "could not open database with path '%s'", storageFile) } documentStore := sqlite.NewDocumentStoreWithDB(db) blobStore := sqlite.NewBlobStoreWithDB(db) handler := appHTTP.NewHandler( appHTTP.WithBus(bus), appHTTP.WithServerModules( module.ContextModuleFactory(), module.ConsoleModuleFactory(), cast.CastModuleFactory(), module.LifecycleModuleFactory(bus), module.NetModuleFactory(bus), module.RPCModuleFactory(bus), module.StoreModuleFactory(documentStore), module.BlobModuleFactory(bus, blobStore), ), ) if err := handler.Load(bundle); err != nil { return errors.Wrap(err, "could not load app bundle") } mux.Handle("/*", handler) logger.Info(cmdCtx, "listening", logger.F("address", address)) if err := http.ListenAndServe(address, mux); err != nil { return errors.WithStack(err) } return nil }, } }