feat(docker): full self-hosted Compose support
Make the web image drop-in for a Honcho docker-compose stack:
- nginx reverse-proxies /v3 and /health to $HONCHO_UPSTREAM (variable +
Docker resolver so it starts even before the upstream resolves), giving
the SPA a same-origin path to Honcho with no browser CORS.
- Runtime config: an entrypoint writes config.js from
OPENCONCHO_DEFAULT_HONCHO_URL, so one prebuilt image targets any backend
("same-origin" | absolute URL | empty). SPA seeds a first-run default
instance from it (additive; no-op in dev/desktop).
- docker-compose.yml example service + GHCR multi-arch publish workflow on
release.
- nginx.conf -> envsubst template; docs rewritten.
Closes #21. Closes #31.
This commit is contained in:
11
docker/40-openconcho-config.sh
Normal file
11
docker/40-openconcho-config.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Regenerate the SPA's runtime config from the environment at container start.
|
||||
# Lets one prebuilt image target any Honcho backend without a rebuild.
|
||||
# OPENCONCHO_DEFAULT_HONCHO_URL — absolute URL, "same-origin", or empty.
|
||||
# Runs from /docker-entrypoint.d before nginx starts. Requires the html dir to
|
||||
# be writable (default); skip or bind-mount config.js when running --read-only.
|
||||
set -eu
|
||||
|
||||
cat > /usr/share/nginx/html/config.js <<EOF
|
||||
window.__OPENCONCHO_DEFAULT_HONCHO_URL__ = "${OPENCONCHO_DEFAULT_HONCHO_URL:-}";
|
||||
EOF
|
||||
@@ -1,54 +0,0 @@
|
||||
# OpenConcho — nginx site config for the runtime container.
|
||||
# Serves the React SPA from /usr/share/nginx/html with client-side routing.
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Don't leak the nginx version.
|
||||
server_tokens off;
|
||||
|
||||
# Long-cache static assets — Vite hashes filenames so they're safely immutable.
|
||||
location ~* \.(?:js|mjs|css|woff2?|ttf|otf|eot|svg|png|jpg|jpeg|gif|ico|webp|avif|wasm)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Healthcheck (no logging spam).
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "ok\n";
|
||||
}
|
||||
|
||||
# --- Optional: same-origin Honcho reverse proxy (eliminates browser CORS) ---
|
||||
# The SPA's fetches are subject to browser CORS only on the web build (the
|
||||
# desktop app routes through Rust and bypasses CORS). To avoid configuring
|
||||
# CORS on Honcho itself, proxy the API under this origin and point the UI's
|
||||
# base URL at it. See docs/docker.md for the full tradeoff — note the UI
|
||||
# currently requires an absolute base URL, so this block is opt-in.
|
||||
#
|
||||
# location /honcho/ {
|
||||
# proxy_pass http://your-honcho-host:8000/;
|
||||
# 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;
|
||||
# }
|
||||
|
||||
# SPA fallback: any unknown path returns index.html so the router resolves it.
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
# gzip text responses (Vite pre-compresses CSS/JS, but HTML still benefits).
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
}
|
||||
64
docker/nginx.conf.template
Normal file
64
docker/nginx.conf.template
Normal file
@@ -0,0 +1,64 @@
|
||||
# OpenConcho — nginx site config (envsubst template).
|
||||
# The nginx image renders ${HONCHO_UPSTREAM} from the environment at start.
|
||||
# Serves the React SPA and reverse-proxies the Honcho API under the same origin.
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Don't leak the nginx version.
|
||||
server_tokens off;
|
||||
|
||||
# Same-origin reverse proxy to Honcho so the browser never sees a
|
||||
# cross-origin request (no CORS). The variable + Docker resolver let nginx
|
||||
# start even when the upstream isn't resolvable yet, re-resolving per request.
|
||||
resolver 127.0.0.11 ipv6=off valid=10s;
|
||||
set $honcho_upstream "${HONCHO_UPSTREAM}";
|
||||
|
||||
# `^~` so these win over the static-asset regex below.
|
||||
location ^~ /v3/ {
|
||||
proxy_pass $honcho_upstream$request_uri;
|
||||
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;
|
||||
}
|
||||
location = /health {
|
||||
proxy_pass $honcho_upstream/health;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Runtime config — regenerated per container start, must never be cached.
|
||||
location = /config.js {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Long-cache static assets — Vite hashes filenames so they're immutable.
|
||||
location ~* \.(?:js|mjs|css|woff2?|ttf|otf|eot|svg|png|jpg|jpeg|gif|ico|webp|avif|wasm)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Container healthcheck (local; distinct from Honcho's /health).
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "ok\n";
|
||||
}
|
||||
|
||||
# SPA fallback: unknown paths return index.html so the router resolves them.
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
}
|
||||
Reference in New Issue
Block a user