logpaste icon indicating copy to clipboard operation
logpaste copied to clipboard

prefix path support

Open Arniiiii opened this issue 11 months ago • 6 comments

idea: hostname/prefix/ instead of hostname

patch on release 0.3.1: (Click to expand)
diff --git a/README.md b/README.md
index 371725a..eb76992 100644
--- a/README.md
+++ b/README.md
@@ -92,14 +92,15 @@ docker run \
 
 ### Command-line flags
 
-| Flag              | Meaning                                            | Default Value                                          |
-| ----------------- | -------------------------------------------------- | ------------------------------------------------------ |
-| `-title`          | Title to display on homepage                       | `"LogPaste"`                                           |
-| `-subtitle`       | Subtitle to display on homepage                    | `"A minimalist, open-source debug log upload service"` |
-| `-footer`         | Footer to display on homepage (may include HTML)   |                                                        |
-| `-showdocs`       | Whether to display usage documentation on homepage | `true`                                                 |
-| `-perminutelimit` | Number of pastes to allow per IP per minute        | `0` (no limit)                                         |
-| `-maxsize`        | Max file size users can upload                     | `2` (2 MiB)                                            |
+| Flag              | Meaning                                             | Default Value                                          |
+| ----------------- | --------------------------------------------------- | ------------------------------------------------------ |
+| `-title`          | Title to display on homepage                        | `"LogPaste"`                                           |
+| `-subtitle`       | Subtitle to display on homepage                     | `"A minimalist, open-source debug log upload service"` |
+| `-footer`         | Footer to display on homepage (may include HTML)    |                                                        |
+| `-showdocs`       | Whether to display usage documentation on homepage  | `true`                                                 |
+| `-perminutelimit` | Number of pastes to allow per IP per minute         | `0` (no limit)                                         |
+| `-maxsize`        | Max file size users can upload                      | `2` (2 MiB)                                            |
+| `-prefix`         | Set prefix path for homepage(i.e. `ex.com/prefix/`) | `` (empty string)                                      |
 
 ### Docker environment variables
 
@@ -113,6 +114,7 @@ You can adjust behavior of the Docker container by passing these parameters with
 | `LITESTREAM_REGION`            | AWS region where your S3 bucket is located                                                        |
 | `LITESTREAM_ACCESS_KEY_ID`     | AWS access key ID for an IAM role with access to the bucket where you want to replicate data.     |
 | `LITESTREAM_SECRET_ACCESS_KEY` | AWS secret access key for an IAM role with access to the bucket where you want to replicate data. |
+| `LP_PREFIX`                    | Set prefix path for main functionality (i.e. via curl you'll get `https://ex.com/prefix/id`       |
 
 ### Docker build args
 
diff --git a/cmd/logpaste/main.go b/cmd/logpaste/main.go
index 241fe7e..6720cfb 100644
--- a/cmd/logpaste/main.go
+++ b/cmd/logpaste/main.go
@@ -22,6 +22,7 @@ func main() {
 	footer := flag.String("footer", "", "custom page footer (can contain HTML)")
 	showDocs := flag.Bool("showdocs",
 		true, "whether to display usage information on homepage")
+	prefix := flag.String("prefix", "", "prefix in pathes to css and js files in static's main index.html")
 	perMinuteLimit := flag.Int("perminutelimit",
 		0, "number of pastes to allow per IP per minute (set to 0 to disable rate limiting)")
 	maxPasteMiB := flag.Int64("maxsize", 2, "max file size as MiB")
@@ -36,6 +37,7 @@ func main() {
 		Subtitle:   *subtitle,
 		FooterHTML: *footer,
 		ShowDocs:   *showDocs,
+		Prefix:     *prefix,
 	}, *perMinuteLimit, maxCharLimit).Router())
 	if os.Getenv("LP_BEHIND_PROXY") != "" {
 		h = gorilla.ProxyIPHeadersHandler(h)
diff --git a/handlers/paste.go b/handlers/paste.go
index acfafd3..05a821b 100644
--- a/handlers/paste.go
+++ b/handlers/paste.go
@@ -201,5 +201,5 @@ func baseURLFromRequest(r *http.Request) string {
 	} else {
 		scheme = "http"
 	}
-	return fmt.Sprintf("%s://%s", scheme, r.Host)
+	return fmt.Sprintf("%s://%s%s", scheme, r.Host, os.Getenv("LP_PREFIX"))
 }
