Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not working on Windows #97

Open
crossworth opened this issue May 8, 2021 · 16 comments · May be fixed by #251
Open

Not working on Windows #97

crossworth opened this issue May 8, 2021 · 16 comments · May be fixed by #251

Comments

@crossworth
Copy link

Hi, great project. I'm trying using it on Windows, but can't get it to work.

When running and trying to access the browser, the following error is displayed:
open ui\build\index.html: file does not exist

I have done some diging and have found the problem, but don't know the right "fix", so for this reason I'm opening this issue instead of PR.

Basically on this line, filepath.Abs returns C:\

path, err := filepath.Abs(r.URL.Path)

This causes problems with the check if is /

if path == "/" {

There is a problem with filepath.Join as well, since it will use \ and GO embed.FS will not find the file then.
https://golang.org/pkg/embed/#hdr-Directives

The path separator is a forward slash, even on Windows systems.

The only way I could get it to work was making the following changes:

p := r.URL.Path
if filepath.IsAbs(p) {
	p = srv.indexFilePath()
} else {
	p = path.Join(srv.staticDirPath, p)
}

// ...
func (srv *staticFileServer) indexFilePath() string {
	return path.Join(srv.staticDirPath, srv.indexFileName)
}
@hibiken
Copy link
Owner

hibiken commented May 8, 2021

@crossworth Thank you for reporting this issue!
Apologies for not testing fully on other OSs 🙏

Is this blocking you from using the tool completely? Or have you found a way to work around it until the fix is in?
(I'm trying to gauge the priority of this bug).

@crossworth
Copy link
Author

No worries, I'm able to use with the changes from my last message.

So maybe this can be low priority.

@hibiken
Copy link
Owner

hibiken commented May 9, 2021

Got it, thanks for letting me know!
I'll get to this issue in a few weeks 👍

@SkYNewZ
Copy link

SkYNewZ commented Mar 5, 2022

What do you think about embeding templates ? https://pkg.go.dev/embed

@hibiken
Copy link
Owner

hibiken commented Mar 6, 2022

@SkYNewZ thank you for the comment. There were some changes made to use the embed package since this issue has been created, so this issue may be stale. I'll look into this issue once again soon when I have enough bandwidth.

@eaglebetter
Copy link

The problem is still not fixed

@goxiaoy goxiaoy linked a pull request Jun 20, 2022 that will close this issue
@localhosted
Copy link

Can't use it at all in Windows, only forking and fixing. Any news on this PR(#251)?

@navossoc
Copy link

+1

@4GUO
Copy link

4GUO commented Mar 9, 2023

+1 template: pattern matches no files: ui\build\index.html

@lidaqi001
Copy link

+1 template: pattern matches no files: ui\build\index.html

@4GUO
Copy link

4GUO commented Apr 25, 2023 via email

@windowshopr
Copy link

+1 template: pattern matches no files: ui\build\index.html

@4GUO
Copy link

4GUO commented Oct 26, 2023 via email

@Raindrifter
Copy link

+1
It looks like a repository in disrepair, is there a better fork version?
看起来像是个年久失修的仓库,有没有比较好的fork版本呢?

@4GUO
Copy link

4GUO commented Feb 21, 2024 via email

@Fate0x01
Copy link

Fate0x01 commented Oct 28, 2024

By modifying the static.go file, successfully made the UI open normally under the Windows system.

package asynqmon

import (
	"embed"
	"errors"
	"html/template"
	"io/fs"
	"net/http"
	"path"
	"path/filepath"
	"strings"
)

// uiAssetsHandler is a http.Handler.
// The path to the static file directory and
// the path to the index file within that static directory are used to
// serve the SPA.
type uiAssetsHandler struct {
	rootPath       string
	contents       embed.FS
	staticDirPath  string
	indexFileName  string
	prometheusAddr string
	readOnly       bool
}

// ServeHTTP inspects the URL path to locate a file within the static dir
// on the SPA handler.
// If path '/' is requested, it will serve the index file, otherwise it will
// serve the file specified by the URL path.
func (h *uiAssetsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Get the absolute p to prevent directory traversal.
	p := r.URL.Path
	if filepath.IsAbs(p) {
		p = h.indexFilePath()
	}

	// Get the path relative to the root path.
	if !strings.HasPrefix(p, h.rootPath) {
		http.Error(w, "unexpected path prefix", http.StatusBadRequest)
		return
	}
	p = strings.TrimPrefix(p, h.rootPath)

	if code, err := h.serveFile(w, p); err != nil {
		http.Error(w, err.Error(), code)
		return
	}
}

func (h *uiAssetsHandler) indexFilePath() string {
	return path.Join(h.staticDirPath, h.indexFileName)
}

func (h *uiAssetsHandler) renderIndexFile(w http.ResponseWriter) error {
	// Note: Replace the default delimiter ("{{") with a custom one
	// since webpack escapes the '{' character when it compiles the index.html file.
	// See the "homepage" field in package.json.
	tmpl, err := template.New(h.indexFileName).Delims("/[[", "]]").ParseFS(h.contents, h.indexFilePath())
	if err != nil {
		return err
	}
	data := struct {
		RootPath       string
		PrometheusAddr string
		ReadOnly       bool
	}{
		RootPath:       h.rootPath,
		PrometheusAddr: h.prometheusAddr,
		ReadOnly:       h.readOnly,
	}
	return tmpl.Execute(w, data)
}

// serveFile writes file requested at path and returns http status code and error if any.
// If requested path is root, it serves the index file.
// Otherwise, it looks for file requiested in the static content filesystem
// and serves if a file is found.
// If a requested file is not found in the filesystem, it serves the index file to
// make sure when user refreshes the page in SPA things still work.
func (h *uiAssetsHandler) serveFile(w http.ResponseWriter, p string) (code int, err error) {
	if p == "/" || p == "" {
		if err := h.renderIndexFile(w); err != nil {
			return http.StatusInternalServerError, err
		}
		return http.StatusOK, nil
	}
	p = path.Join(h.staticDirPath, p)
	bytes, err := h.contents.ReadFile(p)
	if err != nil {
		// If path is error (e.g. file not exist, path is a directory), serve index file.
		var pathErr *fs.PathError
		if errors.As(err, &pathErr) {
			if err := h.renderIndexFile(w); err != nil {
				return http.StatusInternalServerError, err
			}
			return http.StatusOK, nil
		}
		return http.StatusInternalServerError, err
	}
	// Setting the MIME type for .js files manually to application/javascript as
	// http.DetectContentType is using https://mimesniff.spec.whatwg.org/ which
	// will not recognize application/javascript for security reasons.
	if strings.HasSuffix(p, ".js") {
		w.Header().Add("Content-Type", "application/javascript; charset=utf-8")
	} else {
		w.Header().Add("Content-Type", http.DetectContentType(bytes))
	}

	if _, err := w.Write(bytes); err != nil {
		return http.StatusInternalServerError, err
	}
	return http.StatusOK, nil
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.