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
- -

Bentornato

-

Inserisci le tue credenziali per accedere alla console.

+ + +

Accedi

+

Inserisci le tue credenziali per accedere ai servizi.

@@ -19,7 +22,6 @@ type="text" id="username" name="username" - placeholder="Il tuo username" autocomplete="username" />
@@ -29,7 +31,6 @@ type="password" id="password" name="password" - placeholder="La tua password" autocomplete="current-password" />
@@ -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
+ Logo 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; } }