ansible: sync portal nginx template with live working config

The pw-portal-tls.conf.j2 template was stale (basic 47-line version) while the
live /etc/nginx/sites-enabled/pw-portal.conf was hand-maintained with branding,
/assets/ and /files/ serving. A future ansible run would have clobbered the
working config. Sync the template to the live config (templatized) and document
why /files/ must be served from /opt/erpnext-assets, not the docker volume.
This commit is contained in:
justin 2026-06-02 22:20:08 -05:00
parent dcea3c29bb
commit 2b13c36c93

View file

@ -17,14 +17,51 @@ server {
ssl_certificate_key /etc/letsencrypt/live/{{ portal_domain }}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Strict-Transport-Security "max-age=31536000" always;
client_max_body_size 50m;
# Inject PW navy branding CSS for the portal navbar
location = /portal-branding.css {
default_type text/css;
return 200 '
.navbar { background-color: #1e3a5f !important; border-bottom: 2px solid #f59e0b !important; }
.navbar .nav-link, .navbar .navbar-brand, .navbar a { color: #ffffff !important; }
.navbar .nav-link:hover { color: #f59e0b !important; }
.navbar .navbar-toggler-icon { filter: invert(1); }
.btn-primary-dark { background-color: #1e3a5f !important; border-color: #1e3a5f !important; }
.btn-primary-dark:hover { background-color: #2d4f7a !important; }
.navbar .nav-link[href*="/order/"] { display: none !important; }
.btn.btn-primary-sm[href*="payment_request"], .btn[onclick*="payment_request"], a[href*="make_payment_request"] { display: none !important; }
.page-header-actions-block .btn-primary-dark { display: none !important; }
';
}
# Site-uploaded public /files/ (portal logo, etc).
# IMPORTANT: served from a stable, www-data-owned host path that is re-synced
# by extract-erpnext-assets.sh. Do NOT point this at /var/lib/docker/volumes/...
# directly: /var/lib/docker is root-only (0700) and docker resets that perm on
# restart, which makes nginx 403 on /files/ and breaks the portal logo.
location /files/ {
alias /opt/erpnext-assets/assets/files/;
expires 7d;
add_header Cache-Control "public";
access_log off;
}
# ERPNext static assets from the extracted host copy.
# Re-extracted by deploy.sh / extract-erpnext-assets.sh after every
# bench migrate/build so the content-hashed bundles never drift.
location /assets/ {
alias /opt/erpnext-assets/assets/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Proxy everything else to ERPNext gunicorn
location / {
proxy_pass http://127.0.0.1:{{ erpnext_port }};
proxy_http_version 1.1;
@ -34,14 +71,16 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host {{ portal_domain }};
# WebSocket support (ERPNext real-time)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 120s;
proxy_buffering off;
}
location /.well-known/acme-challenge/ {
root {{ certbot_webroot }};
# Inject PW branding into HTML responses (logo + copy + branding CSS)
sub_filter 'Login to Frappe' 'Performance West Portal';
sub_filter 'Create a Frappe Account' 'Create an Account';
sub_filter 'Powered by Frappe' 'Performance West';
sub_filter '/assets/erpnext/images/erpnext-logo.svg' '/files/pw-logo.png';
sub_filter '</head>' '<link rel="stylesheet" href="/portal-branding.css"></head>';
sub_filter_once off;
sub_filter_types text/html;
}
}