// SPDX-FileCopyrightText: 2026 Echolot contributors // SPDX-License-Identifier: GPL-3.0-or-later // Everything under public/ is served straight from the edge without invoking // this Worker. The Worker exists for the short stable URLs (/apk, /fdroid, // /source) — short enough for a QR code — and to resolve "/apk" to the newest // release asset at request time, so tagging a release in Gitea is the only // publish step. No site redeploy, no URL to update. const STATIC_ROUTES = { "/fdroid": "FDROID_URL", "/source": "SOURCE_URL", }; // Resolve the newest APK from the Gitea "latest release" API. Cached at the // edge for 5 minutes so a release becomes visible quickly, while Gitea sees // at most one API hit per POP per 5 min regardless of download traffic. async function latestApkUrl(env) { if (!env.GITEA_REPO_API) return null; const res = await fetch(`${env.GITEA_REPO_API}/releases/latest`, { headers: { Accept: "application/json", "User-Agent": "echolot-site" }, cf: { cacheTtl: 300, cacheEverything: true }, }); if (!res.ok) return null; const rel = await res.json(); const apk = rel.assets?.find((a) => a.name?.endsWith(".apk")); return apk?.browser_download_url ?? null; } function redirect(location) { return new Response(null, { status: 302, headers: { Location: location, "Cache-Control": "no-store", "Referrer-Policy": "no-referrer", }, }); } export default { async fetch(request, env) { const { pathname } = new URL(request.url); const path = pathname.replace(/\/$/, ""); const fallback = new URL("/#install", request.url).toString(); if (path === "/apk" || path === "/download") { // Order: live Gitea release → manual override → install section. let target = null; try { target = await latestApkUrl(env); } catch { // Gitea unreachable — fall through rather than 500 on a download link. } return redirect(target || env.DOWNLOAD_URL || fallback); } const varName = STATIC_ROUTES[path]; if (varName) return redirect(env[varName] || fallback); return env.ASSETS.fetch(request); }, };