feat: Windows download link + self-served update feed + install tracking
Downloads/updates: - config: DOWNLOADS_DIR (override to a mounted volume in prod). - static.js: serves /downloads/* (installer, latest.yml, .blockmap) with range support for resumable + differential auto-updates; /download/windows redirects to the current .exe (stable link). Landing page gets a "Download the Windows desktop app" button (hidden in-app). Install tracking (who installed the app): - db app_installs + repos.appInstalls (upsert by install_id, fills in the user on sign-in). - POST /api/v1/telemetry/install (records install + user once authenticated); GET /api/v1/admin/installs (admin: list installs with user/version/os/last-seen). - desktop main.js: stable per-install id in userData, exposed via preload (bizConnectNative.installId/version/os); home.html reportInstall() posts it after login. - e2e: +2 checks (telemetry recorded, admin sees it). 119/119. build batch20. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -316,6 +316,24 @@ route('POST', '/api/devices/remove', async (req, res) => {
|
||||
json(res, 200, { ok: true });
|
||||
});
|
||||
|
||||
// --- App install telemetry: records each install and, once the user signs in, who's using it. ---
|
||||
route('POST', '/api/telemetry/install', async (req, res) => {
|
||||
const { installId, platform, appVersion, os } = await readBody(req);
|
||||
if (!installId || typeof installId !== 'string') return json(res, 400, { error: 'installId required' });
|
||||
const u = currentUser(req); // may be null on a pre-login launch — still counted
|
||||
try {
|
||||
R.appInstalls.record({ id: A.id(), installId: installId.slice(0, 64), userId: u && u.id, userEmail: u && u.email, tenantId: u && u.team_id, platform: (platform || '').slice(0, 20), appVersion: (appVersion || '').slice(0, 20), os: (os || '').slice(0, 60) });
|
||||
} catch (_) {}
|
||||
json(res, 200, { ok: true });
|
||||
});
|
||||
// Admin: who installed the app (this tenant).
|
||||
route('GET', '/api/admin/installs', async (req, res) => {
|
||||
const u = currentUser(req);
|
||||
if (!u) return json(res, 401, { error: 'unauthorized' });
|
||||
if (u.role !== 'admin') return json(res, 403, { error: 'admin only' });
|
||||
json(res, 200, R.appInstalls.listForTenant(u.team_id));
|
||||
});
|
||||
|
||||
// ---------- BizGaze SSO: agent arrives already logged in ----------
|
||||
route('GET', '/sso', async (req, res) => {
|
||||
if (!process.env.SSO_SECRET) { res.writeHead(503); return res.end('SSO not configured'); }
|
||||
|
||||
Reference in New Issue
Block a user