import { showToast } from '../components/toast.js'; import { t } from '../i18n.js'; let authConfig = null; async function loadAuthConfig() { if (authConfig) return authConfig; const res = await fetch('/api/auth/config'); authConfig = await res.json(); return authConfig; } export async function render(container) { const config = await loadAuthConfig(); const isSetup = config.needsSetup; // registration_enabled may be absent on older servers — treat as enabled for back-compat const canRegister = config.registration_enabled !== false; container.innerHTML = `

ScreenTinker

${isSetup ? t('auth.subtitle_setup') : t('auth.subtitle_signin')}

${!isSetup && canRegister ? `

${t('auth.trial_notice')}

` : ''}
${isSetup ? `
` : ''} ${!isSetup && canRegister ? ` ` : ''}
${config.googleEnabled || config.microsoftEnabled ? `

${t('auth.divider_or')}
` : ''} ${config.googleEnabled ? `
` : ''} ${config.microsoftEnabled ? ` ` : ''}
${t('auth.support_access')}

${t('auth.terms')}  ·  ${t('auth.privacy')}

`; setupHandlers(config, isSetup); } function setupHandlers(config, isSetup) { const showError = (msg) => { const el = document.getElementById('loginError'); el.textContent = msg; el.style.display = 'block'; }; // Support token login document.getElementById('supportLoginBtn')?.addEventListener('click', async () => { const token = document.getElementById('supportToken')?.value.trim(); if (!token) { showError(t('auth.error_paste_support_token')); return; } try { const res = await fetch('/api/auth/support', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) }); const data = await res.json(); if (!res.ok) { showError(data.error); return; } onAuthSuccess(data); } catch (err) { showError(t('auth.error_support_failed')); } }); // Local login/register if (isSetup) { document.getElementById('loginBtn')?.addEventListener('click', () => doRegister(true)); } else { document.getElementById('loginBtn')?.addEventListener('click', doLogin); document.getElementById('showRegisterBtn')?.addEventListener('click', () => { document.getElementById('localAuthForm').style.display = 'none'; document.getElementById('registerForm').style.display = 'block'; }); document.getElementById('showLoginBtn')?.addEventListener('click', () => { document.getElementById('localAuthForm').style.display = 'block'; document.getElementById('registerForm').style.display = 'none'; }); document.getElementById('registerBtn')?.addEventListener('click', () => doRegister(false)); } // Enter key on password field document.getElementById('loginPassword')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') isSetup ? doRegister(true) : doLogin(); }); async function doLogin() { const email = document.getElementById('loginEmail').value.trim(); const password = document.getElementById('loginPassword').value; if (!email || !password) { showError(t('auth.error_email_password_required')); return; } try { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await res.json(); if (!res.ok) { showError(data.error); return; } onAuthSuccess(data); } catch (err) { showError(t('auth.error_login_failed')); } } async function doRegister(isFirstUser) { const email = document.getElementById(isFirstUser ? 'loginEmail' : 'regEmail').value.trim(); const password = document.getElementById(isFirstUser ? 'loginPassword' : 'regPassword').value; const name = document.getElementById(isFirstUser ? 'loginName' : 'regName')?.value.trim() || ''; if (!email || !password) { showError(t('auth.error_email_password_required')); return; } if (password.length < 6) { showError(t('auth.error_password_min_6')); return; } try { const res = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password, name }) }); const data = await res.json(); if (!res.ok) { showError(data.error); return; } onAuthSuccess(data); } catch (err) { showError(t('auth.error_registration_failed')); } } // Google Sign-In if (config.googleEnabled) { document.getElementById('googleSignInBtn')?.addEventListener('click', async () => { try { // Use Google's popup-based sign in const client = google.accounts.oauth2.initTokenClient({ client_id: config.googleClientId, scope: 'email profile', callback: async (response) => { if (response.access_token) { // Get ID token via Google's tokeninfo const tokenRes = await fetch(`https://oauth2.googleapis.com/tokeninfo?access_token=${response.access_token}`); const tokenData = await tokenRes.json(); // Send to our server const res = await fetch('/api/auth/google', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credential: response.access_token, email: tokenData.email }) }); const data = await res.json(); if (res.ok) onAuthSuccess(data); else showError(data.error); } } }); client.requestAccessToken(); } catch (err) { showError(t('auth.error_google_failed')); } }); } // Microsoft Sign-In if (config.microsoftEnabled) { document.getElementById('microsoftSignInBtn')?.addEventListener('click', async () => { try { const msalConfig = { auth: { clientId: config.microsoftClientId, authority: `https://login.microsoftonline.com/${config.microsoftTenantId}`, redirectUri: window.location.origin } }; const msalInstance = new msal.PublicClientApplication(msalConfig); await msalInstance.initialize(); const loginResponse = await msalInstance.loginPopup({ scopes: ['User.Read'] }); if (loginResponse.accessToken) { const res = await fetch('/api/auth/microsoft', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ access_token: loginResponse.accessToken }) }); const data = await res.json(); if (res.ok) onAuthSuccess(data); else showError(data.error); } } catch (err) { showError(t('auth.error_microsoft_failed')); } }); } } function onAuthSuccess(data) { localStorage.setItem('token', data.token); localStorage.setItem('user', JSON.stringify(data.user)); window.location.hash = '#/'; window.location.reload(); } export function cleanup() {}