Three subsystems were using std::fs (blocking) inside async context,
which stalls the tokio runtime thread during I/O:
- DashboardMetrics::save(): now uses tokio::fs::write + rename
- TokenStore::flush_last_used(): now uses tokio::fs for batch updates
- AuditLog::log(): moved file write to spawn_blocking (fire-and-forget)
The background task and shutdown handler now properly .await the
async save/flush methods. AuditLog writer wrapped in Arc for
cross-thread access from spawn_blocking.
Metrics (downloads, uploads, cache hits) were stored in-memory only
and reset to zero on every restart. Now they persist to metrics.json
in the storage directory with:
- Load on startup from {storage_path}/metrics.json
- Background save every 30 seconds
- Final save on graceful shutdown
- Atomic writes (tmp + rename) to prevent corruption
Artifact count on dashboard now shows total tags/versions across
all registries instead of just counting unique repository names.
This matches user expectations when pushing multiple tags to the
same image (e.g. myapp:v1, myapp:v2 now shows 2, not 1).
- Store metadata (.meta.json) alongside manifests with:
- push_timestamp, last_pulled, downloads counter
- size_bytes, os, arch, variant
- layers list with digest and size
- Update metadata on manifest pull (increment downloads, update last_pulled)
- Extract OS/arch from config blob on push
- Extend UI API TagInfo with metadata fields
- Add public_url config option for pull commands
- Add Docker upstream proxy with auth support
- Add raw repository support
- Bump version to 0.2.12
- Add DashboardMetrics for tracking downloads/uploads/cache hits per registry
- Add ActivityLog for recent activity with bounded size (50 entries)
- Instrument Docker, npm, Maven, and Cargo handlers with metrics
- Add /api/ui/dashboard endpoint with global stats and activity
- Implement dark theme dashboard with real-time polling (5s interval)
- Add mount points table showing registry paths and proxy upstreams