package http import ( "context" "io" "net/http" "sync" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bundle" "forge.cadoles.com/arcad/edge/pkg/bus" "github.com/go-chi/chi/v5" "github.com/igm/sockjs-go/v3/sockjs" "github.com/pkg/errors" ) const ( sockJSPathPrefix = "/edge/sock" serverMainScript = "server/main.js" ) type Handler struct { bundle bundle.Bundle public http.Handler router chi.Router sockjs http.Handler bus bus.Bus sockjsOpts sockjs.Options server *app.Server serverModuleFactories []app.ServerModuleFactory httpClient *http.Client mutex sync.RWMutex } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.router.ServeHTTP(w, r) } func (h *Handler) Load(ctx context.Context, bdle bundle.Bundle) error { h.mutex.Lock() defer h.mutex.Unlock() file, _, err := bdle.File(serverMainScript) if err != nil { return errors.Wrap(err, "could not open server main script") } mainScript, err := io.ReadAll(file) if err != nil { return errors.Wrap(err, "could not read server main script") } server := app.NewServer(h.serverModuleFactories...) fs := bundle.NewFileSystem("public", bdle) public := HTML5Fileserver(fs) sockjs := sockjs.NewHandler(sockJSPathPrefix, h.sockjsOpts, h.handleSockJSSession) if h.server != nil { h.server.Stop() } if err := server.Start(ctx, serverMainScript, string(mainScript)); err != nil { return errors.WithStack(err) } h.bundle = bdle h.server = server h.public = public h.sockjs = sockjs return nil } func NewHandler(funcs ...HandlerOptionFunc) *Handler { opts := defaultHandlerOptions() for _, fn := range funcs { fn(opts) } router := chi.NewRouter() handler := &Handler{ sockjsOpts: opts.SockJS, router: router, serverModuleFactories: opts.ServerModuleFactories, httpClient: opts.HTTPClient, bus: opts.Bus, } for _, middleware := range opts.HTTPMiddlewares { router.Use(middleware) } router.Route("/edge", func(r chi.Router) { r.Route("/sdk", func(r chi.Router) { r.Get("/client.js", handler.handleSDKClient) r.Get("/client.js.map", handler.handleSDKClientMap) }) for _, fn := range opts.HTTPMounts { r.Group(func(r chi.Router) { r.Use(handler.contextMiddleware) fn(r) }) } r.HandleFunc("/sock/*", handler.handleSockJS) }) router.Get("/*", handler.handleAppFiles) return handler }