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

Host in a subfolder / different root path #186

Closed
friesenkiwi opened this issue Jan 17, 2021 · 23 comments
Closed

Host in a subfolder / different root path #186

friesenkiwi opened this issue Jan 17, 2021 · 23 comments
Labels
documentation Issues relating to documentation enhancement Feature requests or improvements to existing functionality
Milestone

Comments

@friesenkiwi
Copy link

Hi,
instead of hosting on the root of a domain (like https://example.com/), I wanted to host babybuddy in a different path like https://example.com/examplepath.

I was able to almost fully achieve it by setting

FORCE_SCRIPT_NAME = '/examplepath'
STATIC_URL = 'examplepath/static/'
WHITENOISE_STATIC_PREFIX = 'static/'
LOGIN_REDIRECT_URL = '/examplepath/'
LOGIN_URL = '/examplepath/login/'
LOGOUT_REDIRECT_URL = '/examplepath/login/'

at the end of production.py.

There are only two minor glitches: All functionality and links work nicely, with the exception of the textual "Home" link and the link on the Babybuddy logo, which leads to https://example.com/ instead of https://example.com/examplepath/

What would be the best way to add that last bit of functionality?

I'm unfortunately not very literate in Python, otherwise I would have tried myself. But if you can give me some hints in the right direction, I'd put up a PR for it.

@cdubz
Copy link
Member

cdubz commented Jan 17, 2021

Hmmmm. It definitely seems like this should be easier than all that. I will take a look at this soonish. In the mean time, is a subdomain not an option you could use?

@cdubz cdubz self-assigned this Jan 17, 2021
@cdubz cdubz added documentation Issues relating to documentation enhancement Feature requests or improvements to existing functionality good first issue Good candidates for simple work for first time contributors labels Jan 17, 2021
@cdubz cdubz added this to the v1.5.1 milestone Jan 17, 2021
@friesenkiwi
Copy link
Author

Well, if you want to e.g. host locally on a Raspberry for privacy reasons, you'd have http://raspberrypi.local and it's hard to setup a subdomain. But you might want to host multiple services, so a subfolder seems the natural choice (at least to me…)

@cdubz
Copy link
Member

cdubz commented Jan 17, 2021

True. But in that case you could also just assign any other local domain you want to the Pi (in additional to the default one) — e.g. babybuddy.local. Regardless, I’m not judging your choice here (: Just not sure when I’ll get a chance to test this.

@friesenkiwi
Copy link
Author

friesenkiwi commented Jan 26, 2021

I found some other places in the code where the path is hardcoded:

@cdubz
Copy link
Member

cdubz commented Feb 2, 2021

I finally did a bit of research on this and surprisingly it does not seem to be well supported at all by Django. You seem to be on the right path. With your settings, could you test using {% url "babybuddy:root-router" %} in place of / a template (e.g. at https://github.com/babybuddy/babybuddy/blob/master/babybuddy/templates/babybuddy/base.html#L36) to see if it gives you the right path?

@cdubz cdubz removed their assignment Feb 2, 2021
@friesenkiwi
Copy link
Author

Yes, this works for the templates, thank you very much! But one thing remains, the https://github.com/babybuddy/babybuddy/blob/master/babybuddy/static_src/root/site.webmanifest.json . Since it's static I guess it will be impossible to influence? I'd put up a PR otherwise.

Also it seems I have to correct my earlier statement a bit and one line has to be

STATIC_URL = 'static/'

(not STATIC_URL = 'babybuddy/static/' as written earlier). I seem to have gotten mistaken by some cache. I'll try to figure that out with a fresh install.

@friesenkiwi
Copy link
Author

Or can you give me a good advice, how to flush all caches and generated static files?

@cdubz
Copy link
Member

cdubz commented Feb 2, 2021

Are that and https://github.com/babybuddy/babybuddy/blob/master/babybuddy/static_src/root/browserconfig.xml the only static files that specify the root path? If so your best bet may be to just manually modify them if your deployment method supports that.

The annoying reason that I commit static assets is generating them requires a lot of extra dependencies (node, gulp, node-sass, and on and on and on). If you want to take a stab at doing it that way you’d need to setup a development environment (see https://github.com/babybuddy/babybuddy/blob/master/CONTRIBUTING.md#development) and then you’d have to take a look at the whitenoise documentation to see if it can support your use case — I think it can but I’m not 100% sure (see http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_STATIC_PREFIX).

Neither option is particularly good 😕

@cdubz cdubz modified the milestones: v1.5.1, v1.5.2 Feb 25, 2021
@philip-n
Copy link

philip-n commented Jun 5, 2021

This is rather vaguely related, but I figured I'd leave it here for others with a similar problem

I wanted to host babybuddy as one among many on a small homeserver and stumbled upon similar problems.

What seems to work is:

  • have BabyBuddy listening on some port, let's say 4242
  • use a reverse proxy with a ruleset that strips the subfolder name and adds the port 4242

So the user can initially browse to http://myhost/babybuddy, which is easy to remember, and is then redirected to http://myhost:4242, where BabyBuddy is happy to offer its service 🎉


If somebody wants to do this based on Traefik with BabyBuddy running in a docker container, this should get you started:

  • Add the following labels to the docker-compose.yml file of your BabyBuddy (order of middlewares seems to matter):
    labels:
      # route all requests starting with "/babybuddy" here
      - "traefik.http.routers.babybuddy.rule=PathPrefix(`/babybuddy`)"
      # use middlewares for stripping the "/babybuddy"-path from url and then redirect to correct port
      - "traefik.http.routers.babybuddy.middlewares=strip_babybuddy_prefix@file,redirect_babybuddy_to_port@file"
  • Define the according middlewares in your traefik_dynamic.toml:
[http.middlewares]
  # remove "/babybuddy" from URL before actually routing to babybuddy
  [http.middlewares.strip_babybuddy_prefix.StripPrefix]
    Prefixes = "/babybuddy"

  # remove "/babybuddy" from URL before actually routing to babybuddy
  [http.middlewares.redirect_babybuddy_to_port.redirectScheme]
    scheme = "http"
    port = 4242

Make sure to set the scheme to http or https, depending on your scenario

@MagiX13
Copy link

MagiX13 commented Dec 15, 2021

I also looked a bit into this and have identified three templates that I needed to adjust to work on my side, namely:

The other changes that I did were all in the base settings file (btw: the LSIO docker file does not respect the DJANGO_SETTINGS_MODULE environment variable, it always sets it to the base file), namely changing "/login/" to "login/" and "/static/" to "static/" as well.
I used to have FORCE_SCRIPT_NAME = "/babybuddy/", but this breaks the "classic" access via the port and only gives me access via nginx. In nginx I have added proxy_set_header X-Script-Name /babybuddy/; which should be enough to remove the FORCE_SCRIPT_NAME proportion of things. Seems like this break is needed
Furthermore, I have a second nginx entry for /static/babybuddy/ as I haven't manage to properly set the static files up to respect the script name variable.

I currently don't have a proper testing setup, but if there is the need, I could set it up and create a pull request, or is all the testing done when a PR is created anyways?

@cdubz
Copy link
Member

cdubz commented Dec 15, 2021

@MagiX13 yeah tests will run on PR -- you don't need to worry about that. And if I think any tests need to be added for this feature I can chip in.

@MagiX13
Copy link

MagiX13 commented Dec 17, 2021

I dug a bit deeper into it and might have found a django bug that makes this quite tricky, i.e. nginx passes the script name to django as HTTP_SCRIPT_NAME (see the request.META content) and django expects SCRIPT_NAME (see django itself )instead. If django were to use HTTP_SCRIPT_NAME (in line with the django documentation) instead, adding a SCRIPT_NAME proxy header would be sufficient to work in the direct port as well as the subdirectory, which would make this trivial. Nevermind, turns out one should set SCRIPT_NAME directly instead of Script-Name... Script-Name gets converted to HTTP_SCRIPT_NAME which does not work
I have created a new pull request, mostly to run the tests :)

If you have some pointers on how to avoid another entry for static/babybuddy please let me know. Django static files and me are not on good footing.

@cdubz
Copy link
Member

cdubz commented Dec 18, 2021

@MagiX13 Sorry I’m a little lost here — what’s the current state with your PR? Ideally we should add some documentation for this as well. I’m unclear how to set this up myself for testing.

@MagiX13
Copy link

MagiX13 commented Dec 19, 2021

@cdubz Fully understand that. I did some more testing and it seems to work on my end, but I would be happy to add some documentation, I guess this goes into the Deployment documentation?

I'm using gunicorn and nginx and the relevant nginx lines look like this:

location /babybuddy {
  proxy_pass http://<babybuddyip:port>;
  proxy_set_header Host $host;
  proxy_set_header SCRIPT_NAME /babybuddy;
}
location /static {
  proxy_pass http://<babybuddyip:port>;
}

Thus the babybuddy works via nginx under http://example.com/babybuddy as well as "directly" via http://<babybuddyip:port>. Sadly the /static is still needed, unless one wants to change the STATIC_URL which would break the http://<babybuddyip:port> approach. (if there are multiple projects serving something under /static, two blocks with /static/babybuddy and /static/admin might be needed)

@cdubz
Copy link
Member

cdubz commented Dec 21, 2021

@MagiX13 yeah lets get some documentation on #358 branch. I actually tried to get this working for testing yesterday based on this issue and that PR but couldn't get it working.

Also note there is a /media directory that probably need special treatment like /static, I think? Have you tested uploading a child photo with your subdir setup?

@cdubz
Copy link
Member

cdubz commented Feb 27, 2022

We have full support for subdirectory hosting now, at least for the example uWSGI + NGINX deployment method. See https://docs.baby-buddy.net/setup/subdirectory/ for details! Happy to add other information for different deployment configurations if people have use cases to share.

@cdubz cdubz removed the good first issue Good candidates for simple work for first time contributors label Feb 27, 2022
@cdubz cdubz added this to the v1.10.2 milestone Feb 27, 2022
@maxdd
Copy link

maxdd commented Mar 14, 2024

Hello, i'm using nginx proxy manager docker to host all my home sites behind an https nginx proxy
I have linuxserver.io docker of baby buddy on port 8082 which i can access locally but which i would like to serve on https://mydomain/bb/ as well

What i'm failing to understand is whether the docker-compose.yml file would need the SUBPATH=/bb/ or not (it doesn't seems so) and whether

location /bb/ {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $remote_addr;
    proxy_set_header X-Real-IP		$remote_addr;
    proxy_pass       http://192.168.1.52:8082;

is sufficient or i'm missing something.

If i do what i wrote above i get

image

Some content seems to load fine though but it feels like babybuddy docker is still looking for the root path

@cdubz
Copy link
Member

cdubz commented Mar 20, 2024

@maxdd I'm not sure what the best approach is for that situation. LSIO has a couple of support options you may want to try checking out: https://www.linuxserver.io/support

@maxdd
Copy link

maxdd commented Mar 20, 2024

Are you suggesting that this is docker related and that adding SUBPATH + a proxy pass on a frontend nginx (not the one used by babybuddy) is sufficient?

@MrApplejuice
Copy link
Contributor

MrApplejuice commented Mar 20, 2024

Hey, just randomly saw this in my feed. I had some issues with hosting babybuddy on a subfolder as well but managed since a year or so to have it work reliably. However, I am using apache, but I think the config is easy to translate to nginx!

Apache configuration for hosting on https://redacted-my-server.com/babybuddy/

        Alias "/babybuddy/babybuddy/media/" "/var/local/babybuddy/media/"
        Alias "/babybuddy/media/" "/var/local/babybuddy/media/"
        <Directory "/var/local/babybuddy/media/">
                Require all granted
        </Directory>

        <LocationMatch "/babybuddy">
                RequestHeader set X_FORWARDED_HOST redacted-my-server.com
                RequestHeader set X-Forwarded-Proto https
                ProxyPass http://127.0.0.1:18000
                ProxyPassReverse "https://redacted-my-server.com/babybuddy/"
        </LocationMatch>

Short explanation: Alias makes apache serve the media-files from the given /var/local/babybuddy folder. Note the silly(!) /babybuddy/babybuddy/media/ - I do not quite remember what exactly it was needed for, but some paths were doubled up when hosting babybuddy on a sub-folder.

Babybuddy server (through docker-compose)

Here The contents of my docker-compose file. Most important aspect here are the environment variables.

version: "2.1"
services:
  babybuddy-custom:
    image: babybuddy:local_build
    container_name: babybuddy-custom
    environment:
      - TZ=Europe/Amsterdam
      - SUB_PATH=/babybuddy
      - DEBUG=False
      - USE_X_FORWARDED_HOST=True
      - FORCE_SCRIPT_NAME=/babybuddy
      - SECURE_PROXY_SSL_HEADER=True
    volumes:
      - /var/local/babybuddy:/config
    ports:
      - 18000:8000
    restart: unless-stopped

I hope this helps someone who has issues.

@maxdd
Copy link

maxdd commented Mar 20, 2024

The issue is that I use nginx as a reverse proxy on a different docker so it doesn't have knowledge of /var/local/babybuddy/media/
Is it still applicable?

Your docker image seems like a local build, are

USE_X_FORWARDED_HOST=True 
FORCE_SCRIPT_NAME=/babybuddy

present in the standard linuxserver version?

@MrApplejuice
Copy link
Contributor

Good point, I forgot about that. Yes, I had to modify my docker-build a bit. My main-addition is configuring a new default django-settings file:

$ cat ./root/app/babybuddy/babybuddy/settings/docker.py

from .base import *

import os

FORCE_SCRIPT_NAME = os.environ.get("FORCE_SCRIPT_NAME", None)
if not FORCE_SCRIPT_NAME:
    del FORCE_SCRIPT_NAME

if os.environ.get("USE_X_FORWARDED_HOST", "False").lower().strip() == "true":
    USE_X_FORWARDED_HOST = True

So you did sniff this out correctly. I branched off at tag v1.10.2-ls37, which is quite old my now. I cannot tell if some of these changes have found their way to the official repo!

@MrApplejuice
Copy link
Contributor

The issue is that I use nginx as a reverse proxy on a different docker so it doesn't have knowledge of /var/local/babybuddy/media/
Is it still applicable?

This is still important if you want to set debug=False (which generally is advised for production environments). When not in debug-mode, django will not serve static files or media files. The media-files live on the "outside" of docker in my case (the config-directory) so I need to serve them by some other means.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Issues relating to documentation enhancement Feature requests or improvements to existing functionality
Projects
None yet
Development

No branches or pull requests

6 participants