Add German localization and styling for administration module
- Introduced new JavaScript file `fis.admin_txt_de.js` for German translations of administration-related terms and messages. - Created `fis.admin.css` for styling the administration interface, including layout, cards, and buttons. - Added `fis.admin.de.js` for the main functionality of the administration module, implementing features such as system status checks and email testing. - Minified version of the German JavaScript file created as `fis.admin.de.min.js`. - Minified CSS file created as `fis.admin.min.css` for optimized loading.
This commit is contained in:
@@ -168,8 +168,9 @@ OCORE_Charting (standalone — referenced by solution but no direct project ref
|
||||
|
||||
### 4.3 Service Layer (Dependency Injection)
|
||||
Business logic lives in **DI-registered services** under `Fuchs/Services/` behind interfaces, injected into `IntranetController`:
|
||||
`IComService`, `IPdfService`, `IInvoiceService`, `IReminderService`, `IReportService`, `IWidgetService`, `IBankingService`, `IMfrClientFactory`.
|
||||
`IComService`, `IPdfService`, `IInvoiceService`, `IReminderService`, `IReportService`, `IWidgetService`, `IBankingService`, `IMfrClientFactory`, `ISystemStatusService`.
|
||||
Stateless services (`IPdfService`, `IBankingService`, `IMfrClientFactory`) are singletons; DB/request-scoped services are scoped (see `Program.cs`).
|
||||
The **Admin** module (`Do_Process_Admin`, `ISystemStatusService`) surfaces a live system-status/diagnostics page (host, SQL/Key Vault/blob/MFR connectivity, email config, test-email) restricted to `fds_sys` > 4 — see ADR [0011](Decisions/0011-admin-module-system-status.md) and the [concept doc](Concepts/admin-system-status.md).
|
||||
`FdsInvoiceData` / `FdsReminderData` are now **pure data holders** (parse + properties); loading, persistence and PDF generation live in the services (fully async — no `Task.Run(...).Wait()`).
|
||||
`FuchsPdf` / `FuchsVisualization` remain as static rendering libraries used *by* the services. The earlier static, controller-coupled helpers (`FuchsWidgets`, `FuchsReports`, `Banking`, `FuchsFdsEmail`) have been removed.
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
status: Active
|
||||
lastUpdated: 2026-07-16
|
||||
applyTo:
|
||||
- "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"
|
||||
relatedDecisions:
|
||||
- "0011-admin-module-system-status.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#addAdminMenuIfAuthorized` calls
|
||||
`$fis.getAuth('fds_sys')`; only when the level is > 4 does it push the `init:admin`
|
||||
button into `$ocms.ocmsmenu` and re-render `#mainmenu`. Users below the threshold never
|
||||
see the button and never fetch the module 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`, `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()`; `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 + auth gate + camelCase JSON helper.
|
||||
- `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` (`addAdminMenuIfAuthorized`) + `fis_main_go.js` — conditional menu button.
|
||||
- `Fuchs.Tests/SystemStatusServiceTests.cs` — service tests.
|
||||
|
||||
## Related decisions
|
||||
- [0011 — Admin module gated on `fds_sys` > 4](../Decisions/0011-admin-module-system-status.md)
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
status: Accepted
|
||||
date: 2026-07-16
|
||||
applyTo:
|
||||
- "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"
|
||||
supersededBy: ""
|
||||
---
|
||||
|
||||
# 0011 — Admin module gated on `fds_sys` > 4
|
||||
|
||||
## Context
|
||||
Operators needed an in-app view of the running deployment's health: which host it
|
||||
runs on, whether SQL Server / Azure Key Vault / Azure Blob Storage / the MFR ERP are
|
||||
reachable, how the email service is configured (including the dev/test
|
||||
`Fuchs:Email:OverrideRecipient` redirect), and a way to send a test email. The
|
||||
`StartupSelfTestService` already probes most of these once at startup and writes the
|
||||
result to the log — but that is invisible to a logged-in operator and cannot be re-run
|
||||
on demand. This is privileged, infrastructure-revealing information (server names,
|
||||
account names, connectivity state) that must not be exposed to ordinary users.
|
||||
|
||||
## Decision
|
||||
- Add an **Admin** module (`admin`) alongside the existing invoice/reminder/report/banking
|
||||
modules, following the same frontend module contract (`init:admin` → `admin/auth`
|
||||
returns `{ manage }` → lazy-load `/web/fis.admin.de.js` + `/web/fis.admin.css` →
|
||||
`init2()`).
|
||||
- Access is gated on the caller's **`fds_sys` module authorization being strictly greater
|
||||
than 4** (`fis_getModuleAuth('fds_sys', authuser) > 4`). The menu button is only rendered,
|
||||
and the module script only fetched, for such users; **every** Admin data endpoint
|
||||
additionally re-checks the level server-side (defense in depth) and returns 401 otherwise.
|
||||
`admin/auth` is the sole endpoint that answers for unauthorized users too (it returns
|
||||
`manage: 0`), so the frontend can decide whether to render the module at all.
|
||||
- Backend diagnostics live in a new DI-registered `ISystemStatusService` (scoped). It exposes
|
||||
a passive `GetInfo()` snapshot and live, never-throwing connectivity probes
|
||||
(`database`, `keyvault`, `blob`, `mfr`) plus `SendTestEmailAsync`. The test email goes
|
||||
through the normal `IComService` pipeline, so the `OverrideRecipient` safety net applies
|
||||
exactly as for any other outbound mail.
|
||||
- The service never returns secrets — only presence flags and non-sensitive values
|
||||
(server/catalog/login name, storage account name, error text). Responses are serialized
|
||||
camelCase to match the frontend's lowercase convention.
|
||||
|
||||
## Consequences
|
||||
- `fds_sys` is now a security-relevant authorization key: granting it a value > 4 exposes
|
||||
infrastructure status and the ability to send test emails. Provision it deliberately.
|
||||
- `SystemStatusService` is intentionally **separate** from `StartupSelfTestService` rather
|
||||
than a shared refactor: the startup service's probe methods are `protected virtual` and its
|
||||
tests override them, so folding both onto one probe surface would have broken that contract.
|
||||
The two therefore duplicate a little probe logic (Key Vault secret read, MFR `GetEntities`,
|
||||
SQL `SELECT`); keep them behaviourally aligned when either changes.
|
||||
- New probes (or new status facts) belong in `SystemStatusService` behind the same
|
||||
`SystemProbeResult` / `SystemInfoSnapshot` shapes; add the component id to
|
||||
`SystemStatusService.Components` and a card in `fis.admin.js`.
|
||||
- Blob connectivity is checked via a new `IBlobStorageService.CheckConnectivityAsync`
|
||||
(account-info request); it too never throws.
|
||||
|
||||
## Alternatives considered
|
||||
- **Reuse `StartupSelfTestService` directly.** Rejected — see Consequences; its virtual/overridden
|
||||
probe surface is owned by its tests, and it is a one-shot `BackgroundService`, not a
|
||||
request-scoped query service.
|
||||
- **Gate only in the frontend (hide the menu button).** Rejected — the endpoints would still be
|
||||
reachable by crafting the POST. Server-side enforcement on every endpoint is required.
|
||||
- **A separate `fds_admin`/new module-auth key.** Rejected — `fds_sys` already models
|
||||
system-level privilege; reusing it avoids a parallel permission to provision.
|
||||
Reference in New Issue
Block a user