From f2b584342c54678eaa71fc718109f885724b8b31 Mon Sep 17 00:00:00 2001 From: hawkinslabdev <59891413+hawkinslabdev@users.noreply.github.com> Date: Mon, 18 May 2026 12:34:47 +0200 Subject: [PATCH 1/5] chore: prepare locale files --- motomate/src/lib/i18n/locales/de.json | 14 ++++++++++++-- motomate/src/lib/i18n/locales/en.json | 12 +++++++++++- motomate/src/lib/i18n/locales/es.json | 14 ++++++++++++-- motomate/src/lib/i18n/locales/fr.json | 14 ++++++++++++-- motomate/src/lib/i18n/locales/it.json | 14 ++++++++++++-- motomate/src/lib/i18n/locales/nl.json | 14 ++++++++++++-- motomate/src/lib/i18n/locales/pt.json | 14 ++++++++++++-- 7 files changed, 83 insertions(+), 13 deletions(-) diff --git a/motomate/src/lib/i18n/locales/de.json b/motomate/src/lib/i18n/locales/de.json index c30a13e..45f3263 100644 --- a/motomate/src/lib/i18n/locales/de.json +++ b/motomate/src/lib/i18n/locales/de.json @@ -55,7 +55,10 @@ "passwordMismatch": "Passwörter stimmen nicht überein", "submit": "Konto erstellen", "hasAccount": "Bereits ein Konto?", - "login": "Anmelden" + "login": "Anmelden", + "closedTitle": "Registrierung geschlossen", + "closedBody": "Neue Konten werden derzeit nicht angenommen. Wenden Sie sich an Ihren Administrator.", + "closedAction": "Zur Anmeldung" }, "magicLink": { "verifying": "Wir melden Sie an…", @@ -927,7 +930,14 @@ "workflows": "Workflows", "sourceCode": "Quellcode", "reportIssue": "Problem melden", - "tapAgain": "Nochmal tippen zum Offnen" + "tapAgain": "Nochmal tippen zum Offnen", + "changelog": "Changelog" + }, + "changelog": { + "title": "Was ist neu?", + "subtitle": "Sieh nach, was sich in den neuesten Versionen geändert hat.", + "refresh": "Aktualisieren", + "noContent": "Keine Änderungen gefunden. Versuche es später erneut." } }, "units": { diff --git a/motomate/src/lib/i18n/locales/en.json b/motomate/src/lib/i18n/locales/en.json index 7f36937..9727e2c 100644 --- a/motomate/src/lib/i18n/locales/en.json +++ b/motomate/src/lib/i18n/locales/en.json @@ -55,7 +55,10 @@ "passwordMismatch": "Passwords do not match", "submit": "Create account", "hasAccount": "Already have an account?", - "login": "Log in" + "login": "Log in", + "closedTitle": "Registration closed", + "closedBody": "New accounts are not accepted at this time. Contact your administrator for access.", + "closedAction": "Go to log in" }, "magicLink": { "title": "Magic link", @@ -737,6 +740,7 @@ "account": "Account", "notifications": "Notifications", "workflows": "Workflows", + "changelog": "Changelog", "sourceCode": "Source Code", "reportIssue": "Report an Issue", "tapAgain": "Tap again to open" @@ -929,6 +933,12 @@ "prevPage": "Previous", "nextPage": "Next", "pageOf": "Page {page} of {total}" + }, + "changelog": { + "title": "What's new?", + "subtitle": "See what changed in the latest versions.", + "refresh": "Refresh", + "noContent": "No changes found. Try again later." } }, "layout": { diff --git a/motomate/src/lib/i18n/locales/es.json b/motomate/src/lib/i18n/locales/es.json index b9f7449..ae99d29 100644 --- a/motomate/src/lib/i18n/locales/es.json +++ b/motomate/src/lib/i18n/locales/es.json @@ -55,7 +55,10 @@ "passwordMismatch": "Las contraseñas no coinciden", "submit": "Crear cuenta", "hasAccount": "¿Ya tienes cuenta?", - "login": "Iniciar sesión" + "login": "Iniciar sesión", + "closedTitle": "Registro cerrado", + "closedBody": "Los nuevos registros no están abiertos. Contacta con tu administrador para obtener acceso.", + "closedAction": "Ir a iniciar sesión" }, "magicLink": { "verifying": "Iniciando sesión…", @@ -927,7 +930,14 @@ "workflows": "Flujos de trabajo", "sourceCode": "Codigo fuente", "reportIssue": "Reportar un problema", - "tapAgain": "Toca de nuevo para abrir" + "tapAgain": "Toca de nuevo para abrir", + "changelog": "Changelog" + }, + "changelog": { + "title": "¿Qué hay de nuevo?", + "subtitle": "Consulta qué ha cambiado en las últimas versiones.", + "refresh": "Actualizar", + "noContent": "No se encontraron cambios. Inténtalo más tarde." } }, "units": { diff --git a/motomate/src/lib/i18n/locales/fr.json b/motomate/src/lib/i18n/locales/fr.json index f7ad560..d181f98 100644 --- a/motomate/src/lib/i18n/locales/fr.json +++ b/motomate/src/lib/i18n/locales/fr.json @@ -55,7 +55,10 @@ "passwordMismatch": "Les mots de passe ne correspondent pas", "submit": "Créer un compte", "hasAccount": "Déjà un compte ?", - "login": "Se connecter" + "login": "Se connecter", + "closedTitle": "Inscription fermée", + "closedBody": "Les nouveaux comptes ne sont pas acceptés pour l'instant. Contactez votre administrateur pour obtenir l'accès.", + "closedAction": "Aller à la connexion" }, "magicLink": { "verifying": "Connexion en cours…", @@ -927,7 +930,14 @@ "workflows": "Flux de travail", "sourceCode": "Code source", "reportIssue": "Signaler un probleme", - "tapAgain": "Appuyez a nouveau pour ouvrir" + "tapAgain": "Appuyez a nouveau pour ouvrir", + "changelog": "Changelog" + }, + "changelog": { + "title": "Quoi de neuf ?", + "subtitle": "Découvrez ce qui a changé dans les dernières versions.", + "refresh": "Actualiser", + "noContent": "Aucune modification trouvée. Réessayez plus tard." } }, "units": { diff --git a/motomate/src/lib/i18n/locales/it.json b/motomate/src/lib/i18n/locales/it.json index 33407e3..659d169 100644 --- a/motomate/src/lib/i18n/locales/it.json +++ b/motomate/src/lib/i18n/locales/it.json @@ -55,7 +55,10 @@ "passwordMismatch": "Le password non corrispondono", "submit": "Crea account", "hasAccount": "Hai già un account?", - "login": "Accedi" + "login": "Accedi", + "closedTitle": "Registrazione chiusa", + "closedBody": "I nuovi account non sono accettati al momento. Contatta il tuo amministratore per l'accesso.", + "closedAction": "Vai al login" }, "magicLink": { "verifying": "Accesso in corso…", @@ -928,7 +931,14 @@ "workflows": "Flussi di lavoro", "sourceCode": "Codice sorgente", "reportIssue": "Segnala un problema", - "tapAgain": "Tocca di nuovo per aprire" + "tapAgain": "Tocca di nuovo per aprire", + "changelog": "Changelog" + }, + "changelog": { + "title": "Cosa c'è di nuovo?", + "subtitle": "Scopri cosa è cambiato nelle ultime versioni.", + "refresh": "Aggiorna", + "noContent": "Nessuna modifica trovata. Riprova più tardi." } }, "units": { diff --git a/motomate/src/lib/i18n/locales/nl.json b/motomate/src/lib/i18n/locales/nl.json index aa15901..a3f4ceb 100644 --- a/motomate/src/lib/i18n/locales/nl.json +++ b/motomate/src/lib/i18n/locales/nl.json @@ -55,7 +55,10 @@ "passwordMismatch": "Wachtwoorden komen niet overeen", "submit": "Account maken", "hasAccount": "Heb je al een account?", - "login": "Inloggen" + "login": "Inloggen", + "closedTitle": "Registratie gesloten", + "closedBody": "Nieuwe accounts worden momenteel niet geaccepteerd. Neem contact op met uw beheerder voor toegang.", + "closedAction": "Ga naar inloggen" }, "magicLink": { "verifying": "Je wordt ingelogd…", @@ -927,7 +930,14 @@ "workflows": "Regels", "sourceCode": "Broncode", "reportIssue": "Probleem melden", - "tapAgain": "Tik nogmaals om te openen" + "tapAgain": "Tik nogmaals om te openen", + "changelog": "Bekijk changelog" + }, + "changelog": { + "title": "Wat is er nieuw?", + "subtitle": "Bekijk wat er veranderd is in de nieuwste versies.", + "refresh": "Vernieuwen", + "noContent": "Geen gewijzigen gevonden. Probeer het later opnieuw." } }, "units": { diff --git a/motomate/src/lib/i18n/locales/pt.json b/motomate/src/lib/i18n/locales/pt.json index e02e775..19b638c 100644 --- a/motomate/src/lib/i18n/locales/pt.json +++ b/motomate/src/lib/i18n/locales/pt.json @@ -55,7 +55,10 @@ "passwordMismatch": "As senhas não coincidem", "submit": "Criar conta", "hasAccount": "Já tem uma conta?", - "login": "Iniciar sessão" + "login": "Iniciar sessão", + "closedTitle": "Registo fechado", + "closedBody": "Novos registos não são aceites neste momento. Contacte o seu administrador para obter acesso.", + "closedAction": "Ir para o login" }, "magicLink": { "verifying": "A iniciar sessão…", @@ -927,7 +930,14 @@ "workflows": "Fluxos de trabalho", "sourceCode": "Codigo fonte", "reportIssue": "Reportar um problema", - "tapAgain": "Toque novamente para abrir" + "tapAgain": "Toque novamente para abrir", + "changelog": "Changelog" + }, + "changelog": { + "title": "O que há de novo?", + "subtitle": "Veja o que mudou nas versões mais recentes.", + "refresh": "Atualizar", + "noContent": "Nenhuma alteração encontrada. Tente novamente mais tarde." } }, "units": { From 78ff49496a34e9d9b66b8c950b428ee1ae05c9f4 Mon Sep 17 00:00:00 2001 From: hawkinslabdev <59891413+hawkinslabdev@users.noreply.github.com> Date: Mon, 18 May 2026 12:36:06 +0200 Subject: [PATCH 2/5] feat(changelog): add changelog page and update version display in settings --- .../src/routes/(app)/settings/+layout.svelte | 64 ++++++- .../(app)/settings/changelog/+page.svelte | 157 ++++++++++++++++++ 2 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 motomate/src/routes/(app)/settings/changelog/+page.svelte diff --git a/motomate/src/routes/(app)/settings/+layout.svelte b/motomate/src/routes/(app)/settings/+layout.svelte index 9c751dc..9929717 100644 --- a/motomate/src/routes/(app)/settings/+layout.svelte +++ b/motomate/src/routes/(app)/settings/+layout.svelte @@ -124,7 +124,12 @@ -
v{appVersion}
+ v{appVersion}
{#if children} @@ -210,11 +215,53 @@ transform: translateX(0); } .settings-nav-version { + position: relative; font-family: 'JetBrains Mono', monospace; font-size: var(--text-xs); color: var(--text-subtle); padding: 0.5rem 1rem; opacity: 0.7; + text-decoration: none; + display: block; + transition: + color 0.1s, + opacity 0.1s; + } + .settings-nav-version:hover { + color: var(--text-muted); + opacity: 1; + } + .settings-nav-version--active { + color: var(--accent); + opacity: 1; + } + .settings-nav-version::after { + content: attr(data-tooltip); + position: absolute; + top: calc(100% + 4px); + left: 1rem; + background: var(--bg-muted); + border: 1px solid var(--border-strong); + color: var(--text); + font-family: Inter, system-ui, sans-serif; + font-size: var(--text-xs); + font-weight: 500; + padding: 0.2rem 0.5rem; + border-radius: 6px; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: opacity 0.15s ease; + z-index: 10; + } + .settings-nav-version:hover::after, + .settings-nav-version:focus-visible::after { + opacity: 1; + } + @media (prefers-reduced-motion: reduce) { + .settings-nav-version::after { + transition: none; + } } .settings-nav-divider { height: 1px; @@ -266,6 +313,21 @@ transform: translateX(0); } .settings-nav-version { + border-left: none; + border-bottom: 2px solid transparent; + border-radius: 0; + white-space: nowrap; + padding: 0.625rem 0.875rem; + min-height: 44px; + display: flex; + align-items: center; + opacity: 1; + font-family: 'JetBrains Mono', monospace; + } + .settings-nav-version--active { + border-bottom-color: var(--accent); + } + .settings-nav-version::after { display: none; } } diff --git a/motomate/src/routes/(app)/settings/changelog/+page.svelte b/motomate/src/routes/(app)/settings/changelog/+page.svelte new file mode 100644 index 0000000..1900449 --- /dev/null +++ b/motomate/src/routes/(app)/settings/changelog/+page.svelte @@ -0,0 +1,157 @@ + + + + {#snippet children()} + + {/snippet} + + +{#if blocks.length === 0} + +{:else} +
+ {#each blocks as block} +
+

v{block.version}

+
    + {#each block.entries as entry} +
  • + {#if entry.category} + {entry.category} + {:else} + + {/if} + {#each parseSegments(entry.text) as seg}{#if seg.type === 'issue'}(#{seg.number}){:else}{seg.value}{/if}{/each} +
  • + {/each} +
+
+ {/each} +
+{/if} + + From bcc06e2db5fe4cebc2a7cbf075dc8cd590f0c3b5 Mon Sep 17 00:00:00 2001 From: hawkinslabdev <59891413+hawkinslabdev@users.noreply.github.com> Date: Mon, 18 May 2026 12:46:08 +0200 Subject: [PATCH 3/5] feat(auth): add AUTH_ALLOW_REGISTRATION flag --- README.md | 1 + motomate/.env.example | 1 + motomate/src/lib/auth/registration.ts | 7 + motomate/src/lib/db/repositories/users.ts | 5 + motomate/src/lib/i18n/locales/de.json | 3 +- motomate/src/lib/i18n/locales/en.json | 3 +- motomate/src/lib/i18n/locales/es.json | 3 +- motomate/src/lib/i18n/locales/fr.json | 3 +- motomate/src/lib/i18n/locales/it.json | 3 +- motomate/src/lib/i18n/locales/nl.json | 3 +- motomate/src/lib/i18n/locales/pt.json | 3 +- .../(app)/settings/changelog/+page.server.ts | 22 +++ .../src/routes/(auth)/login/+page.server.ts | 10 +- motomate/src/routes/(auth)/login/+page.svelte | 12 +- .../routes/(auth)/register/+page.server.ts | 8 +- .../src/routes/(auth)/register/+page.svelte | 175 ++++++++++-------- motomate/src/tests/registration-gate.test.ts | 46 +++++ 17 files changed, 212 insertions(+), 96 deletions(-) create mode 100644 motomate/src/lib/auth/registration.ts create mode 100644 motomate/src/routes/(app)/settings/changelog/+page.server.ts create mode 100644 motomate/src/tests/registration-gate.test.ts diff --git a/README.md b/README.md index 731c269..9f76abb 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ services: - PUBLIC_APP_ORIGINS=http://localhost - AUTH_COOKIE_SECURE=false - AUTH_SECRET=change-me-in-production-min-32-chars + - AUTH_ALLOW_REGISTRATION=true - STORAGE_ADAPTER=local - BODY_SIZE_LIMIT=20971520 restart: unless-stopped diff --git a/motomate/.env.example b/motomate/.env.example index d5d2201..2fcb40a 100644 --- a/motomate/.env.example +++ b/motomate/.env.example @@ -4,6 +4,7 @@ DATABASE_URL=./data/motomate.db # Auth AUTH_SECRET=change-me-in-production-min-32-chars AUTH_COOKIE_SECURE=false +AUTH_ALLOW_REGISTRATION=true # Storage: 'local' or 's3' STORAGE_ADAPTER=local diff --git a/motomate/src/lib/auth/registration.ts b/motomate/src/lib/auth/registration.ts new file mode 100644 index 0000000..c1b2339 --- /dev/null +++ b/motomate/src/lib/auth/registration.ts @@ -0,0 +1,7 @@ +import { env } from '$env/dynamic/private'; +import { hasAnyUser } from '$lib/db/repositories/users.js'; + +export async function isRegistrationOpen(): Promise { + if (env.AUTH_ALLOW_REGISTRATION !== 'false') return true; + return !(await hasAnyUser()); +} diff --git a/motomate/src/lib/db/repositories/users.ts b/motomate/src/lib/db/repositories/users.ts index 19c0aa4..fc569fa 100644 --- a/motomate/src/lib/db/repositories/users.ts +++ b/motomate/src/lib/db/repositories/users.ts @@ -72,6 +72,11 @@ export async function markOnboardingDone(userId: string): Promise { .where(eq(users.id, userId)); } +export async function hasAnyUser(): Promise { + const row = await db.query.users.findFirst(); + return row !== undefined; +} + export async function deleteUser(userId: string): Promise { await db.delete(users).where(eq(users.id, userId)); } diff --git a/motomate/src/lib/i18n/locales/de.json b/motomate/src/lib/i18n/locales/de.json index 45f3263..6f3e15d 100644 --- a/motomate/src/lib/i18n/locales/de.json +++ b/motomate/src/lib/i18n/locales/de.json @@ -42,7 +42,8 @@ "rateLimited": "Zu viele Versuche. Bitte versuche es später erneut.", "invalidFormat": "Ungültiges E-Mail- oder Passwortformat", "invalidCredentials": "Ungültige E-Mail oder Passwort", - "invalidEmail": "Bitte gib eine gültige E-Mail-Adresse ein" + "invalidEmail": "Bitte gib eine gültige E-Mail-Adresse ein", + "registrationClosed": "Die Registrierung ist geschlossen. Wenden Sie sich an Ihren Administrator." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/en.json b/motomate/src/lib/i18n/locales/en.json index 9727e2c..fa2dbda 100644 --- a/motomate/src/lib/i18n/locales/en.json +++ b/motomate/src/lib/i18n/locales/en.json @@ -42,7 +42,8 @@ "rateLimited": "Too many attempts. Please try again later.", "invalidFormat": "Invalid email or password format", "invalidCredentials": "Invalid email or password", - "invalidEmail": "Please enter a valid email address" + "invalidEmail": "Please enter a valid email address", + "registrationClosed": "Unfortunately, you can't sign up right now. Contact your administrator for access." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/es.json b/motomate/src/lib/i18n/locales/es.json index ae99d29..94544af 100644 --- a/motomate/src/lib/i18n/locales/es.json +++ b/motomate/src/lib/i18n/locales/es.json @@ -42,7 +42,8 @@ "rateLimited": "Demasiados intentos. Por favor, inténtalo de nuevo más tarde.", "invalidFormat": "Formato de correo electrónico o contraseña inválido", "invalidCredentials": "Correo electrónico o contraseña inválidos", - "invalidEmail": "Por favor, introduce una dirección de correo electrónico válida" + "invalidEmail": "Por favor, introduce una dirección de correo electrónico válida", + "registrationClosed": "El registro está cerrado. Contacta con tu administrador para obtener acceso." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/fr.json b/motomate/src/lib/i18n/locales/fr.json index d181f98..8e22564 100644 --- a/motomate/src/lib/i18n/locales/fr.json +++ b/motomate/src/lib/i18n/locales/fr.json @@ -42,7 +42,8 @@ "rateLimited": "Trop de tentatives. Veuillez réessayer plus tard.", "invalidFormat": "Format d'e-mail ou de mot de passe invalide", "invalidCredentials": "E-mail ou mot de passe invalide", - "invalidEmail": "Veuillez entrer une adresse e-mail valide" + "invalidEmail": "Veuillez entrer une adresse e-mail valide", + "registrationClosed": "L'inscription est fermée. Contactez votre administrateur pour obtenir l'accès." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/it.json b/motomate/src/lib/i18n/locales/it.json index 659d169..1688691 100644 --- a/motomate/src/lib/i18n/locales/it.json +++ b/motomate/src/lib/i18n/locales/it.json @@ -42,7 +42,8 @@ "rateLimited": "Troppi tentativi. Riprova più tardi.", "invalidFormat": "Formato email o password non valido", "invalidCredentials": "Email o password non validi", - "invalidEmail": "Inserisci un indirizzo email valido" + "invalidEmail": "Inserisci un indirizzo email valido", + "registrationClosed": "La registrazione è chiusa. Contatta il tuo amministratore per l'accesso." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/nl.json b/motomate/src/lib/i18n/locales/nl.json index a3f4ceb..0e9fd0b 100644 --- a/motomate/src/lib/i18n/locales/nl.json +++ b/motomate/src/lib/i18n/locales/nl.json @@ -42,7 +42,8 @@ "rateLimited": "Te veel pogingen. Probeer het later opnieuw.", "invalidFormat": "Ongeldig e-mailadres of wachtwoordformaat", "invalidCredentials": "Ongeldig e-mailadres of wachtwoord", - "invalidEmail": "Voer een geldig e-mailadres in" + "invalidEmail": "Voer een geldig e-mailadres in", + "registrationClosed": "Aanmelden is nu niet mogelijk. Neem contact op met uw beheerder voor toegang." } }, "register": { diff --git a/motomate/src/lib/i18n/locales/pt.json b/motomate/src/lib/i18n/locales/pt.json index 19b638c..f92f524 100644 --- a/motomate/src/lib/i18n/locales/pt.json +++ b/motomate/src/lib/i18n/locales/pt.json @@ -42,7 +42,8 @@ "rateLimited": "Demasiadas tentativas. Por favor, tenta novamente mais tarde.", "invalidFormat": "Formato de e-mail ou palavra-passe inválido", "invalidCredentials": "E-mail ou palavra-passe inválidos", - "invalidEmail": "Por favor, introduz um endereço de e-mail válido" + "invalidEmail": "Por favor, introduz um endereço de e-mail válido", + "registrationClosed": "O registo está fechado. Contacte o seu administrador para obter acesso." } }, "register": { diff --git a/motomate/src/routes/(app)/settings/changelog/+page.server.ts b/motomate/src/routes/(app)/settings/changelog/+page.server.ts new file mode 100644 index 0000000..fa2687d --- /dev/null +++ b/motomate/src/routes/(app)/settings/changelog/+page.server.ts @@ -0,0 +1,22 @@ +import type { PageServerLoad } from './$types'; + +const CHANGELOG_URL = 'https://raw.githubusercontent.com/hawkinslabdev/motomate/main/CHANGELOG.md'; +const TTL = 3_600_000; + +let cache: { raw: string; at: number } | null = null; + +export const load: PageServerLoad = async ({ url }) => { + const refresh = url.searchParams.has('refresh'); + if (!refresh && cache && Date.now() - cache.at < TTL) { + return { raw: cache.raw }; + } + try { + const res = await fetch(CHANGELOG_URL, { signal: AbortSignal.timeout(8000) }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const raw = await res.text(); + cache = { raw, at: Date.now() }; + return { raw }; + } catch { + return { raw: cache?.raw ?? null }; + } +}; diff --git a/motomate/src/routes/(auth)/login/+page.server.ts b/motomate/src/routes/(auth)/login/+page.server.ts index 7453218..6b2bf56 100644 --- a/motomate/src/routes/(auth)/login/+page.server.ts +++ b/motomate/src/routes/(auth)/login/+page.server.ts @@ -1,6 +1,7 @@ import { fail, redirect } from '@sveltejs/kit'; import { lucia } from '$lib/auth/index.js'; import { getUserByEmail, createUser, updateUserSettings } from '$lib/db/repositories/users.js'; +import { isRegistrationOpen } from '$lib/auth/registration.js'; import { createMagicLinkToken, sendMagicLinkEmail } from '$lib/auth/magic-link.js'; import { LoginSchema, MagicLinkRequestSchema } from '$lib/validators/schemas.js'; import { rateLimit } from '$lib/auth/rate-limit.js'; @@ -22,6 +23,7 @@ type AuthErrors = { invalidFormat: string; invalidCredentials: string; invalidEmail: string; + registrationClosed: string; }; }; }; @@ -31,8 +33,7 @@ const localeMessages: Record = { en, de, fr, es, it, nl, pt const ARGON2_OPTS = { memoryCost: 19456, timeCost: 2, outputLen: 32, parallelism: 1 }; -// Uses a pre-computed hash to ensure every login attempt takes the same amount of time. -// This prevents abusers from using response speeds to guess if an email address exists in the database. +// Uses a pre-computed hash to ensure every login attempt takes same amount of time let _dummyHash: string | undefined; async function getDummyHash(): Promise { if (!_dummyHash) _dummyHash = await hash('_timing_dummy_', ARGON2_OPTS); @@ -41,7 +42,7 @@ async function getDummyHash(): Promise { export const load: PageServerLoad = async ({ locals }) => { if (locals.user) redirect(302, '/dashboard'); - return {}; + return { registrationEnabled: await isRegistrationOpen() }; }; export const actions: Actions = { @@ -130,6 +131,9 @@ export const actions: Actions = { // Find or create user (passwordless) let user = await getUserByEmail(parsed.data.email); if (!user) { + if (!(await isRegistrationOpen())) { + return fail(400, { error: errors.registrationClosed }); + } user = await createUser({ email: parsed.data.email }); } diff --git a/motomate/src/routes/(auth)/login/+page.svelte b/motomate/src/routes/(auth)/login/+page.svelte index 09ca4f9..2e590df 100644 --- a/motomate/src/routes/(auth)/login/+page.svelte +++ b/motomate/src/routes/(auth)/login/+page.svelte @@ -2,8 +2,8 @@ import { enhance } from '$app/forms'; import { _ } from '$lib/i18n'; - let { form } = $props<{ - data: Record; + let { data, form } = $props<{ + data: { registrationEnabled: boolean }; form: { error?: string; email?: string; @@ -146,9 +146,11 @@ {/if} - + {#if data.registrationEnabled} + + {/if} {/if}