diff --git a/Dockerfile b/Dockerfile index 73419e5..e9ab90c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,6 @@ RUN go build \ FROM gcr.io/distroless/base:latest WORKDIR /root COPY --from=build /app/pasty . -COPY web ./web/ +COPY internal/web/web ./web/ EXPOSE 8080 CMD ["./pasty"] \ No newline at end of file diff --git a/web/assets/css/style.css b/internal/web/frontend/assets/css/style.css similarity index 100% rename from web/assets/css/style.css rename to internal/web/frontend/assets/css/style.css diff --git a/web/assets/css/style.css.map b/internal/web/frontend/assets/css/style.css.map similarity index 100% rename from web/assets/css/style.css.map rename to internal/web/frontend/assets/css/style.css.map diff --git a/web/assets/css/style.scss b/internal/web/frontend/assets/css/style.scss similarity index 100% rename from web/assets/css/style.scss rename to internal/web/frontend/assets/css/style.scss diff --git a/web/assets/js/app.js b/internal/web/frontend/assets/js/app.js similarity index 100% rename from web/assets/js/app.js rename to internal/web/frontend/assets/js/app.js diff --git a/web/assets/js/modules/animation.js b/internal/web/frontend/assets/js/modules/animation.js similarity index 100% rename from web/assets/js/modules/animation.js rename to internal/web/frontend/assets/js/modules/animation.js diff --git a/web/assets/js/modules/api.js b/internal/web/frontend/assets/js/modules/api.js similarity index 100% rename from web/assets/js/modules/api.js rename to internal/web/frontend/assets/js/modules/api.js diff --git a/web/assets/js/modules/duration.js b/internal/web/frontend/assets/js/modules/duration.js similarity index 100% rename from web/assets/js/modules/duration.js rename to internal/web/frontend/assets/js/modules/duration.js diff --git a/web/assets/js/modules/encryption.js b/internal/web/frontend/assets/js/modules/encryption.js similarity index 100% rename from web/assets/js/modules/encryption.js rename to internal/web/frontend/assets/js/modules/encryption.js diff --git a/web/assets/js/modules/notifications.js b/internal/web/frontend/assets/js/modules/notifications.js similarity index 100% rename from web/assets/js/modules/notifications.js rename to internal/web/frontend/assets/js/modules/notifications.js diff --git a/web/assets/js/modules/spinner.js b/internal/web/frontend/assets/js/modules/spinner.js similarity index 100% rename from web/assets/js/modules/spinner.js rename to internal/web/frontend/assets/js/modules/spinner.js diff --git a/web/assets/js/modules/state.js b/internal/web/frontend/assets/js/modules/state.js similarity index 100% rename from web/assets/js/modules/state.js rename to internal/web/frontend/assets/js/modules/state.js diff --git a/web/assets/libs/aesjs/aes.min.js b/internal/web/frontend/assets/libs/aesjs/aes.min.js similarity index 100% rename from web/assets/libs/aesjs/aes.min.js rename to internal/web/frontend/assets/libs/aesjs/aes.min.js diff --git a/web/assets/libs/animatecss/animate.min.css b/internal/web/frontend/assets/libs/animatecss/animate.min.css similarity index 100% rename from web/assets/libs/animatecss/animate.min.css rename to internal/web/frontend/assets/libs/animatecss/animate.min.css diff --git a/web/assets/libs/highlightjs/highlight.min.js b/internal/web/frontend/assets/libs/highlightjs/highlight.min.js similarity index 100% rename from web/assets/libs/highlightjs/highlight.min.js rename to internal/web/frontend/assets/libs/highlightjs/highlight.min.js diff --git a/web/assets/libs/highlightjs/solarized-dark.min.css b/internal/web/frontend/assets/libs/highlightjs/solarized-dark.min.css similarity index 100% rename from web/assets/libs/highlightjs/solarized-dark.min.css rename to internal/web/frontend/assets/libs/highlightjs/solarized-dark.min.css diff --git a/web/index.html b/internal/web/frontend/index.html similarity index 100% rename from web/index.html rename to internal/web/frontend/index.html diff --git a/internal/web/frontend_server.go b/internal/web/frontend_server.go new file mode 100644 index 0000000..d8f9e9d --- /dev/null +++ b/internal/web/frontend_server.go @@ -0,0 +1,77 @@ +package web + +import ( + "embed" + "errors" + "io" + "io/fs" + "mime" + "net/http" + "path/filepath" + "strconv" + "strings" +) + +//go:embed frontend/* +var frontend embed.FS + +func frontendHandler(notFoundHandler http.HandlerFunc) http.HandlerFunc { + return func(writer http.ResponseWriter, request *http.Request) { + path := strings.TrimSpace(strings.TrimSuffix(request.URL.Path, "/")) + + isFirstLevel := strings.Count(path, "/") <= 1 + + file, err := frontend.Open(filepath.Join("frontend", path)) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + if isFirstLevel { + serveIndexFile(writer, request) + } else { + notFoundHandler(writer, request) + } + return + } + writeErr(writer, err) + return + } + defer func() { + _ = file.Close() + }() + + fileInfo, err := file.Stat() + if err != nil { + writeErr(writer, err) + return + } + + if fileInfo.IsDir() { + if isFirstLevel { + serveIndexFile(writer, request) + } else { + notFoundHandler(writer, request) + } + return + } + + content, err := io.ReadAll(file) + if err != nil { + writeErr(writer, err) + return + } + + writer.Header().Set("Content-Type", mime.TypeByExtension(fileInfo.Name())) + writer.Header().Set("Content-Length", strconv.Itoa(len(content))) + _, _ = writer.Write(content) + } +} + +func serveIndexFile(writer http.ResponseWriter, _ *http.Request) { + indexFile, err := frontend.ReadFile("frontend/index.html") + if err != nil { + writeErr(writer, err) + return + } + writer.Header().Set("Content-Type", "text/html") + writer.Header().Set("Content-Length", strconv.Itoa(len(indexFile))) + _, _ = writer.Write(indexFile) +} diff --git a/internal/web/server.go b/internal/web/server.go index 9eb484b..fb64a84 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -39,7 +39,11 @@ type Server struct { func (server *Server) Start() error { router := chi.NewRouter() + // Serve the web frontend + router.Get("/*", frontendHandler(router.NotFoundHandler())) + // Register the paste API endpoints + router.Get("/api/*", router.NotFoundHandler()) router.With(server.v2MiddlewareInjectPaste).Get("/api/v2/pastes/{paste_id}", server.v2EndpointGetPaste) router.Post("/api/v2/pastes", server.v2EndpointCreatePaste) router.With(server.v2MiddlewareInjectPaste, server.v2MiddlewareAuthorize).Patch("/api/v2/pastes/{paste_id}", server.v2EndpointModifyPaste)