Files
Fuchs_Intranet/Fuchs/Docs/Concepts/admin-system-status.md
T
StefanandClaude Sonnet 5 107b7f54f1
Playwright Tests / test (push) Canceled after 0s
Verify mailer credentials against ProcessWeb API before showing E-Mail-Dienst as healthy
The Admin status pill previously turned green whenever the mailer was
enabled in config, without proving the account/token actually work.
Add IComService.VerifyCredentialsAsync (GET /mailer/fc?fn=verify, also
passing serverid when configured so the API confirms this server
belongs to the account) and wire it into a new "email" system-status
probe, so the pill only goes green once the Mailer API itself confirms
the credentials.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 23:51:32 +02:00

7.5 KiB

status, lastUpdated, applyTo, relatedDecisions
status lastUpdated applyTo relatedDecisions
Active 2026-09-15
Fuchs/Controllers/IntranetController.Admin.cs
Fuchs/Services/SystemStatusService.cs
Fuchs/Services/ISystemStatusService.cs
Fuchs/Services/SystemStatusModels.cs
Fuchs/js/intranet/modules/fis.admin*.js
Fuchs/js/intranet/modules/fis.admin.scss
Fuchs/js/intranet/fis_main_menu.js
0011-admin-module-system-status.md
0015-split-admin-user-management-module.md

Admin / System-Status module

Summary

The Admin module gives a privileged operator a live, in-app view of the running deployment's configuration and health, plus a test-email tool. It answers "which host am I on, can this instance reach SQL Server / Key Vault / Blob Storage / the MFR ERP, how is email wired up (including the OverrideRecipient redirect), and does sending actually work". Access is restricted to users whose fds_sys module authorization is greater than 4.

How it works

Authorization (two layers)

  1. Menu visibility — at page load fis_main_menu.js#addAdminMenuButtonsIfAuthorized calls $fis.getAuth('fds_sys') once (shared with the separate usr user-management module, see 0015); only when the level is > 4 does it push a single "Administration" dropdown entry into $ocms.ocmsmenu — containing this module's "Systemüberwachung" (init:admin) item alongside usr's "Benutzerverwaltung" (init:usr) item — and re-render #mainmenu. Users below the threshold never see the dropdown and never fetch either module's script.
  2. Server-side gate — every /do/admin/* endpoint resolves fis_getModuleAuth('fds_sys', authuser) and returns 401 unless it is > 4. The only exception is admin/auth, which returns { manage: 0 } for unauthorized users so the frontend can cleanly decline to render. This is defense in depth: hiding the button is not a security control on its own.

Request flow

click "Administration"  → $ocms.init('admin')
  POST /do/admin/auth   → { manage, level }         (manage>0 ⇒ authorized)
  load /web/fis.admin.de.js + /web/fis.admin.css
  $ocms.admin.init2()   → init3() renders the page
    POST /do/admin/status → { info, probes }         full snapshot + all probes
  per-card "Aktualisieren" → POST /do/admin/probe/<component> → { probe }
  "Test-E-Mail senden"     → POST /do/admin/testmail (to/subject/body) → { result }

Backend (ISystemStatusService / SystemStatusService, scoped)

  • GetInfo() — passive snapshot, no network calls: host (machine/OS/framework/environment/ test-deployment/uptime), database (server/catalog/login parsed from the connection string — never the password), email (mailer enabled/base-url/account/server-id/token-present + OverrideRecipient), blob (enabled/configured/containers), MFR (host/creds-present/sync), Key Vault (vault-uri/app-prefix/managed-secret-count/client-registered).
  • Probes (database, email, keyvault, blob, mfr) each return a SystemProbeResult (status = ok/error/disabled/unconfigured, ok, message, detail, durationMs, optional metrics). They never throw — failures are captured in the result. database runs SELECT @@SERVERNAME, DB_NAME(), SUSER_SNAME(); email calls IComService.VerifyCredentialsAsync (GET {BaseUrl}/mailer/fc?fn=verify with the configured Basic-auth account/token — see the Mailer API contract) and only turns ok when the Mailer API itself confirms the credentials (ok:true); when ProcessWebComSettings.ServerId is configured it is sent along as &serverid=... so the API also confirms this server belongs to the account (an account/token that is valid but for a server the account doesn't own comes back ok:false with the mismatched serverid echoed, surfaced in detail); a configured-but-wrong token surfaces as error, never as a false-positive ok — the mailerEnabled/tokenConfigured flags in GetInfo() describe configuration only and were never proof the credentials actually work; keyvault reads the first managed secret via the DI SecretClient; blob calls IBlobStorageService.CheckConnectivityAsync (account-info request plus a per-container blob count for the invoice/reminder containers, surfaced as metrics and totalled in the message); mfr calls IMfrClientFactory.Create().GetEntities(). The generic metrics (label/value pairs) is how a probe reports extra facts for display — the blob probe uses it for the file counts.
  • SendTestEmailAsync(to, subject, body) HTML-encodes the body and sends via IComService, so the Fuchs:Email:OverrideRecipient redirect applies exactly as for any other mail; the result reports the requested recipient and, when active, the override target.
  • Every probe emits the fuchs.systemstatus.probes counter tagged by component + status and an systemstatus.probe activity span.

Startup-checks widget (non-refreshable)

StartupSelfTestService runs the boot self-test once (Key Vault / Database / MFR / PDF-license / mailer, gated by Fuchs:StartupChecks:*). It now also writes its outcome to the singleton StartupCheckReporter, which GetInfo() returns as StartupChecks. The Admin page renders it as a single non-refreshable card (there is no per-check retry — the live connectivity probes above cover on-demand re-testing; the startup card is a historical record of the boot run). When the self-test is disabled (Enabled=false, the appsettings default) or has not completed, the card shows a "nicht ausgeführt" note. Each item shows OK / Fehler / übersprungen (a disabled check reports enabled=false).

Frontend (fis.admin.js + fis.admin_txt_de.js + fis.admin.scss)

Standard lazy-loaded module (same contract as inv/rep/bam). Renders a system card plus a responsive grid of status cards; connectivity cards carry a colored status pill and a per-card refresh button, the email card carries the test-email dialog. The topbar has an "Alle prüfen" button that reloads the whole snapshot. Admin responses are serialized camelCase (the DTOs are PascalCase) so the JS reads probe.status, info.database.server, etc.

Key files

  • Fuchs/Controllers/IntranetController.Admin.cs — Do_Process_Admin dispatch + shared auth gate/camelCase JSON helper (also used by the separate usr module, see 0015).
  • Fuchs/Services/ISystemStatusService.cs, SystemStatusService.cs, SystemStatusModels.cs — diagnostics service + DTOs.
  • Fuchs/Services/StartupCheckReport.cs (StartupCheckReporter singleton) + StartupSelfTestService.cs — boot self-test result captured for the non-refreshable widget.
  • Fuchs/Services/IBlobStorageService.cs / AzureBlobStorageService.cs — CheckConnectivityAsync + BlobConnectivity.
  • Fuchs/js/intranet/modules/fis.admin*.js, fis.admin.scss — the module, texts, styles (bundled via bdlconfig.json).
  • Fuchs/js/intranet/fis_main_menu.js (addAdminMenuButtonsIfAuthorized) + fis_main_go.js — conditional menu buttons (this module and usr).
  • Fuchs.Tests/SystemStatusServiceTests.cs — service tests.

User account/group management moved to its own module and concept doc — see admin-user-management.md.