package http import ( "io/ioutil" "net/http" "strings" "sync" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/bundle" "forge.cadoles.com/arcad/edge/pkg/bus" "forge.cadoles.com/arcad/edge/pkg/sdk" "github.com/igm/sockjs-go/v3/sockjs" "github.com/pkg/errors" ) const ( sockJSPathPrefix = "/sock" clientJSPath = "/client.js" backendMainScript = "backend/main.js" ) type Handler struct { bundle bundle.Bundle public http.Handler sockjs http.Handler bus bus.Bus sockjsOpts sockjs.Options backend *app.Backend backendModuleFactories []app.BackendModuleFactory mutex sync.RWMutex } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mutex.RLock() defer h.mutex.RUnlock() switch { case r.URL.Path == clientJSPath: serveFile(w, r, &sdk.FS, "client/dist/client.js") case r.URL.Path == clientJSPath+".map": serveFile(w, r, &sdk.FS, "client/dist/client.js.map") case strings.HasPrefix(r.URL.Path, sockJSPathPrefix): h.sockjs.ServeHTTP(w, r) default: h.public.ServeHTTP(w, r) } } func (h *Handler) Load(bdle bundle.Bundle) error { h.mutex.Lock() defer h.mutex.Unlock() file, _, err := bdle.File(backendMainScript) if err != nil { return errors.Wrap(err, "could not open backend main script") } mainScript, err := ioutil.ReadAll(file) if err != nil { return errors.Wrap(err, "could not read backend main script") } backend := app.NewBackend(h.backendModuleFactories...) if err := backend.Load(backendMainScript, string(mainScript)); err != nil { return errors.WithStack(err) } fs := bundle.NewFileSystem("public", bdle) public := http.FileServer(fs) sockjs := sockjs.NewHandler(sockJSPathPrefix, h.sockjsOpts, h.handleSockJSSession) if h.backend != nil { h.backend.Stop() } if err := backend.Start(); err != nil { return errors.WithStack(err) } h.bundle = bdle h.backend = backend h.public = public h.sockjs = sockjs return nil } func NewHandler(funcs ...HandlerOptionFunc) *Handler { opts := defaultHandlerOptions() for _, fn := range funcs { fn(opts) } handler := &Handler{ sockjsOpts: opts.SockJS, backendModuleFactories: opts.BackendModuleFactories, bus: opts.Bus, } return handler }