mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat: API key gating for desktop cloud fallback + registration system Gate desktop cloud fallback behind WORLDMONITOR_API_KEY — desktop users need a valid key for cloud access, otherwise operate local-only (sidecar). Add email registration system via Convex DB for future key distribution. Client-side: installRuntimeFetchPatch() checks key presence before allowing cloud fallback, with secretsReady promise + 2s timeout. Server-side: origin-aware validation in sebuf gateway — desktop origins require key, web origins pass through. - Add WORLDMONITOR_API_KEY to 3-place secret system (Rust, TS, sidecar) - New "World Monitor" settings tab with key input + registration form - New api/_api-key.js server-side validation (origin-aware) - New api/register-interest.js edge function with rate limiting - Convex DB schema + mutation for email registration storage - CORS headers updated for X-WorldMonitor-Key + Authorization - E2E tests for key gate (blocked without key, allowed with key) - Deployment docs (API_KEY_DEPLOYMENT.md) + updated desktop config docs * fix: harden worldmonitor key + registration input handling * fix: show invalid WorldMonitor API key status * fix: simplify key validation, trim registration checks, add env example vars - Inline getValidKeys() in _api-key.js - Remove redundant type checks in register-interest.js - Simplify WorldMonitorTab status to present/missing - Add WORLDMONITOR_VALID_KEYS and CONVEX_URL to .env.example * feat(sidecar): integrate proto gateway bundle into desktop build The sidecar's buildRouteTable() only discovers .js files, so the proto gateway at api/[domain]/v1/[rpc].ts was invisible — all 45 sebuf RPCs returned 404 in the desktop app. Wire the existing build script into Tauri's build commands and add esbuild as an explicit devDependency.
62 lines
4.1 KiB
HTML
62 lines
4.1 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>World Monitor Settings</title>
|
|
<script>(function(){try{var t=localStorage.getItem('worldmonitor-theme');if(t==='light')document.documentElement.dataset.theme='light';}catch(e){}document.documentElement.classList.add('no-transition');})()</script>
|
|
</head>
|
|
<body style="background:#1a1c1e;color:#e8eaed;margin:0">
|
|
<div class="settings-shell">
|
|
<div class="settings-tabs" role="tablist">
|
|
<button class="settings-tab active" role="tab" aria-selected="true" aria-controls="tabPanelLLMs" data-tab="llms">LLMs</button>
|
|
<button class="settings-tab" role="tab" aria-selected="false" aria-controls="tabPanelKeys" data-tab="keys">API Keys</button>
|
|
<button class="settings-tab" role="tab" aria-selected="false" aria-controls="tabPanelDebug" data-tab="debug">Debug & Logs</button>
|
|
<button class="settings-tab" role="tab" aria-selected="false" aria-controls="tabPanelWorldMonitor" data-tab="worldmonitor">World Monitor</button>
|
|
</div>
|
|
<p id="settingsActionStatus" class="settings-action-status" aria-live="polite"></p>
|
|
<div class="settings-tab-panels">
|
|
<div id="tabPanelLLMs" class="settings-tab-panel active" role="tabpanel">
|
|
<main id="llmApp" class="settings-content"><div style="display:flex;align-items:center;justify-content:center;padding:60px 0;color:#9aa0a6;font-size:14px;gap:10px"><svg width="20" height="20" viewBox="0 0 24 24" style="animation:spin 1s linear infinite"><style>@keyframes spin{to{transform:rotate(360deg)}}</style><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-dasharray="31 31"/></svg>Loading...</div></main>
|
|
</div>
|
|
<div id="tabPanelKeys" class="settings-tab-panel" role="tabpanel">
|
|
<main id="apiKeysApp" class="settings-content"><div style="display:flex;align-items:center;justify-content:center;padding:60px 0;color:#9aa0a6;font-size:14px;gap:10px"><svg width="20" height="20" viewBox="0 0 24 24" style="animation:spin 1s linear infinite"><style>@keyframes spin{to{transform:rotate(360deg)}}</style><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" stroke-dasharray="31 31"/></svg>Loading...</div></main>
|
|
</div>
|
|
<div id="tabPanelDebug" class="settings-tab-panel" role="tabpanel">
|
|
<div class="debug-actions">
|
|
<button id="openLogsBtn" type="button">Open Logs Folder</button>
|
|
<button id="openSidecarLogBtn" type="button">Open API Log</button>
|
|
</div>
|
|
<section class="settings-diagnostics" id="diagnosticsSection">
|
|
<header class="diag-header">
|
|
<h2>Diagnostics</h2>
|
|
<div class="diag-toggles">
|
|
<label><input type="checkbox" id="verboseApiLog"> Verbose Sidecar Log</label>
|
|
<label><input type="checkbox" id="fetchDebugLog"> Frontend Fetch Debug</label>
|
|
</div>
|
|
</header>
|
|
<div class="diag-traffic-bar">
|
|
<h3>API Traffic <span id="trafficCount"></span></h3>
|
|
<div class="diag-traffic-controls">
|
|
<label><input type="checkbox" id="autoRefreshLog" checked> Auto</label>
|
|
<button id="refreshLogBtn" type="button">Refresh</button>
|
|
<button id="clearLogBtn" type="button">Clear</button>
|
|
</div>
|
|
</div>
|
|
<div id="trafficLog" class="diag-traffic-log"></div>
|
|
</section>
|
|
</div>
|
|
<div id="tabPanelWorldMonitor" class="settings-tab-panel" role="tabpanel">
|
|
<main id="worldmonitorApp" class="settings-content"></main>
|
|
</div>
|
|
</div>
|
|
<footer class="settings-footer">
|
|
<button id="cancelBtn" type="button" class="settings-btn settings-btn-secondary">Cancel</button>
|
|
<button id="okBtn" type="button" class="settings-btn settings-btn-primary">OK</button>
|
|
</footer>
|
|
</div>
|
|
|
|
<script type="module" src="/src/settings-main.ts"></script>
|
|
</body>
|
|
</html>
|