feat: add read-only System Info admin page with health checks and module inventory

New page at /admin/system-info with three tabs:
- Overview: PHP version, SAPI, environment, 6 health checks (DB, schema,
  storage, RBAC baseline, admin role, scheduler heartbeat)
- Modules: table of active modules with version, dependencies, permission count
- Permissions: active/inactive counts and per-source breakdown

Gated behind new system_info.view permission assigned to Admin role.
No mutations — purely diagnostic/observability.

Includes SystemHealthService, SystemInfoService, SystemHealthRepository
with interface, DI registration, i18n keys (de+en), idempotent DB update
script, and 17 new PHPUnit tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 15:08:02 +01:00
parent be8bf496cb
commit cf8c59d3f8
17 changed files with 1164 additions and 4 deletions

View File

@@ -1052,6 +1052,7 @@ VALUES
('system_audit.purge', 'Can purge system audit logs', 1, 1),
('api_tokens.manage', 'Can manage user API tokens', 1, 1),
('stats.view', 'Can view statistics', 1, 1),
('system_info.view', 'Can view system info and health status', 1, 1),
('roles.assign_all', 'Can assign all roles (bypass assignable-roles check)', 1, 1),
('notifications.view', 'Can view and manage own notifications', 1, 1)
ON DUPLICATE KEY UPDATE
@@ -1153,7 +1154,7 @@ JOIN permissions p ON p.`key` IN (
'api_docs.view', 'docs.view',
'api_tokens.manage',
'mail_log.view', 'api_audit.view', 'system_audit.view', 'system_audit.purge',
'stats.view',
'stats.view', 'system_info.view',
'roles.assign_all'
)
WHERE r.description IN ('Admin', 'Administrator') OR r.id = 1

View File

@@ -0,0 +1,14 @@
-- Add system_info.view permission for existing installs.
-- Idempotent: ON DUPLICATE KEY prevents re-insert.
INSERT INTO `permissions` (`key`, `description`, `active`, `is_system`)
VALUES ('system_info.view', 'Can view system info and health status', 1, 1)
ON DUPLICATE KEY UPDATE `description` = VALUES(`description`);
-- Assign to Admin role(s) for existing installs.
INSERT INTO `role_permissions` (`role_id`, `permission_id`, `created`)
SELECT r.id, p.id, NOW()
FROM roles r
JOIN permissions p ON p.`key` = 'system_info.view'
WHERE r.description IN ('Admin', 'Administrator') OR r.id = 1
ON DUPLICATE KEY UPDATE role_id = role_id;