adds the ability to login to stoat desktop with your session token
Signed-off-by: AvaLilac <257690424+AvaLilac@users.noreply.github.com>
This commit is contained in:
parent
7d07e3fcc8
commit
b22130645a
1 changed files with 139 additions and 0 deletions
139
avia_core/LoginWithToken.js
Normal file
139
avia_core/LoginWithToken.js
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
(function () {
|
||||||
|
if (window.__LOGIN_WITH_TOKEN__) return;
|
||||||
|
window.__LOGIN_WITH_TOKEN__ = true;
|
||||||
|
|
||||||
|
async function loginWithToken(token) {
|
||||||
|
const res = await fetch('https://stoat.chat/api/users/@me', {
|
||||||
|
headers: { 'x-session-token': token }
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Invalid token');
|
||||||
|
const user = await res.json();
|
||||||
|
|
||||||
|
const db = await new Promise((resolve, reject) => {
|
||||||
|
const r = indexedDB.open('localforage');
|
||||||
|
r.onsuccess = () => resolve(r.result);
|
||||||
|
r.onerror = () => reject(r.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
const tx = db.transaction('keyvaluepairs', 'readwrite');
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const r = tx.objectStore('keyvaluepairs').put({
|
||||||
|
session: {
|
||||||
|
_id: user._id,
|
||||||
|
token: token,
|
||||||
|
userId: user._id,
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
}, 'auth');
|
||||||
|
r.onsuccess = () => resolve();
|
||||||
|
r.onerror = () => reject(r.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTokenDialog() {
|
||||||
|
const backdrop = document.createElement('div');
|
||||||
|
backdrop.className = 'top_0 left_0 right_0 bottom_0 pos_fixed z_100 max-h_100% d_grid us_none place-items_center pointer-events_all anim-n_scrimFadeIn anim-dur_0.1s anim-fm_forwards trs_var(--transitions-medium)_all p_80px ov-y_auto';
|
||||||
|
backdrop.style.cssText = '--background: rgba(0, 0, 0, 0.6);';
|
||||||
|
|
||||||
|
backdrop.innerHTML = `
|
||||||
|
<div style="opacity: 1; --motion-translateY: 0px; transform: translateY(var(--motion-translateY));">
|
||||||
|
<div class="p_24px min-w_280px max-w_560px bdr_28px d_flex flex-d_column c_var(--md-sys-color-on-surface) bg_var(--md-sys-color-surface-container-high)">
|
||||||
|
<span class="lh_2rem fs_1.5rem ls_0 fw_400 mbe_16px">Login With Token</span>
|
||||||
|
<div class="c_var(--md-sys-color-on-surface-variant) lh_1.25rem fs_0.875rem ls_0.015625rem fw_400">
|
||||||
|
<div class="d_flex flex-d_column flex-g_initial m_0 ai_initial jc_initial gap_var(--gap-md)">
|
||||||
|
<mdui-text-field id="lwt-token-input" variant="filled" type="password" name="token" required label="Session Token"></mdui-text-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gap_8px d_flex jc_end mbs_24px">
|
||||||
|
<button id="lwt-close-btn" type="button" class="lh_1.25rem fs_0.875rem ls_0.015625rem fw_400 pos_relative px_16px flex-sh_0 d_flex ai_center jc_center ff_inherit cursor_pointer bd_none trs_var(--transitions-medium)_all c_var(--color) fill_var(--color) h_40px bdr_var(--borderRadius-full) --color_var(--md-sys-color-primary)">
|
||||||
|
<md-ripple aria-hidden="true"></md-ripple>Close
|
||||||
|
</button>
|
||||||
|
<button id="lwt-login-btn" type="button" class="lh_1.25rem fs_0.875rem ls_0.015625rem fw_400 pos_relative px_16px flex-sh_0 d_flex ai_center jc_center ff_inherit cursor_pointer bd_none trs_var(--transitions-medium)_all c_var(--color) fill_var(--color) h_40px bdr_var(--borderRadius-full) --color_var(--md-sys-color-on-primary) bg_var(--md-sys-color-primary)">
|
||||||
|
<md-ripple aria-hidden="true"></md-ripple>Login
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(backdrop);
|
||||||
|
|
||||||
|
const closeBtn = backdrop.querySelector('#lwt-close-btn');
|
||||||
|
const loginBtn = backdrop.querySelector('#lwt-login-btn');
|
||||||
|
const tokenInput = backdrop.querySelector('#lwt-token-input');
|
||||||
|
|
||||||
|
function close() { backdrop.remove(); }
|
||||||
|
|
||||||
|
function setLoading(loading) {
|
||||||
|
loginBtn.disabled = loading;
|
||||||
|
loginBtn.style.cursor = loading ? 'not-allowed' : 'pointer';
|
||||||
|
const ripple = loginBtn.querySelector('md-ripple');
|
||||||
|
loginBtn.textContent = loading ? 'Logging in…' : 'Login';
|
||||||
|
if (ripple) loginBtn.prepend(ripple);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setError(msg) {
|
||||||
|
loginBtn.disabled = false;
|
||||||
|
loginBtn.style.cursor = 'pointer';
|
||||||
|
const ripple = loginBtn.querySelector('md-ripple');
|
||||||
|
loginBtn.textContent = msg;
|
||||||
|
if (ripple) loginBtn.prepend(ripple);
|
||||||
|
setTimeout(() => {
|
||||||
|
loginBtn.textContent = 'Login';
|
||||||
|
if (ripple) loginBtn.prepend(ripple);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
backdrop.addEventListener('click', (e) => { if (e.target === backdrop) close(); });
|
||||||
|
closeBtn.addEventListener('click', close);
|
||||||
|
|
||||||
|
loginBtn.addEventListener('click', async () => {
|
||||||
|
const token = tokenInput.value?.trim();
|
||||||
|
if (!token) {
|
||||||
|
setError('Enter a token!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await loginWithToken(token);
|
||||||
|
} catch (err) {
|
||||||
|
setError('Invalid token!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectLoginButton() {
|
||||||
|
const signUpBtn = [...document.querySelectorAll('button')]
|
||||||
|
.find(b => b.textContent.trim() === 'Sign Up');
|
||||||
|
if (!signUpBtn) return;
|
||||||
|
|
||||||
|
const parent = signUpBtn.parentElement;
|
||||||
|
if (parent.querySelector('[data-lwt-btn]')) return;
|
||||||
|
|
||||||
|
const clone = signUpBtn.cloneNode(false);
|
||||||
|
clone.dataset.lwtBtn = 'true';
|
||||||
|
clone.textContent = 'Login With Token';
|
||||||
|
|
||||||
|
const ripple = document.createElement('md-ripple');
|
||||||
|
ripple.setAttribute('aria-hidden', 'true');
|
||||||
|
clone.prepend(ripple);
|
||||||
|
|
||||||
|
clone.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
openTokenDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
signUpBtn.insertAdjacentElement('afterend', clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
let debounceTimer = null;
|
||||||
|
new MutationObserver(() => {
|
||||||
|
clearTimeout(debounceTimer);
|
||||||
|
debounceTimer = setTimeout(injectLoginButton, 150);
|
||||||
|
}).observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
injectLoginButton();
|
||||||
|
})();
|
||||||
Loading…
Add table
Reference in a new issue