Add a Display section to the settings modal with a timezone dropdown.
Selection is persisted to localStorage and applied to all timestamps via
fmtDate (date-only) and fmtDateFull (date + time + TZ abbreviation, e.g.
"Mar 28, 2026, 2:48 PM EDT"). Changing the timezone live-re-renders the
current page. Defaults to UTC.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an instance_history table that records every field change:
- createInstance logs a 'created' event
- updateInstance diffs old vs new and logs one row per changed field
(name, state, stack, vmid, tailscale_ip, all service flags)
- History is stored under the new vmid when vmid changes
New endpoint: GET /api/instances/:vmid/history
The 'timestamps' section on the detail page is replaced with a
grid timeline showing timestamp | field | old → new for each event.
State changes are colour-coded (deployed=green, testing=amber,
degraded=red). Boolean service flags display as on/off.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a gear button to the nav that opens a settings modal with:
- Export: GET /api/export returns all instances as a JSON backup file
with a Content-Disposition attachment header
- Import: POST /api/import validates and bulk-replaces all instances;
client uses FileReader to POST the parsed JSON, with a confirm dialog
before destructive replace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three root causes addressed:
1. Added <base href="/"> to index.html so all relative asset paths
(css/app.css, js/*.js) resolve from the root regardless of the
current SPA route. Without this, /instance/117 requested
/instance/css/app.css, which hit the SPA fallback and returned
HTML; helmet's nosniff then refused it as a stylesheet.
2. Removed upgrade-insecure-requests from the CSP (useDefaults: false).
This directive told browsers to upgrade HTTP→HTTPS for every asset
request, breaking all resource loading on HTTP-only deployments.
3. Changed script-src-attr from 'none' to 'unsafe-inline' to allow
the inline onclick handlers used throughout the UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dockerfile — creates a non-root app user and runs the process under it
server/routes.js — tailscale_ip validated against IPv4 regex (empty string still allowed)
index.html — sql.js CDN script tag already removed earlier in this session