diff --git a/handlers/server.go b/handlers/server.go
index 28e1eb0..0c3525d 100644
--- a/handlers/server.go
+++ b/handlers/server.go
@@ -29,6 +29,7 @@ type SiteProperties struct {
 	Subtitle   string
 	FooterHTML string
 	ShowDocs   bool
+	Prefix string
 }
 
 type defaultServer struct {
diff --git a/static/js/app.js b/static/js/app.js
index e06ec2e..4f3c3f0 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -3,7 +3,8 @@
 // Make ESLint happy.
 /* global Prism, logpaste */
 
-const baseUrl = document.location.origin;
+const baseUrl = document.location.href;
+const currentUrl = document.location.pathname;
 
 const curlCmd = document.getElementById("curl-cmd");
 if (curlCmd) {
@@ -34,7 +35,7 @@ if (jsExample) {
 <script>
 const text = "some text I want to upload";
 
-logpaste.uploadText(text).then((id) => {
+logpaste.uploadText(text,${baseUrl}).then((id) => {
   console.log(\`uploaded to ${baseUrl}/\${id}\`);
 });
 </script>
@@ -57,7 +58,7 @@ function displayResult(resultId) {
   resultDiv.appendChild(header);
 
   const anchor = document.createElement("a");
-  anchor.href = `/${resultId}`;
+  anchor.href = `${currentUrl}${resultId}`;
   anchor.innerText = resultUrl;
   resultDiv.appendChild(anchor);
 
@@ -87,7 +88,7 @@ function displayError(error) {
 document.getElementById("upload").addEventListener("click", () => {
   const textToUpload = document.getElementById("upload-textarea").value;
   logpaste
-    .uploadText(textToUpload)
+    .uploadText(textToUpload,baseUrl)
     .then((id) => {
       displayResult(id);
     })
diff --git a/static/js/logpaste.js b/static/js/logpaste.js
index 1402e40..057280d 100644
--- a/static/js/logpaste.js
+++ b/static/js/logpaste.js
@@ -2,7 +2,7 @@
 
 (function () {
   function uploadText(text, baseUrl = "") {
-    return fetch(baseUrl + "/", {
+    return fetch(baseUrl, {
       method: "PUT",
       body: text,
     })
diff --git a/views/index.html b/views/index.html
index 80402e9..7fd5518 100644
--- a/views/index.html
+++ b/views/index.html
@@ -4,7 +4,7 @@
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>{{ .Title }}</title>
-    <link rel="stylesheet" type="text/css" href="/css/style.css" />
+    <link rel="stylesheet" type="text/css" href="{{ .Prefix }}/css/style.css" />
   </head>
   <body>
     <div class="container">
@@ -71,9 +71,9 @@
     </div>
 
     {{ if .ShowDocs }}
-      <script src="/third-party/prism/prism.js" data-manual></script>
+    <script src="{{ .Prefix }}/third-party/prism/prism.js" data-manual></script>
     {{ end }}
-    <script src="/js/logpaste.js"></script>
-    <script src="/js/app.js"></script>
+    <script src="{{ .Prefix }}/js/logpaste.js"></script>
+    <script src="{{ .Prefix }}/js/app.js"></script>
   </body>
 </html>

I can confirm it works when behind nginx (reverse-proxy) at a path like /paste/. I cannot confirm if not specifying -prefix=/paste and LP_PREFIX="/paste" will work.

Here's nginx config snippet (some info scraped, here example when ssl certificates are generated with acme.sh , filepathes may be not the same ):

/etc/nginx/nginx.conf :

    server {
        listen 80; 
        listen [::]:80; 
        listen 443 ssl; 
        listen [::]:443 ssl; 
        server_name ...;

        ssl_certificate ...;
        ssl_certificate_key ...;
        
        include global/acme.conf;

        location / {
            root  /usr/share/nginx/html;
            index  index.html index.htm robots.txt;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
       

         location /paste/ {
            rewrite ^ $request_uri;
            rewrite ^/paste(/.*) $1 break;
            return 400;
            proxy_pass http://127.0.0.1:3001$uri;

            proxy_set_header Connection $http_connection;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            client_max_body_size 1024M;
        }

}

/etc/nginx/global/acme.conf:

location /.well-known/acme-challenge/ {
    alias /var/www/acme/.well-known/acme-challenge/;
}

Arniiiii avatar May 28 '25 20:05 Arniiiii

idea: hostname/prefix/ instead of hostname patch on release 0.3.1: (Click to expand)

I can confirm it works when behind nginx (reverse-proxy) at a path like /paste/. I cannot confirm if not specifying -prefix=/paste and LP_PREFIX="/paste" will work.

Sorry, I'm not sure what you're asking.

Can you clarify what problem this solves?

mtlynch avatar May 28 '25 20:05 mtlynch

idea: hostname/prefix/ instead of hostname patch on release 0.3.1: (Click to expand) I can confirm it works when behind nginx (reverse-proxy) at a path like /paste/. I cannot confirm if not specifying -prefix=/paste and LP_PREFIX="/paste" will work.

Sorry, I'm not sure what you're asking.

Can you clarify what problem this solves?

Assume you have nginx as a reverse proxy. It forwards everything it got at /prefix/ to a logpaste instance as if it was / .

Without this patch:

  1. Main index.html ( from outside it's http(s)://hostname/paste/index.html ) doesn't work correctly: cannot load js and css files
  2. In js files it cannot send correctly to itself at http(s)://hostname/prefix
  3. If try any way with curl at http(s)://hostname/prefix/ you'll get link but http(s)://hostname/id while it should be http(s)://hostname/prefix/id

Also the patch adds relevant docs to README.md

Possibly, this patch should be tested when there's no prefix. I couldn't test it, but I guess it should work, but still.

Arniiiii avatar May 28 '25 20:05 Arniiiii

also, from looking once more at this: https://gtleophijtebuiotmpvoifgkl.space/paste/ I found one more place to fix:

diff --git a/static/js/app.js b/static/js/app.js
index e06ec2e..4a4917a 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -30,7 +30,7 @@ const jsExample = document.getElementById("js-example");
 if (jsExample) {
   jsExample.innerHTML = Prism.highlight(
     `
-<script src="${baseUrl}/js/logpaste.js"></script>
+<script src="${baseUrl}js/logpaste.js"></script>
 <script>
 const text = "some text I want to upload";
 

Arniiiii avatar May 28 '25 20:05 Arniiiii

I appreciate you wanting to help out, but it seems like you're sort of optimizing for your time rather than mine as the maintainer.

In general, if you have a bug report or suggestion, I recommend:

  1. Creating a descriptive title that describes the issue. In this case, I'd recommend something like, "Feature request: Support user-defined route prefixes." The title "prefix" is not descriptive and feels like not a thoughtful contribution.
  2. In the issue body, describe the problem you're trying to solve and (optionally) what you recommend to solve it
    • Wait to implement a patch/PR until you have buy-in from the maintainer unless it's a trivial fix or you don't mind wasting your own time

In this case, I'm still not understanding what problem you're trying to solve.

Without this patch:

Main index.html ( from outside it's http(s)://hostname/paste/index.html ) doesn't work correctly: cannot load js and css files

I'm confused by this. You're saying that the end-user accesses logpaste from a URL like this:

  • http://example.com/paste/

But there's a proxy in front of the app that routes the request to logpaste?

So, why isn't the proxy stripping the /paste prefix so that HTTP /paste GET is stripped to HTTP / GET by the time it reaches logpaste?

Is there something unique about logpaste where it needs to do something special to accommodate this scenario? Or are you saying that every web app in existence should support custom route prefixes so that this proxy prefix scenario works?

mtlynch avatar May 29 '25 00:05 mtlynch

In this case, I'm still not understanding what problem you're trying to solve.

Without this patch: Main index.html ( from outside it's http(s)://hostname/paste/index.html ) doesn't work correctly: cannot load js and css files

I'm confused by this. You're saying that the end-user accesses logpaste from a URL like this:

* `http://example.com/paste/`

But there's a proxy in front of the app that routes the request to logpaste?

So, why isn't the proxy stripping the /paste prefix so that HTTP /paste GET is stripped to HTTP / GET by the time it reaches logpaste?

It does. It strips to / so that /paste/ is / from logpaste's point of view.

But then:

  1. At homepage's javascript files the scripts are trying to connect to logpaste / as if it is https://example.com/ (thinking that baseUrl is hostname, while it should be whole href of web site) while there's nothing at https://example.com/ . In the scenario it should talk to https://example.com/paste/ , so whole href.
  2. If try echo "some text I want to upload" | curl -F '_=<-' https://example.com/paste/ it will return https://example.com/id when the correct path is https://example.com/paste/id

Is there something unique about logpaste where it needs to do something special to accommodate this scenario? Or are you saying that every web app in existence should support custom route prefixes so that this proxy prefix scenario works?

Logpaste, as many web services, think that hostname is base URL. While it's true in a lot of cases, it's not true for the case.

I know that github's CI system has the problem too.

Forgejo explicitly asks for base URL in config and you can specify there URL with path prefix like https://example.com/code/.

These 2 examples are just from my mind and there can be many other examples where it fails.

Arniiiii avatar May 29 '25 12:05 Arniiiii

Thanks for the explanation.

I think the better solution is to allow a BASE_URL environment variable rather than a prefix flag.

mtlynch avatar Jun 22 '25 20:06 mtlynch