dullage / flatnotes

A self-hosted, database-less note taking web app that utilises a flat folder of markdown files for storage.
MIT License
1.5k stars 87 forks source link

Support for alternate base url #183

Closed neonspectra closed 5 months ago

neonspectra commented 6 months ago

Overview

Flatnotes expects to live at the root domain of the site at which it is hosted. This works great if you want to host it as a subdomain, but not great if you want to make it a subdirectory of a domain.

IE:

This is a classic problem in web application hosting design, and there's a really good software-agnostic generalised discussion of the nature of the problem here.

Reproducing the Issue

All you need to do is host an instance of Flatnotes with your favourite reverse proxy in front of it. You will see that it works when using a domain with no baseurl redirection, but it breaks if you try to stuff Flatnotes into its own subpath.

This should be demonstrable with any reverse proxy of your choice. For the sake of demonstration, I've made some examples using Caddy for how to do this. I've used syntax that I am actively using for other services which support alternate baseurl configuration, so I'm confident that it should work if Flatnotes supported this in config.

βœ… Working Config

Caddyfile snippet:

example.com {
  reverse_proxy flatnotes:8080
}
Screenshot 2024-04-27 at 18 54 45

🚫 Broken Config

Caddyfile snippet:

example.com {
redir /flatnotes /flatnotes/
handle /flatnotes/* {
  reverse_proxy flatnotes:8080
}
Screenshot 2024-04-27 at 18 53 59

Desired Functionality

Flatnotes should support an environment variables along the lines of FLATNOTES_BASEURL which accepts a path in the form of /subdirectory.

For example, some psudeocode:

Why would you want to do this?

Sometimes you may not have a subdomain available in the DNS zone that you want to host flatnotes in.

For example, if you are using Tailscale Magic DNS with your tailnet name, you only have one subdomain per active node. This means that if you want to have more than one service hosted on the same system, you need to host them as subdirectories rather than putting services on a given root domain.

fawick commented 6 months ago

Having a maintained way to set the base URL to a subfolder would be great, indeed.

First, I tried to come up with a solution by adding the --root_path option to the uvicorn call in entrypoint.sh but that was not enough. The browser tried to load the front-end assets without the subfolder.

Second, I added the base option to vite.config.js. That helped with the JS assets, but all requests to /api/* are still failing (e.g. /api/config or /api/notes/[...]).

Code-Otto commented 6 months ago

Also check out #49 (And PR #135), there's been a prior attempt at this but disregarded because of the added complexity

I'd appreciate the addition of such a feature since this is the last missing straw for proper Flatnotes integration in my home server

fawick commented 6 months ago

Thank you @Code-Otto for making me aware on the prior work.

As it looks, #135 is probably rendered obsolete by the switch from Parcel to Vite (and #135 was rejected anyway).

dullage commented 6 months ago

I'm currently working on a complete rebuild of the web UI in Vue 3 with Vue Router. As part of this work I should be able to allow for a path prefix to be set at build time.

Unfortunately I can't see a clean way to implement a runtime option but hopefully a build option will suffice for those that need it.

kaliatech commented 5 months ago

I was looking to try flatnotes, but I have the same requirement. I want to install it on a subpath, not a subdomain. I have run in to this same issue multiple times while developing SPA type applications myself and also while trying to use other SPA type applications. I believe this is a common oversight of the react and vue communities.

While doing a custom build for each configured needed is a common suggestion, I don't believe it's the best option. It breaks 12-factor principles. Fwiw, two items that have helped me work around this in my own apps successfully:

Vite's base path

Vite supports specifying an empty base path indicating that assets and chunks should be loaded as relative to the HTML base. This functionality has changed over time and is not well documented. It is not always easy to use and I believe has some limitations in more advanced use cases. But, if a relative base works for you then the next step is to understand that all these relative paths are relative to the html base element in your index.html. The vue/react routers can also be configured to function as relative to this base. With all of that in mind, you can configure a subpath by modifying that value in your index.html directly without having to rebuild your SPA. I often use docker startup/init scripts to set the HTML base element value (by rewriting the base attribute inside the index.html) according to environment variables that get applied during docker container startup. In that way, no rebuilds of the SPA are ever needed and the same built SPA package can deployed anywhere.

Dynamic Paths

While base path and relative urls can be used for many things, there is often still a need to form up absolute urls. In all cases these need to be dynamically formed at runtime. I've seen many projects store a configured "base path" on the server (usually from docker env variables), but I think it's just as valid to dynamically determine the "base path" by having the SPA look at the URL it was loaded from as one of the first things it does. i.e. "If I am being loaded at 'https://whatever.com/apath1/flatnotes-test/...', then I can assume my base path is /apath1/flatnotes-testq and I should use that for building up all urls to public assets, redirects, etc."

praisethemoon commented 5 months ago

Hello! Also loving this project, and wanting this feature, keep it up! πŸ™Œ nevertheless ❀️

dullage commented 5 months ago

Thank you very much for taking the time to provide the info here @kaliatech! The base element wasn't something I was aware of but solves a lot of the problems I was having.

I'm pleased to say that I now have a working build that supports a custom path prefix πŸŽ‰. I would appreciate some beta testers if anyone would like to give it a go. You just need to use the docker tag dullage/flatnotes:custom-path-prefix and set the FLATNOTES_PATH_PREFIX environment variable to something like /flatnotes.

⚠️Note: New image attachments will embed a URL that works even if the path prefix is changed. Unfortunately, existing attachment URLs will need to be manually updated (but only for existing users that want to use a path prefix). You'll need to change, for example, /attachments/foo.png to attachments/foo.png.

Duplicate Suggestion: #49 Relevant Commit: a43b9cf Previous PRs: #71 #135

kaliatech commented 5 months ago

Nice! It is working for me locally using a custom path and the dullage/flatnotes:custom-path-prefix image.

Fwiw, I use Caddy to reverse proxy to the docker container with a simple config like this:

    handle /custom-path {
      redir /custom-path /custom-path/
    }

    handle /custom-path* {
      reverse_proxy http://my-flatpath-server:8080
    }   

The only minor issue I noticed is that probably not all favicons are loading as expected. I'm seeing error like this in the dev console:

GET https://www.mydomain.com/custom-path/assets/android-chrome-192x192.png 404 (Not Found)

Not sure if you intend to copy the favicons to the dist /assets folder (allowing users to override them) or had something else in mind. ...if that is your intent, will probably want to eventually have instructions on how to override the root /favicon.ico as well.

Thanks for handling the root URL path issue so quickly!

dullage commented 5 months ago

Thanks for giving it a go @kaliatech and for reporting the issue with the PWA icons. This should be resolved in the latest build of the :custom-path-prefix image. If you get a chance, I'd appreciate it if you could pull the image and confirm you are no longer seeing the 404.

kaliatech commented 5 months ago

Looks good. I pulled latest and all favicons are now resolving without error.

I also noticed the container is reporting unhealthy status. So, I think you might need to apply the FLATNOTES_PATH_PREFIX to healthcheck.

dullage commented 5 months ago

Thanks @kaliatech, another good catch. This has now been sorted, and a new image has been pushed.

_Note: Previously the app would add/remove leading/trailing slashes as necessary, but now the FLATNOTES_PATH_PREFIX value is validated to ensure it starts with but does not end with a slash (e.g. /flatnotes)._

dullage commented 5 months ago

Now available in 5.1.0.