diff --git a/js/components/auth-modal.js b/js/components/auth-modal.js index 1cad60c..9697f46 100644 --- a/js/components/auth-modal.js +++ b/js/components/auth-modal.js @@ -110,6 +110,14 @@ class AuthModal extends HTMLElement { ` : ''} +
+ +

${t('auth.rememberMeHint')}

+
+ @@ -247,6 +255,9 @@ class AuthModal extends HTMLElement { this.error = null this.render() + const rememberMe = this.querySelector('#remember-me')?.checked || false + auth.setRememberMe(rememberMe) + const result = await auth.login(uuid) this.loading = false @@ -469,6 +480,26 @@ style.textContent = /* css */` auth-modal .link-btn:hover { color: var(--color-accent-hover); } + + auth-modal .checkbox-label { + display: flex; + align-items: center; + gap: var(--space-sm); + cursor: pointer; + font-size: var(--font-size-sm); + } + + auth-modal .checkbox-label input { + width: 16px; + height: 16px; + accent-color: var(--color-accent); + } + + auth-modal .field-hint { + font-size: var(--font-size-xs); + color: var(--color-text-muted); + margin-top: var(--space-xs); + } ` document.head.appendChild(style) diff --git a/js/services/auth.js b/js/services/auth.js index 4132dcd..b351e1e 100644 --- a/js/services/auth.js +++ b/js/services/auth.js @@ -7,6 +7,7 @@ */ import { directus } from './directus.js' +import { setPersist, getPersist } from './directus/client.js' import { i18n } from '../i18n.js' const AUTH_DOMAIN = 'dgray.io' @@ -16,6 +17,9 @@ class AuthService { this.currentUser = null this.listeners = new Set() this.hashCache = new Map() + if (localStorage.getItem('dgray_remember') === '1') { + setPersist(true) + } } /** @@ -136,6 +140,8 @@ class AuthService { this.currentUser = null this.clearStoredUuid() + localStorage.removeItem('dgray_remember') + setPersist(false) this.resetPreferencesToDefaults() this.notifyListeners() } @@ -227,7 +233,8 @@ class AuthService { * @param {string} uuid */ storeUuid(uuid) { - localStorage.setItem('dgray_uuid', uuid) + const storage = getPersist() ? localStorage : sessionStorage + storage.setItem('dgray_uuid', uuid) } /** @@ -235,16 +242,30 @@ class AuthService { * @returns {string|null} */ getStoredUuid() { - return localStorage.getItem('dgray_uuid') + return sessionStorage.getItem('dgray_uuid') || localStorage.getItem('dgray_uuid') } /** * Clears stored UUID */ clearStoredUuid() { + sessionStorage.removeItem('dgray_uuid') localStorage.removeItem('dgray_uuid') } + setRememberMe(value) { + setPersist(value) + if (value) { + localStorage.setItem('dgray_remember', '1') + } else { + localStorage.removeItem('dgray_remember') + } + } + + getRememberMe() { + return localStorage.getItem('dgray_remember') === '1' + } + /** * Subscribe to auth state changes * @param {Function} callback diff --git a/js/services/directus/client.js b/js/services/directus/client.js index 0fbd568..df20f09 100644 --- a/js/services/directus/client.js +++ b/js/services/directus/client.js @@ -1,5 +1,19 @@ const DIRECTUS_URL = 'https://api.dgray.io' +let _persist = false + +export function setPersist(value) { + _persist = value +} + +export function getPersist() { + return _persist +} + +function _storage() { + return _persist ? localStorage : sessionStorage +} + class DirectusError extends Error { constructor(status, message, data = {}) { super(message) @@ -37,13 +51,16 @@ class DirectusClient { // ── Token Management ── loadTokens() { - const stored = localStorage.getItem('dgray_auth') + const stored = sessionStorage.getItem('dgray_auth') || localStorage.getItem('dgray_auth') if (stored) { try { const { accessToken, refreshToken, expiry } = JSON.parse(stored) this.accessToken = accessToken this.refreshToken = refreshToken this.tokenExpiry = expiry + if (localStorage.getItem('dgray_auth')) { + _persist = true + } this.scheduleTokenRefresh() } catch (e) { this.clearTokens() @@ -56,7 +73,7 @@ class DirectusClient { this.refreshToken = refreshToken this.tokenExpiry = Date.now() + (expiresIn * 1000) - localStorage.setItem('dgray_auth', JSON.stringify({ + _storage().setItem('dgray_auth', JSON.stringify({ accessToken: this.accessToken, refreshToken: this.refreshToken, expiry: this.tokenExpiry @@ -69,6 +86,7 @@ class DirectusClient { this.accessToken = null this.refreshToken = null this.tokenExpiry = null + sessionStorage.removeItem('dgray_auth') localStorage.removeItem('dgray_auth') if (this.refreshTimeout) { diff --git a/locales/de.json b/locales/de.json index 2e7dd25..2d076d2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -198,7 +198,9 @@ "downloadBackup": "Backup herunterladen", "confirmSaved": "Ich habe meine UUID gespeichert", "registrationFailed": "Registrierung fehlgeschlagen", - "loginRequired": "Bitte melde dich an, um fortzufahren" + "loginRequired": "Bitte melde dich an, um fortzufahren", + "rememberMe": "Auf diesem Gerät merken", + "rememberMeHint": "Deine UUID wird lokal gespeichert. Nur aktivieren auf vertrauenswürdigen Geräten." }, "favorites": { "title": "Favoriten", diff --git a/locales/en.json b/locales/en.json index a22c774..1a91a88 100644 --- a/locales/en.json +++ b/locales/en.json @@ -198,7 +198,9 @@ "downloadBackup": "Download Backup", "confirmSaved": "I have saved my UUID", "registrationFailed": "Registration failed", - "loginRequired": "Please log in to continue" + "loginRequired": "Please log in to continue", + "rememberMe": "Remember me on this device", + "rememberMeHint": "Your UUID will be stored locally. Only enable on trusted devices." }, "favorites": { "title": "Favorites", diff --git a/locales/es.json b/locales/es.json index de4f31c..861117e 100644 --- a/locales/es.json +++ b/locales/es.json @@ -198,7 +198,9 @@ "downloadBackup": "Descargar copia de seguridad", "confirmSaved": "He guardado mi UUID", "registrationFailed": "Error en el registro", - "loginRequired": "Inicia sesión para continuar" + "loginRequired": "Inicia sesión para continuar", + "rememberMe": "Recordarme en este dispositivo", + "rememberMeHint": "Tu UUID se guardará localmente. Actívalo solo en dispositivos de confianza." }, "favorites": { "title": "Favoritos", diff --git a/locales/fr.json b/locales/fr.json index 46bf54d..c0bf23b 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -198,7 +198,9 @@ "downloadBackup": "Télécharger la sauvegarde", "confirmSaved": "J'ai sauvegardé mon UUID", "registrationFailed": "Échec de l'inscription", - "loginRequired": "Veuillez vous connecter pour continuer" + "loginRequired": "Veuillez vous connecter pour continuer", + "rememberMe": "Se souvenir de moi", + "rememberMeHint": "Votre UUID sera stocké localement. N'activez que sur des appareils de confiance." }, "favorites": { "title": "Favoris", diff --git a/locales/it.json b/locales/it.json index 07bb6a3..4bb9006 100644 --- a/locales/it.json +++ b/locales/it.json @@ -198,7 +198,9 @@ "downloadBackup": "Scarica backup", "confirmSaved": "Ho salvato il mio UUID", "registrationFailed": "Registrazione fallita", - "loginRequired": "Accedi per continuare" + "loginRequired": "Accedi per continuare", + "rememberMe": "Ricordami su questo dispositivo", + "rememberMeHint": "Il tuo UUID verrà salvato localmente. Attiva solo su dispositivi affidabili." }, "favorites": { "title": "Preferiti", diff --git a/locales/pt.json b/locales/pt.json index 0cc0c45..6110bd0 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -198,7 +198,9 @@ "downloadBackup": "Baixar Backup", "confirmSaved": "Eu salvei meu UUID", "registrationFailed": "Falha no cadastro", - "loginRequired": "Por favor, faça login para continuar" + "loginRequired": "Por favor, faça login para continuar", + "rememberMe": "Lembrar neste dispositivo", + "rememberMeHint": "Seu UUID será armazenado localmente. Ative apenas em dispositivos confiáveis." }, "favorites": { "title": "Favoritos", diff --git a/locales/ru.json b/locales/ru.json index 927dd4a..a80810e 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -198,7 +198,9 @@ "downloadBackup": "Скачать резервную копию", "confirmSaved": "Я сохранил свой UUID", "registrationFailed": "Регистрация не удалась", - "loginRequired": "Войдите, чтобы продолжить" + "loginRequired": "Войдите, чтобы продолжить", + "rememberMe": "Запомнить на этом устройстве", + "rememberMeHint": "Ваш UUID будет сохранён локально. Включайте только на доверенных устройствах." }, "favorites": { "title": "Избранное",