Homelable could only own the root of an origin. Behind an existing proxy at `https://home.example/homelab/` the built `index.html` still asked for `/assets/*`, which fell through to whatever owned the root — and when that answered `text/html` for a `<script>` under `nosniff`, the browser failed the load on an HTTP 200. The only workaround was patching the checkout and rebuilding on every upgrade. One build-time knob, `VITE_BASE_PATH`, becomes Vite's `base`. Vite rewrites the asset URLs it emits; everything the app builds by hand goes through the new `utils/basePath.ts` — the axios instances, the WebSocket URL, the live-view route, the local brand icons, and the OIDC login href. `resolveServerPath` covers what the *backend* hands back, which is always root-absolute because it cannot know where the SPA is mounted: uploaded floor-plan URLs already stored in a canvas are resolved at render time, so plans predating the move keep loading. The OIDC callback used to redirect to `/`, dropping subpath users at the origin root after login; it now reads the prefix back out of `OIDC_REDIRECT_URI`. The default is `/`, and stays a no-op there by construction: every helper returns the string it returned before, the root build output is unchanged and both nginx sites are byte-identical to what shipped — the Docker image copies `docker/nginx.conf` verbatim and the installer keeps its original heredoc. Only a non-default prefix takes the generated config. Those generated configs use `root`, never `alias`, since `alias` plus `try_files` mis-resolves `$uri` — the Docker build lands the bundle in the matching subdirectory, and the installer symlinks it under `/var/www/homelable`. Both accept either reverse-proxy style, prefix forwarded intact or already stripped, with no redirect loop between them, and `absolute_redirect off` stops the no-slash 301 from eating the port. Closes #334. ha-relevant: maybe
87 lines
2.9 KiB
Plaintext
87 lines
2.9 KiB
Plaintext
# Homelable served under a subpath (__BASE__) instead of the root of the origin.
|
|
#
|
|
# Generated at image build time by Dockerfile.frontend when VITE_BASE_PATH is set:
|
|
# __BASE__ is the prefix with its trailing slash, __BASE_NO_SLASH__ without it. A
|
|
# default build keeps docker/nginx.conf and this file is never read.
|
|
#
|
|
# The static build is copied to /usr/share/nginx/html__BASE__, so every block below
|
|
# uses plain `root` semantics — `alias` plus `try_files` mis-resolves $uri.
|
|
#
|
|
# Both reverse-proxy styles work against this config:
|
|
# - prefix forwarded intact (proxy_pass http://homelable;) -> the __BASE__ blocks
|
|
# - prefix stripped (proxy_pass http://homelable/;) -> the trailing blocks
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
root /usr/share/nginx/html;
|
|
index index.html;
|
|
# Relative Location headers — the port and scheme belong to whatever proxy is
|
|
# in front, not to this server block.
|
|
absolute_redirect off;
|
|
|
|
# --- prefix forwarded intact ------------------------------------------------
|
|
location = __BASE_NO_SLASH__ {
|
|
return 301 __BASE__;
|
|
}
|
|
|
|
# WebSocket (must come before the API block to take priority)
|
|
location __BASE__api/v1/status/ws/ {
|
|
proxy_pass http://backend:8000/api/v1/status/ws/;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
location __BASE__api/ {
|
|
proxy_pass http://backend:8000/api/;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Legacy /ws/ path
|
|
location __BASE__ws/ {
|
|
proxy_pass http://backend:8000/ws/;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
}
|
|
|
|
# SPA fallback under the prefix
|
|
location __BASE__ {
|
|
try_files $uri $uri/ __BASE__index.html;
|
|
}
|
|
|
|
# --- prefix already stripped by the front proxy -----------------------------
|
|
# No redirect to __BASE__ here: the front proxy would strip it again and loop.
|
|
location /api/v1/status/ws/ {
|
|
proxy_pass http://backend:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
location /api/ {
|
|
proxy_pass http://backend:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
location /ws/ {
|
|
proxy_pass http://backend:8000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
}
|
|
|
|
location / {
|
|
root /usr/share/nginx/html__BASE_NO_SLASH__;
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
}
|