feat: Enhanced Prometheus metrics and health checks (#212)#269
feat: Enhanced Prometheus metrics and health checks (#212)#269rubenvdlinde wants to merge 14 commits intodevelopmentfrom
Conversation
…into feature/212/prometheus-metrics
Quality Report — ConductionNL/procest @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-21 04:43 UTC
Download the full PDF report from the workflow artifacts.
- HealthController: add class-level @SPEC tag (ADR-003) - MetricsController: add class-level @SPEC tags; add @SPEC to index(); fix @param-before-@spec ordering in getCached() docblock (phpcs ERROR) - HealthControllerTest: named parameters for createMock/assert calls (ADR-015), fix blank-line spacing - MetricsControllerTest: named parameters for createMock/assert calls (ADR-015), fix blank-line spacing Co-fixed-by: Juan Claude van Damme <hydra-reviewer@conduction.nl>
| * | ||
| * @psalm-suppress UnusedClass | ||
| */ | ||
| class HealthController extends Controller |
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-2 | ||
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-3 | ||
| * | ||
| * @psalm-suppress UnusedClass |
There was a problem hiding this comment.
[fixed: added class-level @SPEC tags + index() @SPEC + moved @SPEC after @param in getCached()] Rule: ADR-003 — class docblock missing @SPEC (SpecTagSniff WARNING); index() missing @SPEC (SpecTagSniff WARNING); getCached() had @SPEC before @param violating PEAR.Commenting.FunctionComment order (phpcs ERROR).
Quality Report — ConductionNL/procest @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-21 07:40 UTC
Download the full PDF report from the workflow artifacts.
| $this->db = $this->createMock(IDBConnection::class); | ||
| $this->appManager = $this->createMock(IAppManager::class); | ||
| $this->logger = $this->createMock(LoggerInterface::class); | ||
| $this->request = $this->createMock(originalClassName: IRequest::class); |
There was a problem hiding this comment.
[fixed: converted 22 positional-arg calls to named parameters + fixed 6 blank-line spacing issues] Rule: ADR-015 — all calls to internal code must use named parameters (NamedParametersSniff ERROR). Blank-line rule: Squiz.WhiteSpace.FunctionSpacing. Both sets of fixes applied by phpcbf (blank lines) and manual edit (named params).
| $this->db = $this->createMock(IDBConnection::class); | ||
| $this->appManager = $this->createMock(IAppManager::class); | ||
| $this->logger = $this->createMock(LoggerInterface::class); | ||
| $this->request = $this->createMock(originalClassName: IRequest::class); |
There was a problem hiding this comment.
[fixed: converted 22 positional-arg calls to named parameters + fixed 7 blank-line spacing issues] Rule: ADR-015 — all calls to internal code must use named parameters (NamedParametersSniff ERROR). Blank-line rule: Squiz.WhiteSpace.FunctionSpacing. Both sets of fixes applied by phpcbf (blank lines) and manual edit (named params).
Code Review — Juan Claude van DammeResult: PASS (6 fix sets applied, 0 unfixed, 0 blocking) Checks run
Fixes applied
Prior security findings (Clyde's domain — not touched)SEC-01/SEC-02: See inline comments for per-finding detail. |
| * | ||
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-4 | ||
| * | ||
| * @NoCSRFRequired |
There was a problem hiding this comment.
[unfixed: no container write access to root-owned repo files] Rule: OWASP A01:2021 — Missing @publicpage annotation on HealthController::index(). Without @publicpage, Nextcloud's auth middleware intercepts all unauthenticated requests (container health probes) with a 302 redirect to the login page, making the endpoint non-functional. Fix: add * @PublicPage on a line above * @NoCSRFRequired in the index() docblock. This is a mechanical 1-line addition — apply in next cycle.
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-2 | ||
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-3 | ||
| * | ||
| * @NoCSRFRequired |
There was a problem hiding this comment.
[unfixed: no container write access to root-owned repo files] Rule: OWASP A01:2021 — Missing @publicpage annotation on MetricsController::index(). Without @publicpage, Nextcloud's auth middleware blocks all unauthenticated Prometheus scrape requests with a 302 redirect. The /api/metrics endpoint is non-functional for external scrapers. Fix: add * @PublicPage above * @NoCSRFRequired in the index() docblock. Mechanical 1-line addition — apply in next cycle.
| /** | ||
| * Health check endpoint. | ||
| * | ||
| * @spec openspec/changes/prometheus-metrics/tasks.md#task-4 |
There was a problem hiding this comment.
[unfixed: no container write access; also requires SEC-01/@publicpage fix first] Rule: CWE-209 / OWASP A05:2021 — Three exception messages returned verbatim in health response (lines 123, 147, 170). checkDatabase() at line 123 returns 'failed: '.$e->getMessage(), checkOpenRegister() at line 147 does the same, and checkFilesystem() at line 170 does the same AND is missing a logger call. Once @publicpage is applied, unauthenticated callers receive raw database connection-string fragments, table names, or file paths on error. Fix: replace each with a generic string ('failed: database unavailable', 'failed: app check failed', 'failed: filesystem unavailable') and add $this->logger->error() in checkFilesystem() catch. 5-line bounded change — apply in same cycle as SEC-01.
Security Review — Clyde BarcodeResult: FAIL (0 fixed, 3 unfixed, 2 blocking CRITICAL) Findings
SEC-01 and SEC-02 are re-escalations from cycle 1 (Clyde Barcode, 2026-04-18). Neither was fixed between cycles. This container (hydra-security) has no write access to the root-owned repository files — fixes cannot be applied here. Both are mechanical 1-line additions; they must be applied in the next build/fix cycle. SEC-03 is contingent on SEC-01: the error detail leakage only reaches unauthenticated callers after Dependency audit driftnpm audit discrepancy: pre-run quality reported Checks run
Checks skipped
See inline comments for per-finding detail. |
Closes #212
Summary
Enhanced Prometheus metrics and health check endpoints to provide better observability of the Procest application. Added Nextcloud version label to the
procest_infoinfo gauge, a newprocest_cases_created_todaymetric for daily case tracking, APCu caching for expensive queries with configurable TTL to reduce database load, and OpenRegister dependency verification in health checks. All implementation code includes @SPEC traceability tags linking to the specification tasks.Spec Reference
openspec/changes/prometheus-metrics/design.mdChanges
lib/Controller/MetricsController.php— Added Nextcloud version label to procest_info gauge, procest_cases_created_today metric, APCu caching (30/60s TTL) for expensive queries, and @SPEC traceability tagslib/Controller/HealthController.php— Added OpenRegister dependency check with hard-failure status when unavailable, and @SPEC traceability tagstests/Unit/Controller/MetricsControllerTest.php— Added @SPEC tags for task traceabilitytests/Unit/Controller/HealthControllerTest.php— Added @SPEC tags for task traceabilityTest Coverage
tests/Unit/Controller/MetricsControllerTest.php— Tests procest_info gauge format includes nextcloud_version label, procest_up reflects database health, and procest_cases_created_today metric formattests/Unit/Controller/HealthControllerTest.php— Tests health status returns OK when all checks pass, error status when OpenRegister unavailable, database health reflection, and version information in response