1import api from '../api-client.js'; 2import { 3 saveAuthData, 4 clearAuthData, 5 saveRefreshToken, 6} from '../auth-storage.js'; 7import { ensureDeviceRegistered } from '../device-fp.js'; 8import { updateUserProfile } from '../db/repositories/user.repository.js'; 9 10document.addEventListener('alpine:init', () => { 11 Alpine.data('auth', () => ({ 12 email: '', 13 password: '', 14 loading: false, 15 error: null, 16 successMessage: null, 17 18 init() { 19 }, 20 21 async handleSocialLogin(provider) { 22 this.loading = true; 23 this.error = null; 24 try { 25 let result; 26 27 if (provider === 'google') result = await window.signInWithGoogle(); 28 else if (provider === 'facebook') 29 result = await window.signInWithFacebook(); 30 else if (provider === 'apple') result = await window.signInWithApple(); 31 32 const token = await result.user.getIdToken(); 33 await this.signTokenToBackend(token); 34 } catch (err) { 35 this.error = `${provider} Sign In Failed`; 36 console.error(err); 37 } finally { 38 this.loading = false; 39 } 40 }, 41 42 async handleEmailLogin(e) { 43 e.preventDefault(); 44 this.loading = true; 45 this.error = null; 46 this.successMessage = null; 47 48 // Check for demo accounts 49 if ( 50 this.email === '[email protected]' || 51 this.email === '[email protected]' 52 ) { 53 await this.signTokenToBackend(this.email); 54 return; 55 } 56 57 try { 58 const result = await window.loginWithEmailLink(this.email); 59 this.successMessage = result.message; 60 } catch (err) { 61 this.error = err.message || 'Failed to send login link'; 62 } finally { 63 this.loading = false; 64 } 65 }, 66 67 async handleEmailSignUp(e) { 68 e.preventDefault(); 69 this.loading = true; 70 this.error = null; 71 this.successMessage = null; 72 73 try { 74 const result = await window.signUpWithEmailLink(this.email); 75 this.successMessage = result.message; 76 } catch (err) { 77 this.error = err.message || 'Failed to send sign up link'; 78 } finally { 79 this.loading = false; 80 } 81 }, 82 83 async signTokenToBackend(token) { 84 try { 85 const response = await api.post('/api/auth/login', { token }); 86 87 if (response.status === 200 || response.status === 201) { 88 const { user, access_token, refresh_token } = response.data.data; 89 90 // 1. Save data to IDB and Cookies 91 await saveAuthData(user, access_token); 92 93 if (refresh_token) { 94 await saveRefreshToken(refresh_token); 95 } 96 97 // 2. Register device 98 try { 99 await ensureDeviceRegistered(access_token); 100 } catch (deviceErr) { 101 console.error('Device registration failed:', deviceErr); 102 // We continue anyway, as login was successful 103 } 104 105 // 3. Update global store 106 Alpine.store('app').user = user; 107 108 // 4. Redirect 109 const urlParams = new URLSearchParams(window.location.search); 110 const redirectUrl = urlParams.get('redirect') || '/profile'; 111 window.location.href = redirectUrl; 112 } 113 } catch (err) { 114 // No err.response = the failure is client-side (IndexedDB, interceptor, 115 // post-login code) â show the real error instead of a generic message. 116 this.error = 117 err.response?.data?.message || err.message || 'Authentication failed on server'; 118 console.error('Sign token to backend error:', err, 'response:', err.response); 119 } 120 }, 121 122 async handleLogout() { 123 try { 124 await clearAuthData(); 125 Alpine.store('app').user = null; 126 window.location.href = '/login'; 127 } catch (err) { 128 console.error('Logout failed:', err); 129 } 130 }, 131 })); 132});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.