/** * GET /api/mobile/app-download-link — long-lived (24 h) signed APK download URL for the * Settings → Mobile App install page (QR code + download button). * Requires a session AND mobile-app entitlement (ad_user.IsMobileWorker or client administrator). * Optional ?file=history/.apk for an archived build (admins only). */ import { readManifest, enrichManifest, signDownloadUrl, safeApkPath, canUseMobileApp, isAdminCookie, TTL_INSTALL_PAGE, CURRENT_APK } from '../../utils/mobileReleases' export default defineEventHandler(async (event) => { setHeader(event, 'Cache-Control', 'no-store') const token = await getTokenHelper(event) if (!token) { setResponseStatus(event, 401); return { status: 401, message: 'Not authenticated' } } if (!canUseMobileApp(event)) { setResponseStatus(event, 403) return { status: 403, code: 'not_mobile_worker', message: 'Mobile app not enabled for this user (IsMobileWorker)' } } const manifest = readManifest() if (!manifest) { setResponseStatus(event, 404); return { status: 404, message: 'No release published' } } let rel = CURRENT_APK const requested = String(getQuery(event).file || '').replace(/^\/+/, '') if (requested && requested !== CURRENT_APK) { if (!isAdminCookie(event)) { setResponseStatus(event, 403); return { status: 403, message: 'Admins only' } } if (!safeApkPath(requested)) { setResponseStatus(event, 404); return { status: 404, message: 'File not found' } } rel = requested } try { const m = enrichManifest(manifest) const url = signDownloadUrl(rel, TTL_INSTALL_PAGE) return { status: 200, url, file: rel, expiresAt: new Date(Date.now() + TTL_INSTALL_PAGE * 1000).toISOString(), versionName: m.versionName, versionCode: m.versionCode, releaseDate: m.releaseDate, releaseNotes: m.releaseNotes, minRequiredVersion: m.minRequiredVersion, size: m.size, sha256: m.sha256, isAdmin: isAdminCookie(event) } } catch (err: any) { setResponseStatus(event, 500) return { status: 500, message: err?.message || 'Download URL not available' } } })