fix: registration via direct Convex call + compact WM tab layout

- Sidecar calls Convex HTTP API directly (Vercel Attack Challenge Mode
  blocks server-side proxy). CONVEX_URL read from env, not hardcoded.
- Rust injects CONVEX_URL into sidecar via option_env! (CI) / env var (dev)
- GitHub Actions passes CONVEX_URL secret to all 4 build steps
- Tighten WM tab CSS spacing so all content fits in one viewport
This commit is contained in:
Elie Habib
2026-02-21 11:36:03 +00:00
parent 1fc6dc2dbb
commit 8699dae5e5
4 changed files with 56 additions and 34 deletions

View File

@@ -192,6 +192,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_VARIANT: full
VITE_DESKTOP_RUNTIME: '1'
CONVEX_URL: ${{ secrets.CONVEX_URL }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.APPLE_SIGNING_IDENTITY }}
@@ -215,6 +216,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_VARIANT: full
VITE_DESKTOP_RUNTIME: '1'
CONVEX_URL: ${{ secrets.CONVEX_URL }}
with:
tagName: v__VERSION__
releaseName: 'World Monitor v__VERSION__'
@@ -232,6 +234,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_VARIANT: tech
VITE_DESKTOP_RUNTIME: '1'
CONVEX_URL: ${{ secrets.CONVEX_URL }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.APPLE_SIGNING_IDENTITY }}
@@ -256,6 +259,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_VARIANT: tech
VITE_DESKTOP_RUNTIME: '1'
CONVEX_URL: ${{ secrets.CONVEX_URL }}
with:
tagName: v__VERSION__-tech
releaseName: 'Tech Monitor v__VERSION__'

View File

@@ -796,8 +796,12 @@ async function dispatch(requestUrl, req, routes, context) {
}
return json({ verboseMode });
}
// Registration proxy — forward to cloud (bypasses Vercel bot protection)
// Registration — call Convex directly (Vercel Attack Challenge Mode blocks server-side)
if (requestUrl.pathname === '/api/register-interest' && req.method === 'POST') {
const convexUrl = process.env.CONVEX_URL;
if (!convexUrl) {
return json({ error: 'Registration service not configured' }, 503);
}
try {
const body = await new Promise((resolve, reject) => {
const chunks = [];
@@ -805,21 +809,29 @@ async function dispatch(requestUrl, req, routes, context) {
req.on('end', () => resolve(Buffer.concat(chunks).toString()));
req.on('error', reject);
});
const response = await fetchWithTimeout('https://worldmonitor.app/api/register-interest', {
const parsed = JSON.parse(body);
const email = parsed.email;
if (!email || typeof email !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return json({ error: 'Invalid email address' }, 400);
}
const response = await fetchWithTimeout(`${convexUrl}/api/mutation`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Origin': 'https://worldmonitor.app',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
},
body,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: 'registerInterest:register',
args: { email, source: parsed.source || 'desktop', appVersion: parsed.appVersion || 'unknown' },
format: 'json',
}),
}, 15000);
const responseBody = await response.text();
return new Response(responseBody || '{}', {
status: response.status,
headers: { 'content-type': 'application/json' },
});
let result;
try { result = JSON.parse(responseBody); } catch { result = { status: 'registered' }; }
if (result.status === 'error') {
return json({ error: result.errorMessage || 'Registration failed' }, 500);
}
return json(result.value || result);
} catch (e) {
logVerbose(`[register-interest] error: ${e.message}`);
return json({ error: 'Registration service unreachable' }, 502);
}
}

View File

@@ -733,6 +733,13 @@ fn start_local_api(app: &AppHandle) -> Result<(), String> {
}
append_desktop_log(app, "INFO", &format!("injected {secret_count} keychain secrets into sidecar env"));
// Inject build-time secrets (CI) with runtime env fallback (dev)
if let Some(url) = option_env!("CONVEX_URL") {
cmd.env("CONVEX_URL", url);
} else if let Ok(url) = std::env::var("CONVEX_URL") {
cmd.env("CONVEX_URL", url);
}
let child = cmd
.spawn()
.map_err(|e| format!("Failed to launch local API: {e}"))?;

View File

@@ -605,28 +605,27 @@ tr.diag-err td { color: var(--settings-red); }
/* Hero banner */
.wm-hero {
text-align: center;
padding: 28px 24px;
margin-bottom: 28px;
padding: 18px 20px;
margin-bottom: 16px;
border: 1px solid rgba(52, 211, 153, 0.15);
border-radius: 12px;
background: linear-gradient(180deg, rgba(52, 211, 153, 0.04) 0%, transparent 100%);
}
.wm-hero-title {
margin: 0 0 12px;
font-size: 24px;
margin: 0 0 8px;
font-size: 22px;
font-weight: 700;
color: var(--settings-text);
letter-spacing: -0.01em;
}
.wm-hero-desc {
margin: 0;
font-size: 14px;
color: var(--settings-text-secondary);
line-height: 1.6;
max-width: 480px;
margin: 0 auto;
font-size: 13px;
color: var(--settings-text-secondary);
line-height: 1.5;
max-width: 480px;
}
/* OR divider */
@@ -634,7 +633,7 @@ tr.diag-err td { color: var(--settings-red); }
display: flex;
align-items: center;
gap: 16px;
margin: 24px 0;
margin: 14px 0;
color: var(--settings-text-secondary);
font-size: 13px;
font-weight: 500;
@@ -650,43 +649,43 @@ tr.diag-err td { color: var(--settings-red); }
/* BYOK footer */
.wm-byok {
margin-top: 28px;
padding: 16px 20px;
margin-top: 14px;
padding: 12px 16px;
background: var(--settings-surface);
border-radius: 8px;
border: 1px solid var(--settings-border);
}
.wm-byok-title {
margin: 0 0 4px;
font-size: 14px;
margin: 0 0 2px;
font-size: 13px;
font-weight: 600;
color: var(--settings-text);
}
.wm-byok-desc {
margin: 0;
font-size: 13px;
font-size: 12px;
color: var(--settings-text-secondary);
line-height: 1.5;
line-height: 1.4;
}
.wm-section {
margin-bottom: 24px;
margin-bottom: 12px;
}
.wm-section-title {
margin: 0 0 4px;
font-size: 15px;
margin: 0 0 2px;
font-size: 14px;
font-weight: 600;
color: var(--settings-text);
}
.wm-section-desc {
margin: 0 0 12px;
font-size: 13px;
margin: 0 0 8px;
font-size: 12px;
color: var(--settings-text-secondary);
line-height: 1.5;
line-height: 1.4;
}
.wm-key-row {