PageSourceSearch

https://realservicesolutions.com/script/js/login.js

js realservicesolutions.com collected 2026-10-02 01:41:18 UTC 11,587 bytes, 247 lines download raw bytes

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.