From a0cb98bd06ebf2d15b32051e849431a573a493b7 Mon Sep 17 00:00:00 2001
From: Giuseppe Raffa <77052701+sesee3@users.noreply.github.com>
Date: Fri, 29 May 2026 14:03:55 +0200
Subject: [PATCH] feat(auth): Added UIs + Security middlewares and basic API
for user and sessions. Added also shared cookie between pages of the meb
server to authentication persist
---
auth/src/index.js | 7 +-
auth/src/middlewares/internalware.js | 2 +-
auth/src/pages/login.html | 23 +-
auth/src/pages/profile.html | 25 +-
auth/src/routes/auth.js | 3 +
auth/src/routes/pages.js | 35 +-
auth/src/static/imgs/logo.svg | 11 +
auth/src/static/styles/style.css | 698 +++++++++++++--------------
8 files changed, 400 insertions(+), 404 deletions(-)
create mode 100644 auth/src/static/imgs/logo.svg
diff --git a/auth/src/index.js b/auth/src/index.js
index 43c1351..7c1151e 100644
--- a/auth/src/index.js
+++ b/auth/src/index.js
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
import { authRouter } from './routes/auth.js';
import { userAPIs } from './routes/users.js';
import { sessionsAPIs } from './routes/sessions.js';
-import { pagesAPIs } from './routes/pages.js';
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -33,7 +33,8 @@ app.get('/health', (_req, res) => {
});
});
-// Pagine web pubbliche (HTML) — /login, /profile, /config.js
+// Pagine web
+import { pagesAPIs } from './routes/pages.js';
app.use('/', pagesAPIs);
// API JSON
@@ -47,6 +48,6 @@ app.use((err, _req, res, _next) => {
res.status(500).json({ error: 'internal_error' });
});
-app.listen(Number(process.env.PORT ?? 3000), '0.0.0.0', () => {
+app.listen(3000, '0.0.0.0', () => {
console.log('Auth started');
});
diff --git a/auth/src/middlewares/internalware.js b/auth/src/middlewares/internalware.js
index 69077e5..7d457f9 100644
--- a/auth/src/middlewares/internalware.js
+++ b/auth/src/middlewares/internalware.js
@@ -1,4 +1,4 @@
-const token = process.env.INTERNAL_API_TOKEN;
+const token = process.env.INTERNAL_TOKEN;
export function internalware(req, res, next) {
const header = req.get('X-Internal-Token');
diff --git a/auth/src/pages/login.html b/auth/src/pages/login.html
index 97091e5..5015f38 100644
--- a/auth/src/pages/login.html
+++ b/auth/src/pages/login.html
@@ -3,14 +3,17 @@
-
+
MEB — Accedi
-
MEB
-
Bentornato
-
Inserisci le tue credenziali per accedere alla console.
+
+
+
+
+
Accedi
+
Inserisci le tue credenziali per accedere ai servizi.
@@ -79,11 +80,17 @@
// Redirect al profilo. Il cookie httpOnly è già stato impostato
// dal server nella response; sarà valido anche per console
// (stesso host:port in dev, stesso dominio padre in prod).
- const next = new URLSearchParams(location.search).get("next");
+ const next = new URLSearchParams(location.search).get(
+ "next",
+ );
window.location.href = next || "/profile";
} else {
let data = {};
- try { data = await res.json(); } catch { /* ignore */ }
+ try {
+ data = await res.json();
+ } catch {
+ /* ignore */
+ }
errorMsg.textContent =
data.message || "Errore durante il login.";
}
diff --git a/auth/src/pages/profile.html b/auth/src/pages/profile.html
index d942048..9091cc7 100644
--- a/auth/src/pages/profile.html
+++ b/auth/src/pages/profile.html
@@ -3,14 +3,13 @@
-
-
+
MEB — Profilo
- MEB
+
@@ -62,7 +61,13 @@
e poi leggono window.MEB_CONFIG.
// In questo modo gli URL dei servizi (console, api) sono iniettati dal server e
// cambiano automaticamente fra dev e prod senza toccare l'HTML.
-router.get('/config.js', (_req, res) => {
- const config = {
- env: process.env.NODE_ENV || 'development',
- console: process.env.CONSOLE_PUBLIC_URL || 'http://localhost:4003',
- api: process.env.API_PUBLIC_URL || 'http://localhost:4000',
- auth: process.env.AUTH_PUBLIC_URL || '', // vuoto = same-origin (la pagina è servita da auth)
- };
- res.type('application/javascript')
- .set('Cache-Control', 'no-store')
- .send(`window.MEB_CONFIG = Object.freeze(${JSON.stringify(config)});`);
-});
+// router.get('/config.js', (_req, res) => {
+// const config = {
+// env: process.env.NODE_ENV || 'development',
+// console: process.env.CONSOLE_PUBLIC_URL || 'http://localhost:4003',
+// api: process.env.API_PUBLIC_URL || 'http://localhost:4000',
+// ml: process.env.ML_PUBLIC_URL || 'http://localhost:4005',
+// auth: process.env.AUTH_PUBLIC_URL || '', // vuoto = same-origin (la pagina è servita da auth)
+// };
+// res.type('application/javascript')
+// .set('Cache-Control', 'no-store')
+// .send(`window.MEB_CONFIG = Object.freeze(${JSON.stringify(config)});`);
+// });
export { router as pagesAPIs };
diff --git a/auth/src/static/imgs/logo.svg b/auth/src/static/imgs/logo.svg
new file mode 100644
index 0000000..8d7e2a0
--- /dev/null
+++ b/auth/src/static/imgs/logo.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/auth/src/static/styles/style.css b/auth/src/static/styles/style.css
index 211c651..92c2fe4 100644
--- a/auth/src/static/styles/style.css
+++ b/auth/src/static/styles/style.css
@@ -15,46 +15,42 @@
/* ── Variabili tema ── */
:root {
- --bg-body: #0f1117;
- --bg-card: #1a1d27;
- --border: #2a2d3a;
- --text-primary: #f0f0f0;
- --text-secondary: #6b7280;
- --text-label: #9ca3af;
- --input-bg: #0f1117;
- --input-placeholder: #3d4150;
- --accent: #4f8ef7;
- --accent-hover: #3b7de8;
- --accent-active: #2f6dd4;
- --error: #f87171;
- --shadow: rgba(0, 0, 0, 0.4);
-
- --grad-1: #0f1117;
- --grad-2: #1a1d27;
- --grad-3: #0d1520;
- --grad-4: #111827;
+ --bg: black;
+ --foreground: white;
+ --foreground-secondary: #999999;
+ --foreground-tertiary: #404040;
+ --foreground-quaternary: #262626;
+ --primary: #51d5ff;
+ --secondary: rgb(81, 213, 255, 0.3);
+ --tertiary: rgb(81, 213, 255, 0.18);
+ --hover: #3b7de8;
+ --active: #2f6dd4;
+ --danger: #de090d;
+ --warning: #ff8306;
+ --success: #06ff13;
+ --box-shadow:
+ 0 8px 32px var(--tertiary), 0 0 0 1px var(--foreground-quaternary);
}
@media (prefers-color-scheme: light) {
:root {
- --bg-body: #eef1f7;
- --bg-card: #ffffff;
- --border: #dde1ec;
- --text-primary: #111827;
- --text-secondary: #6b7280;
- --text-label: #4b5563;
- --input-bg: #f5f7fc;
- --input-placeholder: #b0b8cc;
- --accent: #3b7de8;
- --accent-hover: #2f6dd4;
- --accent-active: #2260be;
- --error: #dc2626;
- --shadow: rgba(0, 0, 0, 0.1);
+ --bg: white;
+ --foreground: black;
+ --foreground-secondary: rgb(60, 60, 67, 0.6);
+ --foreground-tertiary: rgb(60, 60, 67, 0.3);
+ --foreground-quaternary: rgb(60, 60, 67, 0.18);
- --grad-1: #dce8ff;
- --grad-2: #eef1f7;
- --grad-3: #d4e4fb;
- --grad-4: #e8edf8;
+ --primary: #00559d;
+ --secondary: rgb(0, 85, 157, 0.3);
+ --tertiary: rgb(0, 85, 157, 0.18);
+ --hover: #056ac0;
+ --active: #1355c1;
+ --danger: #de090d;
+ --warning: #ff8306;
+ --success: #06ff13;
+ --box-shadow:
+ 0 8px 32px rgba(0, 0, 0, 0.08),
+ 0 0 0 1px var(--foreground-quaternary);
}
}
@@ -77,293 +73,24 @@ body {
display: flex;
align-items: center;
justify-content: center;
- background: linear-gradient(
- 135deg,
- var(--grad-1),
- var(--grad-2),
- var(--grad-3),
- var(--grad-4)
- );
+ background: var(--bg);
+ color: var(--foreground);
background-size: 400% 400%;
animation: gradientShift 12s ease infinite;
}
-/* ── Card ── */
-.login-container {
- width: 100%;
- max-width: 400px;
- padding: 48px 40px;
- background-color: var(--bg-card);
- border-radius: 16px;
- border: 1px solid var(--border);
- box-shadow: 0 24px 48px var(--shadow);
- transition:
- background-color 0.3s,
- border-color 0.3s;
-}
-
-.login-logo {
- font-size: 13px;
- font-weight: bold;
- letter-spacing: 4px;
- color: var(--accent);
- text-transform: uppercase;
- margin-bottom: 24px;
-}
-
-.login-container h1 {
- font-size: 26px;
- color: var(--text-primary);
- margin-bottom: 8px;
- transition: color 0.3s;
-}
-
-.login-container p {
- font-size: 14px;
- color: var(--text-secondary);
- margin-bottom: 32px;
- line-height: 1.5;
- transition: color 0.3s;
-}
-
-/* ── Form ── */
-.login-form {
- display: flex;
- flex-direction: column;
- gap: 20px;
-}
-
-.input-group {
- display: flex;
- flex-direction: column;
- gap: 6px;
-}
-
-.input-group label {
- font-size: 13px;
- color: var(--text-label);
- letter-spacing: 0.3px;
- transition: color 0.3s;
-}
-
-.input-group input {
- width: 100%;
- padding: 12px 14px;
- background-color: var(--input-bg);
- border: 1px solid var(--border);
- border-radius: 8px;
- color: var(--text-primary);
- font-family: "Elms", sans-serif;
- font-size: 15px;
- outline: none;
- transition:
- border-color 0.2s,
- background-color 0.3s,
- color 0.3s;
-}
-
-.input-group input::placeholder {
- color: var(--input-placeholder);
-}
-
-.input-group input:focus {
- border-color: var(--accent);
-}
-
-.error-message {
- font-size: 13px;
- color: var(--error);
- min-height: 18px;
- transition: color 0.3s;
-}
-
-/* ── Bottone ── */
-@keyframes btnPulse {
- 0% {
- box-shadow: 0 0 0 0 rgba(79, 142, 247, 0.5);
- }
- 70% {
- box-shadow: 0 0 0 10px rgba(79, 142, 247, 0);
- }
- 100% {
- box-shadow: 0 0 0 0 rgba(79, 142, 247, 0);
- }
-}
-
-@keyframes btnSpin {
- to {
- transform: rotate(360deg);
- }
-}
-
-.btn-login {
- width: 100%;
- padding: 13px;
- background-color: var(--accent);
- color: #fff;
- border: none;
- border-radius: 8px;
- font-family: "Elms", sans-serif;
- font-size: 15px;
- cursor: pointer;
- position: relative;
- overflow: hidden;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- transition:
- background-color 0.2s,
- transform 0.15s,
- box-shadow 0.2s;
-}
-
-.btn-login::after {
- content: "";
- position: absolute;
- width: 100%;
- height: 100%;
- background: rgba(255, 255, 255, 0.08);
- top: 0;
- left: -100%;
- transition: left 0.3s ease;
-}
-
-.btn-login:hover::after {
- left: 0;
-}
-
-.btn-login:hover {
- background-color: var(--accent-hover);
- transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(79, 142, 247, 0.35);
-}
-
-.btn-login:active {
- background-color: var(--accent-active);
- transform: translateY(0px);
- box-shadow: none;
- animation: btnPulse 0.4s ease-out;
-}
-
-.btn-login:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none;
- box-shadow: none;
-}
-
-.btn-spinner {
- width: 16px;
- height: 16px;
- border: 2px solid rgba(255, 255, 255, 0.3);
- border-top-color: #fff;
- border-radius: 50%;
- animation: btnSpin 0.7s linear infinite;
- display: none;
- flex-shrink: 0;
-}
-
-.btn-login.loading .btn-spinner {
- display: block;
-}
-
-.btn-login.loading .btn-label {
- opacity: 0.7;
-}
-
/* ════════════════════════════════════════════════════════════
- PAGINA PROFILO
+ COMPONENTI CONDIVISI
════════════════════════════════════════════════════════════ */
-body.page-profile {
- display: block;
- align-items: stretch;
- justify-content: stretch;
-}
-
-.profile-shell {
- max-width: 960px;
- margin: 0 auto;
- padding: 32px 20px 80px;
-}
-
-/* ── Topbar ── */
-.profile-topbar {
- display: flex;
- align-items: center;
- gap: 24px;
- padding: 12px 0 28px;
- border-bottom: 1px solid var(--border);
- margin-bottom: 32px;
-}
-
-.profile-brand {
- font-size: 13px;
- font-weight: bold;
- letter-spacing: 4px;
- color: var(--accent);
- text-transform: uppercase;
-}
-
-.profile-nav {
- display: flex;
- gap: 8px;
- flex: 1;
-}
-
-.nav-link {
- padding: 8px 14px;
- border-radius: 8px;
- border: 1px solid var(--border);
- color: var(--text-primary);
- text-decoration: none;
- font-size: 13px;
- transition:
- background 0.15s,
- border-color 0.15s,
- transform 0.1s;
-}
-
-.nav-link:hover {
- border-color: var(--accent);
- background: var(--input-bg);
- transform: translateY(-1px);
-}
-
-.btn-ghost {
- padding: 8px 16px;
- background: transparent;
- color: var(--text-primary);
- border: 1px solid var(--border);
- border-radius: 8px;
- cursor: pointer;
- font-family: "Elms", sans-serif;
- font-size: 13px;
- transition:
- background 0.15s,
- border-color 0.15s,
- color 0.15s;
-}
-
-.btn-ghost:hover {
- border-color: var(--error);
- color: var(--error);
-}
-
-/* ── Main grid ── */
-.profile-main {
- display: grid;
- gap: 24px;
-}
-
-/* ── Card base ── */
.card {
- background-color: var(--bg-card);
- border: 1px solid var(--border);
+ user-select: none;
+ -webkit-user-select: none;
+ background: var(--bg);
+ border: none;
border-radius: 16px;
padding: 28px;
- box-shadow: 0 8px 24px var(--shadow);
+ box-shadow: var(--box-shadow);
}
.card-header {
@@ -376,51 +103,302 @@ body.page-profile {
.card-header h2 {
font-size: 16px;
font-weight: 500;
- color: var(--text-primary);
+ color: var(--foreground);
}
.muted {
- color: var(--text-secondary);
font-size: 13px;
+ color: var(--foreground-secondary);
+}
+
+.mono {
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+ font-size: 12px;
+ word-break: break-all;
+}
+
+.badge {
+ font-size: 10px;
+ padding: 2px 8px;
+ border-radius: 10px;
+ background: var(--primary);
+ color: var(--bg);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+/* ── Input condiviso ── */
+.input-group {
+ user-select: none;
+ -webkit-user-select: none;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.input-group label {
+ font-size: 13px;
+ color: var(--foreground-tertiary);
+ letter-spacing: 0.3px;
+}
+
+.input-group input {
+ width: 100%;
+ padding: 12px 14px;
+ background: var(--bg);
+ border: 0.4px solid var(--foreground-tertiary);
+ border-radius: 10px;
+ color: var(--foreground);
+ font-family: "Elms", sans-serif;
+ font-size: 15px;
+ outline: none;
+ transition: border-color 0.2s;
+}
+
+.input-group input::placeholder {
+ color: var(--foreground-tertiary);
+}
+
+.input-group input:focus {
+ border-color: var(--primary);
+}
+
+.error-message {
+ font-size: 13px;
+ color: var(--danger);
+ min-height: 18px;
+}
+
+/* ── Animazioni bottoni ── */
+@keyframes btnPulse {
+ 0% {
+ box-shadow: 0 0 0 0 var(--secondary);
+ }
+ 70% {
+ box-shadow: 0 0 0 10px transparent;
+ }
+ 100% {
+ box-shadow: 0 0 0 0 transparent;
+ }
+}
+
+@keyframes btnSpin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* ── Bottone ghost (es. logout, revoca) ── */
+.btn-ghost {
+ padding: 8px 16px;
+ background: transparent;
+ color: var(--foreground);
+ border: 0.4px solid var(--foreground-tertiary);
+ border-radius: 10px;
+ cursor: pointer;
+ font-family: "Elms", sans-serif;
+ font-size: 13px;
+ transition:
+ border-color 0.15s,
+ color 0.15s;
+}
+
+.btn-ghost:hover {
+ border-color: var(--danger);
+ color: var(--danger);
+}
+
+/* ════════════════════════════════════════════════════════════
+ PAGINA LOGIN
+ ════════════════════════════════════════════════════════════ */
+
+.login-container {
+ width: 100%;
+ max-width: 400px;
+ padding: 48px 40px;
+ background: var(--bg);
+ border: none;
+ border-radius: 16px;
+ box-shadow: var(--box-shadow);
+}
+
+.login-logo {
+ font-size: 12px;
+ font-weight: bold;
+ letter-spacing: 4px;
+ color: var(--primary);
+ text-transform: uppercase;
+ margin-bottom: 28px;
+}
+
+.login-container h1 {
+ font-size: 26px;
+ color: var(--foreground);
+ margin-bottom: 6px;
+}
+
+.login-container > p {
+ font-size: 14px;
+ color: var(--foreground-secondary);
+ margin-bottom: 32px;
+ line-height: 1.5;
+}
+
+.login-form {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.btn-login {
+ width: 100%;
+ padding: 13px;
+ background: var(--primary);
+ color: var(--bg);
+ border: none;
+ border-radius: 10px;
+ font-family: "Elms", sans-serif;
+ font-size: 15px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ transition:
+ opacity 0.2s,
+ transform 0.15s;
+}
+
+.btn-login:hover {
+ opacity: 0.85;
+ transform: translateY(-1px);
+}
+
+.btn-login:active {
+ opacity: 1;
+ transform: translateY(0);
+ animation: btnPulse 0.4s ease-out;
+}
+
+.btn-login:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.btn-spinner {
+ width: 16px;
+ height: 16px;
+ border: 2px solid rgba(0, 0, 0, 0.25);
+ border-top-color: var(--bg);
+ border-radius: 50%;
+ animation: btnSpin 0.7s linear infinite;
+ display: none;
+ flex-shrink: 0;
+}
+
+.btn-login.loading .btn-spinner {
+ display: block;
+}
+
+.btn-login.loading .btn-label {
+ opacity: 0.8;
+}
+
+/* ════════════════════════════════════════════════════════════
+ PAGINA PROFILO
+ ════════════════════════════════════════════════════════════ */
+
+body.page-profile {
+ display: block;
+}
+
+.profile-shell {
+ max-width: 960px;
+ margin: 0 auto;
+ padding: 32px 20px 80px;
+}
+
+/* ── Topbar ── */
+.profile-topbar {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding-bottom: 24px;
+ border-bottom: 0.4px solid var(--foreground-tertiary);
+ margin-bottom: 32px;
+}
+
+.profile-brand {
+ font-size: 12px;
+ font-weight: bold;
+ letter-spacing: 4px;
+ color: var(--primary);
+ text-transform: uppercase;
+ flex-shrink: 0;
+}
+
+.profile-nav {
+ display: flex;
+ gap: 8px;
+ flex: 1;
+}
+
+.nav-link {
+ padding: 6px 14px;
+ border-radius: 8px;
+ border: 0.4px solid var(--foreground-tertiary);
+ color: var(--foreground);
+ text-decoration: none;
+ font-size: 13px;
+ transition:
+ border-color 0.15s,
+ transform 0.1s;
+}
+
+.nav-link:hover {
+ border-color: var(--primary);
+ transform: translateY(-1px);
+}
+
+/* ── Main grid ── */
+.profile-main {
+ display: grid;
+ gap: 20px;
}
/* ── Hero ── */
.profile-hero {
display: flex;
align-items: center;
- gap: 24px;
+ gap: 20px;
}
.avatar {
- width: 72px;
- height: 72px;
+ width: 64px;
+ height: 64px;
border-radius: 50%;
- background: linear-gradient(
- 135deg,
- var(--accent),
- var(--accent-active)
- );
- color: #fff;
+ background: var(--primary);
+ color: var(--bg);
display: flex;
align-items: center;
justify-content: center;
- font-size: 30px;
+ font-size: 28px;
font-weight: bold;
flex-shrink: 0;
- box-shadow: 0 4px 14px rgba(79, 142, 247, 0.35);
}
.hero-info h1 {
- font-size: 24px;
- color: var(--text-primary);
+ font-size: 22px;
+ color: var(--foreground);
margin-bottom: 4px;
}
-/* ── Sessions list ── */
+/* ── Sessioni ── */
.sessions-list {
display: flex;
flex-direction: column;
- gap: 12px;
+ gap: 10px;
}
.session-row {
@@ -428,16 +406,15 @@ body.page-profile {
align-items: center;
justify-content: space-between;
gap: 16px;
- padding: 16px;
- border: 1px solid var(--border);
+ padding: 14px 16px;
+ border: 0.4px solid var(--foreground-tertiary);
border-radius: 10px;
- background: var(--input-bg);
+ background: var(--bg);
transition: border-color 0.15s;
}
.session-row.is-current {
- border-color: var(--accent);
- background: rgba(79, 142, 247, 0.05);
+ border-color: var(--primary);
}
.session-info {
@@ -447,7 +424,7 @@ body.page-profile {
.session-title {
font-size: 14px;
- color: var(--text-primary);
+ color: var(--foreground);
margin-bottom: 4px;
display: flex;
align-items: center;
@@ -457,43 +434,35 @@ body.page-profile {
.session-meta {
font-size: 12px;
line-height: 1.6;
-}
-
-.badge {
- font-size: 10px;
- padding: 2px 8px;
- border-radius: 10px;
- background: var(--accent);
- color: #fff;
- text-transform: uppercase;
- letter-spacing: 0.5px;
+ color: var(--foreground-secondary);
}
.btn-revoke {
- padding: 8px 14px;
+ padding: 6px 14px;
background: transparent;
- color: var(--error);
- border: 1px solid var(--border);
+ color: var(--danger);
+ border: 0.4px solid var(--foreground-tertiary);
border-radius: 8px;
cursor: pointer;
font-family: "Elms", sans-serif;
font-size: 12px;
+ flex-shrink: 0;
transition:
background 0.15s,
border-color 0.15s;
}
.btn-revoke:hover:not(:disabled) {
- background: rgba(248, 113, 113, 0.1);
- border-color: var(--error);
+ background: rgba(222, 9, 13, 0.08);
+ border-color: var(--danger);
}
.btn-revoke:disabled {
- opacity: 0.6;
+ opacity: 0.5;
cursor: wait;
}
-/* ── kv grid (account info) ── */
+/* ── Griglia chiave/valore ── */
.kv-grid {
display: grid;
grid-template-columns: 180px 1fr;
@@ -502,17 +471,11 @@ body.page-profile {
}
.kv-grid dt {
- color: var(--text-label);
+ color: var(--foreground-tertiary);
}
.kv-grid dd {
- color: var(--text-primary);
-}
-
-.mono {
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- font-size: 12px;
- word-break: break-all;
+ color: var(--foreground);
}
/* ── Toast ── */
@@ -523,16 +486,16 @@ body.page-profile {
transform: translateX(-50%) translateY(20px);
padding: 12px 20px;
border-radius: 10px;
- background: var(--bg-card);
- border: 1px solid var(--border);
- color: var(--text-primary);
- box-shadow: 0 8px 24px var(--shadow);
+ background: var(--foreground-quaternary);
+ border: 0.4px solid var(--foreground-tertiary);
+ color: var(--foreground);
opacity: 0;
pointer-events: none;
transition:
opacity 0.2s,
transform 0.2s;
font-size: 13px;
+ white-space: nowrap;
}
.toast.show {
@@ -541,11 +504,10 @@ body.page-profile {
}
.toast.ok {
- border-color: #22c55e;
+ border-color: var(--success);
}
-
.toast.err {
- border-color: var(--error);
+ border-color: var(--danger);
}
/* ── Responsive ── */
@@ -572,6 +534,7 @@ body.page-profile {
.kv-grid dt {
margin-top: 12px;
+ font-size: 11px;
}
.session-row {
@@ -581,5 +544,6 @@ body.page-profile {
.btn-revoke {
width: 100%;
+ text-align: center;
}
}