diff --git a/internal/api/.gitignore b/internal/api/.gitignore new file mode 100644 index 0000000..624f3dc --- /dev/null +++ b/internal/api/.gitignore @@ -0,0 +1 @@ +webui/ \ No newline at end of file diff --git a/internal/api/api.go b/internal/api/api.go index e61f85f..089c2b7 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -48,6 +48,8 @@ func (as *ApiServer) Start(config config.ApiConfig) { mux.HandleFunc("/schema/routes.schema.json", handleRoutesSchema) mux.HandleFunc("/schema/modules.schema.json", handleModulesSchema) mux.HandleFunc("/schema/processors.schema.json", handleProcessorsSchema) + mux.HandleFunc("/ui", handleWebUI) + mux.HandleFunc("/ui/", handleWebUI) as.serverMu.Lock() defer as.serverMu.Unlock() diff --git a/internal/api/no_webui.go b/internal/api/no_webui.go new file mode 100644 index 0000000..32ed14e --- /dev/null +++ b/internal/api/no_webui.go @@ -0,0 +1,12 @@ +//go:build !showbridge_webui + +package api + +import ( + "net/http" +) + +func handleWebUI(w http.ResponseWriter, req *http.Request) { + + http.Error(w, "Web UI is not enabled", http.StatusNotImplemented) +} diff --git a/internal/api/webui.go b/internal/api/webui.go new file mode 100644 index 0000000..456c6fa --- /dev/null +++ b/internal/api/webui.go @@ -0,0 +1,73 @@ +//go:build showbridge_webui + +package api + +import ( + "embed" + "errors" + "io/fs" + "net/http" + "path" + "strings" + + _ "embed" +) + +//go:embed webui +var webUIFS embed.FS + +var fsPrefix = "webui/browser" + +var index = path.Join(fsPrefix, "index.html") + +func handleWebUI(w http.ResponseWriter, req *http.Request) { + requestedPath := strings.TrimLeft(req.URL.Path, "/ui") + switch req.Method { + case http.MethodGet: + var pathToLoad string + stat, err := fs.Stat(webUIFS, path.Join(fsPrefix, requestedPath)) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + pathToLoad = index + } else { + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + } + if stat != nil { + if stat.IsDir() { + pathToLoad = index + } else { + pathToLoad = path.Join(fsPrefix, requestedPath) + } + } + file, err := webUIFS.ReadFile(pathToLoad) + if err != nil { + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + w.Header().Set("Access-Control-Allow-Origin", "*") + + switch path.Ext(pathToLoad) { + case ".js": + w.Header().Set("Content-Type", "application/javascript") + case ".css": + w.Header().Set("Content-Type", "text/css") + case ".html": + w.Header().Set("Content-Type", "text/html") + case ".ico": + w.Header().Set("Content-Type", "image/x-icon") + } + w.Write(file) + return + + case http.MethodOptions: + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + w.WriteHeader(http.StatusOK) + default: + w.Header().Set("Access-Control-Allow-Origin", "*") + w.WriteHeader(http.StatusMethodNotAllowed) + } +}