mm-math 0.0.4 → 0.0.6
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/index.html +67 -10
- package/package.json +1 -1
package/index.html
CHANGED
|
@@ -519,7 +519,69 @@ 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. The iframe is on calc.moshelab.com so its
|
|
526
|
+
// fetch to /api/login is same-origin — no CORS, no Cloudflare cross-origin checks.
|
|
527
|
+
let _bridge = null;
|
|
528
|
+
let _bridgeReadyResolve = null;
|
|
529
|
+
const _bridgeReady = new Promise(function(resolve) { _bridgeReadyResolve = resolve; });
|
|
530
|
+
|
|
531
|
+
if (!_onCalc) {
|
|
532
|
+
_bridge = document.createElement('iframe');
|
|
533
|
+
_bridge.src = BRIDGE_ORIGIN + '/login-frame.html';
|
|
534
|
+
_bridge.style.cssText = 'display:none;width:0;height:0;border:0;position:absolute;left:-9999px;top:-9999px;';
|
|
535
|
+
_bridge.setAttribute('aria-hidden', 'true');
|
|
536
|
+
document.body.appendChild(_bridge);
|
|
537
|
+
} else {
|
|
538
|
+
// On calc.moshelab.com, bridge isn't used — resolve immediately
|
|
539
|
+
_bridgeReadyResolve();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Shared message handler — resolves the ready promise and dispatches login results
|
|
543
|
+
const _pendingLogins = {};
|
|
544
|
+
window.addEventListener('message', function(event) {
|
|
545
|
+
if (event.origin !== BRIDGE_ORIGIN) return;
|
|
546
|
+
const d = event.data;
|
|
547
|
+
if (!d) return;
|
|
548
|
+
if (d.type === 'bridge_ready') { _bridgeReadyResolve(); return; }
|
|
549
|
+
if (d.type === 'login_result' && d.nonce && _pendingLogins[d.nonce]) {
|
|
550
|
+
_pendingLogins[d.nonce](d);
|
|
551
|
+
delete _pendingLogins[d.nonce];
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
function _loginDirect(username, password) {
|
|
556
|
+
return fetch('/api/login', {
|
|
557
|
+
method: 'POST',
|
|
558
|
+
headers: { 'Content-Type': 'application/json' },
|
|
559
|
+
body: JSON.stringify({ username, password }),
|
|
560
|
+
}).then(function(res) {
|
|
561
|
+
return res.json().then(function(data) {
|
|
562
|
+
data.ok = res.ok; data.status = res.status; return data;
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function _loginViaBridge(username, password) {
|
|
568
|
+
return _bridgeReady.then(function() {
|
|
569
|
+
return new Promise(function(resolve, reject) {
|
|
570
|
+
if (!_bridge || !_bridge.contentWindow) {
|
|
571
|
+
return reject(new Error('Auth bridge unavailable'));
|
|
572
|
+
}
|
|
573
|
+
const nonce = Math.random().toString(36).slice(2, 10);
|
|
574
|
+
const timer = setTimeout(function() {
|
|
575
|
+
delete _pendingLogins[nonce];
|
|
576
|
+
reject(new Error('Sign-in timed out. Try again.'));
|
|
577
|
+
}, 15000);
|
|
578
|
+
|
|
579
|
+
_pendingLogins[nonce] = function(d) { clearTimeout(timer); resolve(d); };
|
|
580
|
+
_bridge.contentWindow.postMessage({ type: 'login', username: username, password: password, nonce: nonce }, BRIDGE_ORIGIN);
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
523
585
|
async function doLogin() {
|
|
524
586
|
const username = document.getElementById('fUser').value.trim();
|
|
525
587
|
const password = document.getElementById('fPass').value;
|
|
@@ -536,15 +598,10 @@ async function doLogin() {
|
|
|
536
598
|
errEl.textContent = '';
|
|
537
599
|
|
|
538
600
|
try {
|
|
539
|
-
const
|
|
540
|
-
method: 'POST',
|
|
541
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
542
|
-
body: new URLSearchParams({ username, password }),
|
|
543
|
-
});
|
|
544
|
-
const data = await res.json();
|
|
601
|
+
const data = await (_onCalc ? _loginDirect(username, password) : _loginViaBridge(username, password));
|
|
545
602
|
|
|
546
|
-
if (!
|
|
547
|
-
errEl.textContent =
|
|
603
|
+
if (!data.ok || !data.success) {
|
|
604
|
+
errEl.textContent = data.status === 429 || (data.error || '').includes('Too many')
|
|
548
605
|
? 'Too many attempts. Wait 60 seconds.'
|
|
549
606
|
: 'Invalid username or password.';
|
|
550
607
|
btn.disabled = false;
|
|
@@ -568,7 +625,7 @@ async function doLogin() {
|
|
|
568
625
|
window.location.href = './browse.html';
|
|
569
626
|
}
|
|
570
627
|
} catch (err) {
|
|
571
|
-
errEl.textContent =
|
|
628
|
+
errEl.textContent = err && err.message ? err.message : String(err);
|
|
572
629
|
btn.disabled = false;
|
|
573
630
|
btn.textContent = 'Sign In';
|
|
574
631
|
}
|