mm-math 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api.js +1 -0
- package/index.html +57 -10
- package/package.json +1 -1
package/api.js
CHANGED
package/index.html
CHANGED
|
@@ -519,7 +519,59 @@ document.getElementById('fUser').addEventListener('keydown', e => {
|
|
|
519
519
|
});
|
|
520
520
|
|
|
521
521
|
// ── Login ─────────────────────────────────────────────────────────────────────
|
|
522
|
-
const
|
|
522
|
+
const _onCalc = window.location.hostname === 'calc.moshelab.com';
|
|
523
|
+
const BRIDGE_ORIGIN = 'https://calc.moshelab.com';
|
|
524
|
+
|
|
525
|
+
// Preload invisible auth-bridge iframe when running outside calc.moshelab.com.
|
|
526
|
+
// The iframe makes a same-origin fetch to the API (no CORS), then postMessages
|
|
527
|
+
// the result back. This bypasses Cloudflare's cross-origin fetch restrictions.
|
|
528
|
+
let _bridge = null;
|
|
529
|
+
if (!_onCalc) {
|
|
530
|
+
_bridge = document.createElement('iframe');
|
|
531
|
+
_bridge.src = BRIDGE_ORIGIN + '/login-frame.html';
|
|
532
|
+
_bridge.style.cssText = 'display:none;width:0;height:0;border:0;position:absolute;left:-9999px;top:-9999px;';
|
|
533
|
+
_bridge.setAttribute('aria-hidden', 'true');
|
|
534
|
+
document.body.appendChild(_bridge);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function _loginDirect(username, password) {
|
|
538
|
+
return fetch('/api/login', {
|
|
539
|
+
method: 'POST',
|
|
540
|
+
headers: { 'Content-Type': 'application/json' },
|
|
541
|
+
body: JSON.stringify({ username, password }),
|
|
542
|
+
}).then(function(res) {
|
|
543
|
+
return res.json().then(function(data) {
|
|
544
|
+
data.ok = res.ok; data.status = res.status; return data;
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function _loginViaBridge(username, password) {
|
|
550
|
+
return new Promise(function(resolve, reject) {
|
|
551
|
+
const nonce = Math.random().toString(36).slice(2, 10);
|
|
552
|
+
const timer = setTimeout(function() {
|
|
553
|
+
window.removeEventListener('message', handler);
|
|
554
|
+
reject(new Error('Request timed out. Try again.'));
|
|
555
|
+
}, 15000);
|
|
556
|
+
|
|
557
|
+
function handler(event) {
|
|
558
|
+
if (event.origin !== BRIDGE_ORIGIN) return;
|
|
559
|
+
const d = event.data;
|
|
560
|
+
if (!d || d.type !== 'login_result' || d.nonce !== nonce) return;
|
|
561
|
+
clearTimeout(timer);
|
|
562
|
+
window.removeEventListener('message', handler);
|
|
563
|
+
resolve(d);
|
|
564
|
+
}
|
|
565
|
+
window.addEventListener('message', handler);
|
|
566
|
+
|
|
567
|
+
if (!_bridge || !_bridge.contentWindow) {
|
|
568
|
+
clearTimeout(timer);
|
|
569
|
+
return reject(new Error('Auth bridge unavailable'));
|
|
570
|
+
}
|
|
571
|
+
_bridge.contentWindow.postMessage({ type: 'login', username, password, nonce }, BRIDGE_ORIGIN);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
523
575
|
async function doLogin() {
|
|
524
576
|
const username = document.getElementById('fUser').value.trim();
|
|
525
577
|
const password = document.getElementById('fPass').value;
|
|
@@ -536,15 +588,10 @@ async function doLogin() {
|
|
|
536
588
|
errEl.textContent = '';
|
|
537
589
|
|
|
538
590
|
try {
|
|
539
|
-
const
|
|
540
|
-
method: 'POST',
|
|
541
|
-
headers: { 'Content-Type': 'application/json' },
|
|
542
|
-
body: JSON.stringify({ username, password }),
|
|
543
|
-
});
|
|
544
|
-
const data = await res.json();
|
|
591
|
+
const data = await (_onCalc ? _loginDirect(username, password) : _loginViaBridge(username, password));
|
|
545
592
|
|
|
546
|
-
if (!
|
|
547
|
-
errEl.textContent =
|
|
593
|
+
if (!data.ok || !data.success) {
|
|
594
|
+
errEl.textContent = data.status === 429 || (data.error || '').includes('Too many')
|
|
548
595
|
? 'Too many attempts. Wait 60 seconds.'
|
|
549
596
|
: 'Invalid username or password.';
|
|
550
597
|
btn.disabled = false;
|
|
@@ -568,7 +615,7 @@ async function doLogin() {
|
|
|
568
615
|
window.location.href = './browse.html';
|
|
569
616
|
}
|
|
570
617
|
} catch (err) {
|
|
571
|
-
errEl.textContent =
|
|
618
|
+
errEl.textContent = err && err.message ? err.message : String(err);
|
|
572
619
|
btn.disabled = false;
|
|
573
620
|
btn.textContent = 'Sign In';
|
|
574
621
|
}
|