DUNGEONS-30: add access gateway with Telegram and Discord approvals
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
73
public/app.js
Normal file
73
public/app.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const form = document.querySelector('#access-form');
|
||||
const input = document.querySelector('#access-number');
|
||||
const button = document.querySelector('#enter-button');
|
||||
const label = document.querySelector('#button-label');
|
||||
const status = document.querySelector('#form-status');
|
||||
const codeLength = Number(form.dataset.codeLength);
|
||||
let waiting = false;
|
||||
let cooldown;
|
||||
|
||||
function announce(message, success = false) {
|
||||
status.textContent = message;
|
||||
status.classList.toggle('success', success);
|
||||
status.hidden = false;
|
||||
}
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
input.removeAttribute('aria-invalid');
|
||||
if (!waiting) status.hidden = true;
|
||||
});
|
||||
|
||||
form.addEventListener('submit', async event => {
|
||||
event.preventDefault();
|
||||
if (waiting) return;
|
||||
const code = input.value.replace(/\s/g, '');
|
||||
if (!new RegExp(`^\\d{${codeLength}}$`).test(code)) {
|
||||
input.setAttribute('aria-invalid', 'true');
|
||||
announce(`Enter the ${codeLength}-digit number from your bot DM.`);
|
||||
input.focus();
|
||||
return;
|
||||
}
|
||||
waiting = true;
|
||||
button.disabled = true;
|
||||
form.setAttribute('aria-busy', 'true');
|
||||
label.textContent = 'CHECKING YOUR NUMBER…';
|
||||
status.hidden = true;
|
||||
let retryAfter = 0;
|
||||
let approved = false;
|
||||
try {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const next = location.pathname.startsWith('/_access') ? params.get('next') || '/' : location.pathname + location.search + location.hash;
|
||||
const response = await fetch('/_access/login', {
|
||||
method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ code, next }), signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
retryAfter = response.status === 429 ? Number(data.retryAfter) || 60 : 0;
|
||||
if (response.status === 401) input.setAttribute('aria-invalid', 'true');
|
||||
throw new Error(data.error || 'We could not check your number. Please try again.');
|
||||
}
|
||||
approved = true;
|
||||
input.value = '';
|
||||
announce('Access granted. Your next chapter is opening…', true);
|
||||
label.textContent = 'OPENING THE GROUND…';
|
||||
location.assign(data.next);
|
||||
} catch (error) {
|
||||
announce(error.name === 'TimeoutError' || error instanceof TypeError ? 'Connection interrupted. Check your connection and try again.' : error.message);
|
||||
status.focus();
|
||||
} finally {
|
||||
form.removeAttribute('aria-busy');
|
||||
if (retryAfter > 0) {
|
||||
const deadline = Date.now() + retryAfter * 1000;
|
||||
const tick = () => {
|
||||
const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1000));
|
||||
label.textContent = `TRY AGAIN IN ${seconds}s`;
|
||||
if (seconds === 0) { clearInterval(cooldown); waiting = false; button.disabled = false; label.textContent = 'ENTER THE ALPHA'; }
|
||||
};
|
||||
tick(); cooldown = setInterval(tick, 1000);
|
||||
} else if (!approved) {
|
||||
waiting = false; button.disabled = false; label.textContent = 'ENTER THE ALPHA';
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user