package app import ( "context" "database/sql" "net/http" "sync" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bus" "forge.cadoles.com/arcad/edge/pkg/bus/memory" edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http" "forge.cadoles.com/arcad/edge/pkg/module" "forge.cadoles.com/arcad/edge/pkg/module/auth" "forge.cadoles.com/arcad/edge/pkg/module/cast" "forge.cadoles.com/arcad/edge/pkg/module/net" "forge.cadoles.com/arcad/edge/pkg/storage" "forge.cadoles.com/arcad/edge/pkg/storage/sqlite" "gitlab.com/wpetit/goweb/logger" "forge.cadoles.com/arcad/edge/pkg/bundle" "github.com/dop251/goja" "github.com/go-chi/chi/middleware" "github.com/go-chi/chi/v5" "github.com/pkg/errors" ) type Server struct { bundle bundle.Bundle db *sql.DB server *http.Server serverMutex sync.RWMutex } func (s *Server) Start(ctx context.Context, addr string) (err error) { if s.server != nil { if err := s.Stop(); err != nil { return errors.WithStack(err) } } router := chi.NewRouter() router.Use(middleware.Logger) bus := memory.NewBus() ds := sqlite.NewDocumentStoreWithDB(s.db) bs := sqlite.NewBlobStoreWithDB(s.db) handler := edgeHTTP.NewHandler( edgeHTTP.WithBus(bus), edgeHTTP.WithServerModules(s.getAppModules(bus, ds, bs)...), ) if err := handler.Load(s.bundle); err != nil { return errors.Wrap(err, "could not load app bundle") } router.Handle("/*", handler) server := &http.Server{ Addr: addr, Handler: router, } go func() { defer func() { if recovered := recover(); recovered != nil { if err, ok := recovered.(error); ok { logger.Error(ctx, err.Error(), logger.E(errors.WithStack(err))) return } panic(recovered) } }() defer func() { if err := s.Stop(); err != nil { panic(errors.WithStack(err)) } }() if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { panic(errors.WithStack(err)) } }() s.serverMutex.Lock() s.server = server s.serverMutex.Unlock() return nil } func (s *Server) Running() bool { s.serverMutex.RLock() defer s.serverMutex.RUnlock() return s.server != nil } func (s *Server) Stop() error { if s.server == nil { return nil } defer func() { s.serverMutex.Lock() s.server = nil s.serverMutex.Unlock() }() if err := s.server.Close(); err != nil { panic(errors.WithStack(err)) } return nil } func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStore) []app.ServerModuleFactory { return []app.ServerModuleFactory{ module.ContextModuleFactory(), module.ConsoleModuleFactory(), cast.CastModuleFactory(), module.LifecycleModuleFactory(), net.ModuleFactory(bus), module.RPCModuleFactory(bus), module.StoreModuleFactory(ds), module.BlobModuleFactory(bus, bs), module.Extends( auth.ModuleFactory(), func(o *goja.Object) { if err := o.Set("CLAIM_ROLE", "role"); err != nil { panic(errors.New("could not set 'CLAIM_ROLE' property")) } if err := o.Set("CLAIM_PREFERRED_USERNAME", "preferred_username"); err != nil { panic(errors.New("could not set 'CLAIM_PREFERRED_USERNAME' property")) } }, ), } } func NewServer(bundle bundle.Bundle, db *sql.DB) *Server { return &Server{ bundle: bundle, db: db, } }