Nexus upgrade design and refresh

This commit is contained in:
2026-04-11 01:23:28 +02:00
parent 9d5bb2d3cf
commit e83925ba64
53 changed files with 13388 additions and 60 deletions

View File

@@ -50,11 +50,12 @@ $user = [
'sub' => (string)($claims['sub'] ?? ''),
'email' => (string)($claims['email'] ?? ''),
'name' => (string)($claims['name'] ?? ($claims['preferred_username'] ?? '')),
'username' => (string)($claims['preferred_username'] ?? $claims['email'] ?? $claims['sub'] ?? ''),
'groups' => $groups,
'id_token' => $idToken,
];
$_SESSION['auth_user'] = $user;
app()->auth()->storeUser($claims, $groups, $idToken);
if (defined('APP_AUTH_DEBUG') && APP_AUTH_DEBUG) {
$log = [
@@ -77,4 +78,6 @@ if (defined('APP_AUTH_DEBUG') && APP_AUTH_DEBUG) {
@file_put_contents(__DIR__ . '/../../../debug/oidc_login.log', json_encode($log) . PHP_EOL, FILE_APPEND);
}
redirect('/');
$returnTo = (string)($_SESSION['oidc_return_to'] ?? '/');
unset($_SESSION['oidc_return_to']);
redirect($returnTo !== '' && str_starts_with($returnTo, '/') && !str_starts_with($returnTo, '//') ? $returnTo : '/');

View File

@@ -10,7 +10,7 @@ if (!empty($_SESSION['auth_user']['id_token'])) {
$idToken = (string)$_SESSION['auth_user']['id_token'];
}
unset($_SESSION['auth_user']);
unset($_SESSION['auth_user'], $_SESSION['auth_id_token'], $_SESSION['auth_expires_at']);
if ($config->authEnabled) {
$client = new OidcClient($config);

View File

@@ -1,34 +1,76 @@
<?php
$modules = modules()->all();
declare(strict_types=1);
$auth = app()->auth();
$authUser = $auth->user();
$modules = array_values(array_filter(
$auth->filterModules(modules()->all()),
static fn (array $module): bool => !empty($module['enabled'])
));
?>
<div class="card">
<div class="pill">Core</div>
<h1 style="margin-top:.75rem;">Nexus Basis-System</h1>
<p class="muted">Aktive Module verwalten und neue Module initialisieren.</p>
<section class="home-hero" data-reveal>
<a class="brand-mark" href="/" aria-label="Nexus">
<img src="/assets/images/kusche-logo.png" alt="Kusche Logo">
</a>
<div class="brand-copy">
<span class="eyebrow">Nexus</span>
<h1><?= e(defined('APP_DOMAIN_PRIMARY') ? (string)APP_DOMAIN_PRIMARY : 'Nexus') ?></h1>
<p>Kompakter Einstieg fuer die verfuegbaren Module.</p>
</div>
<div class="theme-switcher" aria-label="Farbschema">
<?php if ($auth->isEnabled()): ?>
<a class="auth-pill" href="<?= $authUser === null ? '/auth/login' : '/auth/logout' ?>">
<?= $authUser === null ? 'Login' : 'Logout ' . e((string)($authUser['username'] ?? $authUser['name'] ?? '')) ?>
</a>
<?php endif; ?>
<label>
<span>Modus</span>
<select data-theme-mode>
<option value="day">Day</option>
<option value="night">Night</option>
</select>
</label>
<label>
<span>Farbe</span>
<select data-theme-accent>
<option value="logo">Logo</option>
<option value="pink">Pink</option>
<option value="cyan">Cyan</option>
<option value="orange">Orange</option>
<option value="green">Gruen</option>
</select>
</label>
</div>
</section>
<div style="margin-top:1rem;">
<a class="nav-link" href="/modules">Module verwalten</a>
<section class="module-list-section" data-reveal>
<div class="section-head">
<div>
<h2 class="section-title">Verfuegbare Module</h2>
<p>Module mit Login-Pflicht erscheinen erst nach passender Anmeldung.</p>
</div>
<?php if ($authUser !== null): ?>
<a class="nav-link" href="/modules">Module verwalten</a>
<?php endif; ?>
</div>
<div style="margin-top:1.5rem;" class="grid">
<?php foreach ($modules as $module): ?>
<div class="card" style="background:var(--panel-2);">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
<div>
<strong><?= e($module['title']) ?></strong>
<div class="muted" style="font-size:.85rem;"><?= e($module['description'] ?? '') ?></div>
</div>
<?php if (!empty($module['enabled'])): ?>
<span class="pill" style="border-color:var(--accent-2); color:var(--accent-2);">aktiv</span>
<?php else: ?>
<span class="pill">inaktiv</span>
<?php endif; ?>
</div>
<div style="margin-top:.75rem; display:flex; gap:10px; flex-wrap:wrap;">
<a class="nav-link" href="/module/<?= e($module['name']) ?>">Öffnen</a>
<a class="nav-link" href="/modules/setup/<?= e($module['name']) ?>">Setup</a>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php if ($modules === []): ?>
<div class="empty-state" data-reveal>
Keine Module fuer den aktuellen Zugriff sichtbar.
</div>
<?php else: ?>
<div class="module-list">
<?php foreach ($modules as $module): ?>
<a class="module-row" href="<?= e((string)($module['entry'] ?? ('/module/' . $module['name']))) ?>">
<span class="module-row__icon"><?= e(strtoupper(substr((string)($module['title'] ?? $module['name']), 0, 1))) ?></span>
<span class="module-row__content">
<span class="module-kicker"><?= e((string)($module['name'] ?? '')) ?></span>
<strong class="module-title"><?= e((string)($module['title'] ?? $module['name'] ?? 'Modul')) ?></strong>
<span class="module-desc"><?= e((string)($module['description'] ?? '')) ?></span>
</span>
<span class="module-row__action">Oeffnen</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>

View File

@@ -69,9 +69,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
modules()->saveSettings($moduleName, $payload);
modules()->saveAuth($moduleName, [
'required' => isset($_POST['auth_required']),
'users' => (string)($_POST['auth_users'] ?? ''),
'groups' => (string)($_POST['auth_groups'] ?? ''),
]);
$notice = 'Setup gespeichert.';
$current = array_replace_recursive($current, $payload);
$module = modules()->get($moduleName) ?: $module;
}
$authConfig = is_array($module['auth'] ?? null) ? $module['auth'] : ['required' => false, 'users' => [], 'groups' => []];
?>
<div class="card">
<div class="pill">Setup</div>
@@ -122,6 +129,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</label>
<?php endforeach; ?>
<div class="card" style="padding:14px; background:var(--panel-2); display:grid; gap:12px;">
<strong>Modulzugriff</strong>
<label class="muted" style="display:flex; align-items:center; gap:10px;">
<input type="checkbox" name="auth_required" value="1" <?= !empty($authConfig['required']) ? 'checked' : '' ?>>
<span>Login fuer dieses Modul erforderlich</span>
</label>
<label class="muted" style="display:grid; gap:6px;">
<span>Erlaubte Benutzer</span>
<textarea name="auth_users" rows="3" placeholder="Keycloak-Sub, Benutzername oder E-Mail, je Zeile oder Komma"><?= e(implode("\n", is_array($authConfig['users'] ?? null) ? $authConfig['users'] : [])) ?></textarea>
</label>
<label class="muted" style="display:grid; gap:6px;">
<span>Erlaubte Gruppen</span>
<textarea name="auth_groups" rows="3" placeholder="/admin oder mining-users, je Zeile oder Komma"><?= e(implode("\n", is_array($authConfig['groups'] ?? null) ? $authConfig['groups'] : [])) ?></textarea>
</label>
<small class="muted">Wenn Login aktiv ist und Benutzer/Gruppen leer bleiben, darf jeder eingeloggte Benutzer das Modul oeffnen.</small>
</div>
<div style="display:flex; gap:10px;">
<button class="cta-button" type="submit">Speichern</button>
<a class="nav-link" href="/modules">Zurück</a>

View File

@@ -19,7 +19,7 @@ $sidebarDefault = ($moduleSidebar['default'] ?? 'collapsed') === 'open' ? 'open'
$sidebarItems = $moduleSidebar['items'] ?? [];
?>
<!doctype html>
<html lang="en">
<html lang="de" data-theme="<?= e($theme) ?>" data-accent="logo">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -27,7 +27,7 @@ $sidebarItems = $moduleSidebar['items'] ?? [];
<?php asset_styles(); ?>
<?php asset_scripts('header'); ?>
</head>
<body data-theme="<?= e($theme) ?>">
<body>
<div class="bg-orb orb-a"></div>
<div class="bg-orb orb-b"></div>
@@ -40,7 +40,7 @@ $sidebarItems = $moduleSidebar['items'] ?? [];
<div class="dropdown">
<button class="nav-link dropdown-toggle" type="button">Module ▾</button>
<div class="dropdown-menu">
<?php foreach (modules()->all() as $m): ?>
<?php foreach (app()->auth()->filterModules(modules()->all()) as $m): ?>
<?php if (!empty($m['enabled'])): ?>
<a class="dropdown-item" href="/module/<?= e($m['name']) ?>"><?= e($m['title']) ?></a>
<?php endif; ?>