1/** 2 * Login Page 3 */ 4// A saved browser token may be expired or revoked. Keep sign-in reachable; 5// only a successful authentication response selects the fixed return destination. 6 7// Standalone repair for the delivered legacy login DOM and session helpers. 8// Role destinations below preserve the delivered script's behavior. 9function loginBrowserDevice() { 10 let device = getUserDevice(); 11 if (!device) { 12 device = window.crypto.randomUUID(); 13 saveUserDevice(device); 14 } 15 return device; 16} 17const loginRequestTimeout = 15000; 18let loginInProgress = false; 19 20function loginSucceeded(data) { 21 return data && (data.status === 1 || data.status === '1'); 22} 23 24function loginIdentity(user) { 25 if (!user || typeof user !== 'object' || Array.isArray(user)) return null; 26 const id = user.id; 27 return (typeof id === 'string' && id.trim() !== '') || (typeof id === 'number' && Number.isFinite(id)) ? String(id) : null; 28} 29 30function loginToken(token) { 31 return typeof token === 'string' && !/\s/.test(token) && token.length > 0 && token.length <= 8192 && !['null', 'undefined'].includes(token); 32} 33 34const loginStorageMessage = 'Your browser could not save the sign-in session. Allow site storage for RSS and try again.'; 35function loginStorageError() { 36 const error = new Error('Browser storage unavailable'); 37 error.name = 'LoginStorageError'; 38 return error; 39} 40function persistLoginSession(user, token) {
41 let credentialCleared = false; 42 try { 43 // localStorage has no multi-key transaction. Remove the old credential 44 // before changing identity so a failed write cannot mix two accounts. 45 localStorage.removeItem('token'); 46 credentialCleared = true; 47 saveUserData(user); 48 saveToken(token); 49 } catch (_) { 50 if (credentialCleared) { 51 try { localStorage.removeItem('token'); } catch (_) { /* Best-effort cleanup. */ } 52 try { localStorage.removeItem('userData'); } catch (_) { /* Best-effort cleanup. */ } 53 } 54 throw loginStorageError(); 55 } 56} 57 58function showLoginError(message) { 59 const error = document.getElementById('err_msg'); 60 error.textContent = message; 61 error.style.display = 'block'; 62 document.getElementById('success_msg').style.display = 'none'; 63} 64 65function loginFailureMessage(response, data) { 66 if (data?.code === 'security_check_failed') return 'The security check could not be verified. Complete it again and retry.'; 67 if (response.status === 429) return 'Too many sign-in attempts. Please wait a few minutes and try again.'; 68 if (response.status === 401 || response.status === 403) return 'Unable to sign in. Check your email, password and account type.'; 69 return 'Unable to sign in right now. Please try again.'; 70} 71 72async function loginRequest(url, options) { 73 const controller = new AbortController(); 74 const timeout = setTimeout(() => controller.abort(), loginRequestTimeout); 75 try { 76 const response = await fetch(url, { ...options, signal: controller.signal, cache: 'no-store' }); 77 const data = await response.json().catch(error => { 78 if (error?.name === 'AbortError') throw error; 79 return null; 80 }); 81 return { response, data }; 82 } finally { 83 clearTimeout(timeout); 84 } 85} 86 87// The provider handoff is not a completed browser session until the authenticated 88// self-read succeeds and returns exactly the identity supplied by the handoff. 89async function completeGoogleLogin() { 90 const button = login_frm.querySelector('button[type="submit"]'); 91 loginInProgress = true; 92 button.disabled = true; 93 let signedIn = false; 94 try { 95 if (!loginToken(gtoken) || loginIdentity({ id: guserdataid }) === null) throw new Error('Invalid handoff'); 96 const userId = String(guserdataid); 97 const { response, data } = await loginRequest(apiurl + 'users/enduser/' + encodeURIComponent(userId) + '/?find_by=id', { 98 method: 'GET', 99 headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${gtoken}` } 100 }); 101 const user = Array.isArray(data?.data) && data.data.length === 1 ? data.data[0] : null; 102 if (!response.ok || !loginSucceeded(data) || loginIdentity(user) !== userId) throw new Error('Invalid handoff'); 103 const destination = (Number(user.switch) === 3 ? '/dashboard' : '/'); 104 persistLoginSession(user, gtoken); 105 signedIn = true; 106 document.getElementById('success_msg').textContent = 'Logged In Successfully'; 107 document.getElementById('success_msg').style.display = 'block'; 108 // Optional device enrollment must not block an authenticated session or 109 // leave an unhandled rejection if the notification endpoint is unavailable. 110 try { 111 void loginRequest(apiurl + 'authentication/user-device/' + encodeURIComponent(userId), { 112 method: 'POST', 113 headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${gtoken}` }, 114 body: JSON.stringify({ device_id: loginBrowserDevice(), device_type: 'Web', auth_token: gtoken }) 115 }).catch(() => {}); 116 } catch (_) { /* Device storage can be unavailable in restricted browsers. */ } 117 setTimeout(() => { window.location.href = destination; }, 500); 118 } catch (error) { 119 showLoginError(error?.name === 'LoginStorageError' ? loginStorageMessage : 'Unable to complete Google sign-in. Please try again or sign in with your email and password.'); 120 } finally { 121 if (!signedIn) { 122 loginInProgress = false;
123 button.disabled = false; 124 } 125 } 126} 127 128const login_frm = document.getElementById('login_frm'); 129const passwordToggle = document.getElementById('login-password-toggle'); 130passwordToggle?.addEventListener('click', () => { 131 const passwordInput = document.getElementById('password-modern'); 132 const visible = passwordInput.type === 'password'; 133 passwordInput.type = visible ? 'text' : 'password'; 134 passwordToggle.textContent = visible ? 'Hide password' : 'Show password'; 135 passwordToggle.setAttribute('aria-pressed', String(visible)); 136}); 137 138login_frm.addEventListener('submit', async (e) => { 139 e.preventDefault();e.stopPropagation(); 140 if (loginInProgress) return; 141 document.querySelectorAll('.show_msg').forEach(div => { 142 div.style.display = 'none'; 143 }); 144 const user_type = document.querySelector('input[name="login_type"]:checked')?.value; 145 const email = document.querySelector(".email").value.trim(); 146 const password = document.querySelector(".password").value; 147 const web_id = document.querySelector(".device_id").value; 148 let device_id; 149 const device_type = 'Web'; 150 const turnstileToken = document.querySelector('[name="cf-turnstile-response"]')?.value.trim() || ''; 151 const status = document.getElementById('turnstile_status'); 152 const submitButton = login_frm.querySelector('button[type="submit"]'); 153 154 if (!turnstileToken) { 155 if (status) status.textContent = 'Complete the security check before continuing.'; 156 return; 157 } 158 159 loginInProgress = true; 160 submitButton.disabled = true; 161 let signedIn = false; 162 163 try { 164 try { device_id = loginBrowserDevice(); } catch (_) { throw loginStorageError(); } 165 const { response, data } = await loginRequest(apiurl+'authentication/', { 166 method: 'POST', 167 headers: { 168 'Content-Type': 'application/json' 169 }, 170 body: JSON.stringify({ 171 user_type,email,password,web_id,device_id,device_type, 172 turnstile_token: turnstileToken 173 }) 174 }); 175 if (!response.ok || !loginSucceeded(data) || !loginToken(data.access_token) || loginIdentity(data.data) === null) { 176 showLoginError(loginFailureMessage(response, data)); 177 }else{ 178 const destination = (Number(data.data.switch) === 5 ? '/vendor-dashboard' : Number(data.data.switch) === 3 ? '/dashboard' : '/'); 179 persistLoginSession(data.data, data.access_token); 180 signedIn = true; 181 try { localStorage.removeItem('fcmId'); deleteUserDevice(); } catch (_) { /* Optional device cleanup. */ } 182 document.getElementById("success_msg").textContent="Logged In Successfully"; 183 document.getElementById('success_msg').style.display = 'block'; 184 setTimeout(function() { 185 window.location.href = destination; 186 }, 500); 187 } 188 } catch (error) { 189 showLoginError(error?.name === 'LoginStorageError' ? loginStorageMessage : error?.name === 'AbortError' ? 'Sign-in took too long. Please try again.' : 'Unable to sign in. Please try again.'); 190 } finally { 191 if (!signedIn) { 192 loginInProgress = false; 193 submitButton.disabled = false; 194 if (window.rssResetTurnstile) window.rssResetTurnstile(); 195 } 196 } 197}); 198 199if (typeof gtoken !== 'undefined' && gtoken && gtoken !== 'null') void completeGoogleLogin(); 200 201// Only fixed callback categories are allowed in the URL; no provider text or token 202// is interpreted here. Remove this parameter without discarding other URL state. 203(() => { 204 const url = new URL(window.location.href); 205 const notices = { 206 challenge_expired: 'The security check expired. Please try again.', 207 google_cancelled: 'Google sign-in was not completed. Please try again.', 208 unavailable: 'Sign-in is temporarily unavailable. Please try again shortly.', 209 rate_limited: 'Too many sign-in attempts. Please wait before trying again.', 210 security_check: 'The security check could not be verified. Please complete it again.', 211 google_failed: 'Google sign-in could not be completed. Try your RSS email and password, or use password recovery.', 212 account_exists: 'An account with this email already exists. Sign in with your RSS email and password.', 213 verify_email: 'Check your email to verify the account before signing in.' 214 }; 215 const values = url.searchParams.getAll('login_notice'); 216 if (values.length === 1 && Object.prototype.hasOwnProperty.call(notices, values[0])) { 217 if (values[0] === 'verify_email') { 218 const success = document.getElementById('success_msg'); 219 success.textContent = notices.verify_email; 220 success.style.display = 'block'; 221 } else showLoginError(notices[values[0]]); 222 } 223 if (values.length) { 224 url.searchParams.delete('login_notice'); 225 window.history.replaceState(window.history.state, '', url.pathname + url.search + url.hash); 226 } 227 if (!document.getElementById('rss-admin-login-link')) { 228 const line = document.createElement('p'); 229 line.className = 'col-12'; 230 line.append('Administrator? '); 231 const link = document.createElement('a'); 232 link.id = 'rss-admin-login-link';
233 link.href = 'https://admin.realservicesolutions.com/'; 234 link.textContent = 'Sign in to RSS administration'; 235 line.append(link); 236 login_frm.append(line); 237 } 238 // Historical provider-ID password forms cannot prove account ownership. 239 // Keep any delivered form inert and direct recovery through the existing page. 240 for (const id of ['google_rest_frm', 'apple_rest_frm']) { 241 document.getElementById(id)?.addEventListener('submit', event => { 242 event.preventDefault(); 243 event.stopPropagation(); 244 window.location.href = '/forget-password'; 245 }); 246 } 247})();
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.