The README only covered the first install, so the admin page update
stalled at a step that wasn't written down: nginx serves its own copy
of public/ in /var/www, and without copying it a new page quietly falls
back to index.html.
"Updating the server" covers: backing up the database, pulling and
listing what changed, Python packages, copying the web files,
restarting, health checks, which deploy/ templates map to which
installed files, and redeploying collectors when they change. A
rollback section restores the database before starting the older API
(which refuses a newer database), using sqlite3 .restore rather than
cp because of the WAL file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Admins (Microsoft 365 sign-in names in BACKUP_DASH_ADMINS) get an
Admin link to /admin.html, where they can create, list and revoke the
API keys collectors use, instead of running manage.py on the server.
Backend
- /api/v1/admin/keys (list, create, revoke), admins only (403
otherwise); the user comes from the headers nginx sets after sign-in
- Changes need an X-Requested-By header, which another site's page
can't add, so a signed-in admin's browser can't be used to forge them
- A new key is returned once; only its hash is stored. Names are unique
for good, so revoked keys' history stays unambiguous
- Keys record who created/revoked them and the company they last
reported for (schema v2; databases migrate automatically on start)
- /api/v1/me reports is_admin; manage.py shares the key code and
records itself as creator/revoker
Frontend
- admin.html / admin.js: name validation shown after interaction
(:user-invalid, aria-invalid kept in sync), the one-time key with a
copy button (falls back to selecting it if clipboard access is
refused), a key table flagging keys quiet for over 24 hours, and
revoking confirmed in a <dialog>; results announced in a status region
- Shared el() and signed-in helpers moved to dom.js / signed-in.js
- Mock mode (?mock=1) includes sample keys
Tested: 15 new backend tests (access, forged requests, names,
duplicates, revoking, last-used company, v1 -> v2 upgrade); in the
browser, every admin flow in mock mode and at phone width; end to end
through the nginx config with the real API (a key created on the API
works for ingest; revoking it makes ingest 401; non-admins see an
explanation and no Admin link).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
84 tests in collectors/tests, run with Invoke-CollectorTests.ps1 (~15 s,
PowerShell 7.2+ and Pester 5.5+):
- Module.Tests: the shared module (property lookup, time format,
config/lookback, sent-run state and pruning, dry runs, exit codes,
401 detection)
- Veeam/Mars/Wsb/Vb365.Tests: each collector run in its own process
against its mock and a stand-in ingest API, asserting the behaviours
confirmed on real servers: VBR 13 live schedule, copy jobs, MARS UTC
times and ErrorCode 0, WSB culture-formatted times and nested VMs,
VB365 organization mapping, log prefixes and retries, state and
resending, rejected keys
- Installers.Tests: config, files and scheduled task from each
installer, with stand-ins for icacls / Task Scheduler / the key prompt
Collectors run under the test runner's PowerShell, or another shell via
-CollectorShell (e.g. powershell.exe, to test under Windows PowerShell
5.1 like the MARS, WSB and VB365 servers). The mocks take their "now"
from MOCK_NOW so expectations can't drift with the clock; the WSB mock
gains -RunningJobFinished for the running-to-finished case.
Each of 12 past bugs, put back one at a time, fails the test for its
behaviour (and the runner exits 1).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
nginx now checks every dashboard and API request with oauth2-proxy
(auth_request), which signs users in with Microsoft Entra ID. Who may
sign in is controlled in Entra: a single-tenant app registration with
"Assignment required", so only assigned users/groups get through.
- deploy/oauth2-proxy.cfg (entra-id provider, auth-only mode, secure
cookie, trusted proxy = localhost) and deploy/oauth2-proxy.service
- nginx: pages redirect to Microsoft sign-in and back to the same URL;
API calls get a plain 401 (the page already asks to reload);
/api/v1/ingest stays key-only for collectors; /oauth2/auth is
internal; the signed-in user is passed to the API in headers nginx
sets (so browsers can't spoof them); dashboard files are served with
Cache-Control: no-cache so updates show up at once
- GET /api/v1/me; the top bar shows the signed-in user and a Sign out
that also signs out of Microsoft (otherwise SSO signs straight back in)
- README: Entra and server setup steps
Tested locally with oauth2-proxy 7.15.5 and nginx 1.31 running the
deploy configs (TLS, self-signed): redirects to the tenant's sign-in
with the return URL kept, 401s, ingest bypass, sign-out allow-list,
cookie flags, security headers. The signed-in path was tested with a
stand-in for oauth2-proxy's auth answer; a real Entra sign-in wasn't.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Veeam, MARS, WSB and VB365 collectors each carried identical copies
of logging, ISO time conversion, property lookup, posting to the API,
config loading, TLS setup, the sent-runs state file and the 401 check,
and their installers repeated the same key prompt, locked-down config,
file copy and scheduled-task registration. These now live in
collectors/common/BackupDashboard.Collector.psm1:
- Initialize-Collector / Test-RunSent / Send-JobReport /
Test-AuthRejected / Complete-Collector for collector runs
- Write-Log, Get-ErrorText, ConvertTo-IsoTime, Get-FirstValue
- Install-CollectorTask for the shared half of each installer
Collectors find the module next to themselves (installed) or in
..\common (repo); installers copy it into Program Files beside the
collector. The diagnostic scripts stay standalone single files.
No behavior change: a harness ran each collector against its mock
(12 scenarios: dry run, two real runs against a stub API, state file,
logs, exit codes, rejected key, en-GB, backfill) before and after, and
all 84 outputs are identical. Installers, run with stand-ins for the
Windows-only commands, write identical configs and tasks; the only
differences are the module being copied and the summary line naming
the shell's path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PowerShell script for the VB365 server, run hourly as a SYSTEM scheduled
task (Windows PowerShell 5.1 by default, -PwshPath for PowerShell 7).
One dashboard job per VB365 backup job and backup copy job.
- Sessions from Get-VBOJobSession (and Get-VBOCopyJobSession where
available) within the lookback window; status from session Status,
message from the session log's warning/error lines, size from
Statistics.TransferredData ("1.5 GB" -> bytes)
- One VB365 server can protect several Microsoft 365 organizations, so
the dashboard company is chosen per job: OrganizationTenants mapping,
else Tenant, else the organization's name
- Next run from the job's NextRun; empty for disabled jobs
- Get-Vb365CollectorDiagnostics.ps1 dumps jobs and sessions, showing
each DateTime's Kind (local vs UTC); installer adapted from MARS
Not yet run against a real server.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PowerShell script for servers using Windows Server Backup, run hourly as
a SYSTEM scheduled task under Windows PowerShell 5.1. Each server is one
dashboard job.
- Backup jobs from Get-WBJob (in progress and -Previous) within the
lookback window; restores are ignored. WSB jobs have no ID, so runs
are keyed on start time, which also merges a Running report with the
finished one
- Error HRESULT -> failed; detailed error or a failed item -> warning;
messages include the error description, codes as 0x8XXXXXXX, failed
items and the failure log path
- Next run from Get-WBSummary's NextBackupTime
- Get-WsbCollectorDiagnostics.ps1 dumps the cmdlets' real objects and
recent Microsoft-Windows-Backup events (to check whether job times
are local or UTC); installer adapted from the MARS one
Not yet run against a real server.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PowerShell script for servers running the MARS agent, run as a SYSTEM
scheduled task (hourly) under Windows PowerShell 5.1, which the
agent's MSOnlineBackup module targets. Each server is one dashboard job.
- Backup jobs from Get-OBJob (in progress and -Previous) within the
lookback window; restores are ignored. Completed with failed bytes
or an error reports as warning
- Next run computed from the backup and System State policy schedules;
left empty for every-N-weeks schedules to avoid false Overdue alarms
- Property reads tolerate unknown names, since the agent's object
model isn't documented; Get-MarsCollectorDiagnostics.ps1 dumps the
real structure for checking
- Install-MarsCollector.ps1 mirrors the Veeam installer; tests/MockMars.ps1
provides stand-in cmdlets
Not yet run against a real MARS agent.
Co-Authored-By: Claude Opus 5.5 <[email protected]>