Major improvements: multi-stage Docker, security hardening, host metrics, tests

- Multi-stage Docker build: Dockerfile compiles frontend (Node.js) and
  backend (Gradle) inside Docker; no local Java/Node.js required
- Security hardening: replaced full host root mount with specific
  directory mounts (/proc, /sys, /etc, /var, /home, /opt, /tmp, /srv, /usr)
- Host metrics consistency: LinuxMemoryService and CpuDetailsService now
  read from /host/proc instead of container OSHI
- Concurrency: replaced synchronized with AtomicReference in 3 services
- Global error handling: added @RestControllerAdvice for JSON error responses
- Unit tests: added 32 tests for /proc parsers, CPU usage, process uptime
- Docker security: non-root user, read-only FS, no-new-privileges, healthcheck
- Debug logging: added log.debug() in silent catch blocks
- Documentation: updated README, INSTALLATION, DEVELOPMENT, CONFIGURATION,
  ARCHITECTURE, CHANGELOG

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-10-28 23:18:01 +01:00
co-authored by Junie
parent 539175654c
commit 597d8f380a
23 changed files with 1122 additions and 137 deletions
@@ -18,7 +18,7 @@ export class SystemMetricsService {
private readonly refreshInterval = 1000;
readonly metrics$: Observable<SystemMetrics> = timer(0, this.refreshInterval).pipe(
readonly metrics$: Observable<SystemMetrics | null> = timer(0, this.refreshInterval).pipe(
switchMap(() =>
this.http.get<SystemMetrics>('/api/system/metrics').pipe(
catchError((error: unknown) => {
@@ -27,12 +27,7 @@ export class SystemMetricsService {
}),
),
),
// Ignore failed requests without terminating the stream.
map((value) => value),
// Keep the last successful response for subscribers.
shareReplay({ bufferSize: 1, refCount: true }),
// The public type remains SystemMetrics below via the typed getter.
map((value) => value as SystemMetrics),
);
readonly processes$: Observable<SystemProcess[]> = timer(0, this.refreshInterval).pipe(