mm-math 0.0.4 → 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.
Files changed (2) hide show
  1. package/index.html +57 -10
  2. package/package.json +1 -1
package/index.html CHANGED
@@ -519,7 +519,59 @@ document.getElementById('fUser').addEventListener('keydown', e => {
519
519
  });
520
520
 
521
521
  // ── Login ─────────────────────────────────────────────────────────────────────
522
- const API_BASE = window.location.hostname === 'calc.moshelab.com' ? '' : 'https://calc.moshelab.com';
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 res = await fetch(API_BASE + '/api/login', {
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();
591
+ const data = await (_onCalc ? _loginDirect(username, password) : _loginViaBridge(username, password));
545
592
 
546
- if (!res.ok || !data.success) {
547
- errEl.textContent = res.status === 429
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 = 'Error: ' + (err && err.message ? err.message : err);
618
+ errEl.textContent = err && err.message ? err.message : String(err);
572
619
  btn.disabled = false;
573
620
  btn.textContent = 'Sign In';
574
621
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm-math",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "STEM educational toolkit",
5
5
  "main": "index.html",
6
6
  "files": [