Files
ulicraft-server-v1/tooling/render-nginx.sh
T
oierandClaude Opus 5 8507ffd020 feat(files): gate files. browse behind FILES_BROWSE (on|off)
Per-host toggle for the read-only, no-auth browse of the shared upload dir,
so it can stay on for the LAN-party host (oxps) and be off on the
internet-facing one (cochi). Defaults to `on` — existing hosts unchanged.

Neither layer has conditionals, so each gets the value substituted in:
- caddy: docker-compose mounts 62-files.${FILES_BROWSE:-on}.caddy at the
  fixed conf.d target (62-files.caddy -> 62-files.on.caddy, plus a
  comments-only 62-files.off.caddy stub — caddy needs a parseable file
  there). Same trick as FILES_MOUNT_MODE.
- nginx: render-nginx.sh splices nginx/files-browse.server.tmpl into
  ${FILES_BROWSE_SERVER} and adds/omits the name in the port-80 redirect.
  This is the gate that matters; caddy is bound to 127.0.0.1.

Applying a change needs render-nginx.sh --install plus a caddy
--force-recreate (single-file bind mount pins the inode). With `off`,
drop `files` from LE_SUBDOMAINS on that host. upload. is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:08:33 +02:00

87 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# render-nginx.sh — render the nginx vhost template with the .env values.
#
# Substitutes $BASE_DOMAIN, $APP_DIR, $CADDY_HTTP_PORT into
# nginx/ulicraft-caddy.conf.tmpl (the host nginx that terminates TLS in front of
# caddy) and prints the result to stdout, or installs it with --install (writes
# to sites-available, symlinks sites-enabled, tests, reloads nginx).
#
# Usage:
# tooling/render-nginx.sh # render -> stdout
# tooling/render-nginx.sh --install # render + install + reload nginx
#
# .env vars used:
# BASE_DOMAIN required
# CADDY_HTTP_PORT localhost port nginx reverse-proxies to (caddy)
# optional:
# APP_DIR host path to this repo (cert files live under APP_DIR/certs)
# — defaults to the repo root (this checkout)
# SITE_NAME installed filename (default ulicraft.conf)
# FILES_BROWSE on|off (default on) — emit the files.${BASE_DOMAIN}
# read-only browse vhost. `off` omits the server block AND
# the name from the port-80 redirect, so the host serves
# nothing for it (no cert needed either). Same var gates the
# caddy side in docker-compose.yml.
set -euo pipefail
cd "$(dirname "$0")/.." # repo root
log() { printf '\n\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; }
[ -f .env ] || die ".env not found (copy .env.example and fill it)"
# shellcheck disable=SC1091
set -a; . ./.env; set +a
: "${BASE_DOMAIN:?BASE_DOMAIN unset in .env}"
TEMPLATE="${TEMPLATE:-nginx/ulicraft-caddy.conf.tmpl}"
[ -f "$TEMPLATE" ] || die "template not found: $TEMPLATE"
APP_DIR="${APP_DIR:-$PWD}"
SITE_NAME="${SITE_NAME:-ulicraft.conf}"
# CADDY_HTTP_PORT only matters for the caddy-front template; default 80 so the
# static template (which never references it) still renders cleanly.
export BASE_DOMAIN APP_DIR
export CADDY_HTTP_PORT="${CADDY_HTTP_PORT:-80}"
# files.${BASE_DOMAIN} — the no-auth read-only browse. Per-host toggle: on for
# the LAN-party box, off where the share must not be publicly listable. nginx
# has no conditionals, so the vhost is a separate template spliced in here (or
# replaced by a comment). Keep in sync with the caddy mount in docker-compose.yml,
# which reads the same var.
: "${FILES_BROWSE:=on}"
FILES_BROWSE_TEMPLATE="${FILES_BROWSE_TEMPLATE:-nginx/files-browse.server.tmpl}"
case "$FILES_BROWSE" in
on)
[ -f "$FILES_BROWSE_TEMPLATE" ] || die "template not found: $FILES_BROWSE_TEMPLATE"
FILES_BROWSE_NAME=" files.${BASE_DOMAIN}"
FILES_BROWSE_SERVER="$(envsubst '$BASE_DOMAIN $APP_DIR' < "$FILES_BROWSE_TEMPLATE")"
;;
off)
FILES_BROWSE_NAME=""
FILES_BROWSE_SERVER="# files.${BASE_DOMAIN} disabled on this host (FILES_BROWSE=off in .env)."
;;
*) die "FILES_BROWSE must be 'on' or 'off' (got '$FILES_BROWSE')" ;;
esac
export FILES_BROWSE_NAME FILES_BROWSE_SERVER
rendered="$(envsubst '$BASE_DOMAIN $APP_DIR $CADDY_HTTP_PORT $FILES_BROWSE_NAME $FILES_BROWSE_SERVER' < "$TEMPLATE")"
if [ "${1:-}" != "--install" ]; then
printf '%s\n' "$rendered"
warn "dry run — pipe to a file or pass --install to deploy"
exit 0
fi
command -v nginx >/dev/null 2>&1 || die "nginx not found on PATH"
avail="/etc/nginx/sites-available/$SITE_NAME"
enabled="/etc/nginx/sites-enabled/$SITE_NAME"
log "installing $TEMPLATE -> $avail (APP_DIR=$APP_DIR, CADDY_HTTP_PORT=$CADDY_HTTP_PORT, FILES_BROWSE=$FILES_BROWSE)"
printf '%s\n' "$rendered" | sudo tee "$avail" >/dev/null
sudo ln -sf "$avail" "$enabled"
sudo nginx -t
sudo systemctl reload nginx
log "done — nginx reloaded"