feat(website): direct APK download via /download route handler#269
Conversation
Replaces the four Download APK buttons (Hero, Closing CTA, desktop navbar, mobile drawer) that previously navigated to the GitHub releases page with a single Route Handler at /download that resolves the latest release's .apk asset at request time and 302-redirects to it. The user clicks Download APK and the browser kicks off a normal file download without leaving the site visually. Why not a static link GitHub's `/releases/latest/download/<filename>` redirect requires the asset name to be stable across releases. Our APKs are named `PocketNode-v<version>.apk`, so the name shifts every cut — a static link would go stale on every release. The handler asks the GitHub API for the latest release, picks the .apk asset, and redirects to its `browser_download_url` which always points at the current cut. Caching The redirect itself is edge-cached on Vercel for 10 minutes with stale-while-revalidate of 1 hour, so the GitHub API quota (60 requests/hour anonymous) is not a bottleneck even during a launch spike. New releases propagate inside ~10 minutes worst case. Failure mode If the GitHub API fails (rate limit, network blip, malformed response) the handler falls back to redirecting to the GitHub release page so the user can still grab the APK by hand. No 500s. Build verified: /download shows as `ƒ` (Dynamic, server-rendered) in the route table; the other 6 routes still prerender static.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughA new internal ChangesCentralized APK Download Routing
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/app/download/route.ts`:
- Line 65: The current asset selection uses a loose find: apkAsset =
release.assets?.find(asset => asset.name.endsWith('.apk')), which can pick the
wrong APK when multiple APKs exist; update the matching to target the exact
filename contract PocketNode-v<version>.apk instead. Extract or derive the
release version (e.g., from release.tag_name or the version variable) and
replace the endsWith check with a strict match (either equality to
`PocketNode-v${version}.apk` or a regex anchored to
`^PocketNode-v${escapedVersion}\.apk$`) when calling release.assets?.find so
only the intended artifact is selected before redirecting.
- Line 86: The fallback redirect currently returns
NextResponse.redirect(FALLBACK_URL, { status: 302 }) with no caching, causing
repeated upstream calls; update the fallback branch that calls
NextResponse.redirect to attach a short Cache-Control header (for example
Cache-Control: public, max-age=60 or similar) so the 302 fallback response is
cached briefly when GitHub is degraded; locate the redirect call that references
FALLBACK_URL and NextResponse.redirect and add headers to the response before
returning it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: be24868a-3d41-4a7f-abfc-ef93c5516c98
📒 Files selected for processing (4)
website/app/download/route.tswebsite/app/page.tsxwebsite/components/Hero.tsxwebsite/components/Navbar.tsx
| } | ||
|
|
||
| const release = (await response.json()) as LatestRelease | ||
| const apkAsset = release.assets?.find((asset) => asset.name.endsWith('.apk')) |
There was a problem hiding this comment.
Tighten APK asset matching to avoid redirecting to the wrong artifact.
Line 65 currently picks the first *.apk, which is ambiguous if a release includes multiple APKs (e.g., debug/universal/split builds). Match the intended filename contract (PocketNode-v<version>.apk) explicitly before redirecting.
Proposed fix
- const apkAsset = release.assets?.find((asset) => asset.name.endsWith('.apk'))
+ const apkAsset = release.assets?.find((asset) =>
+ /^PocketNode-v\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?\.apk$/.test(asset.name)
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const apkAsset = release.assets?.find((asset) => asset.name.endsWith('.apk')) | |
| const apkAsset = release.assets?.find((asset) => | |
| /^PocketNode-v\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?\.apk$/.test(asset.name) | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/app/download/route.ts` at line 65, The current asset selection uses a
loose find: apkAsset = release.assets?.find(asset =>
asset.name.endsWith('.apk')), which can pick the wrong APK when multiple APKs
exist; update the matching to target the exact filename contract
PocketNode-v<version>.apk instead. Extract or derive the release version (e.g.,
from release.tag_name or the version variable) and replace the endsWith check
with a strict match (either equality to `PocketNode-v${version}.apk` or a regex
anchored to `^PocketNode-v${escapedVersion}\.apk$`) when calling
release.assets?.find so only the intended artifact is selected before
redirecting.
| // we send the user to the GitHub release page rather than show | ||
| // them a 500. They can grab the .apk by hand from there. | ||
| console.error('Failed to resolve latest APK asset:', error) | ||
| return NextResponse.redirect(FALLBACK_URL, { status: 302 }) |
There was a problem hiding this comment.
Cache fallback redirects as well to reduce repeated failing upstream calls.
When GitHub is degraded, every /download request re-hits the API before falling back. Adding a short Cache-Control on the fallback redirect improves resilience and latency during incidents.
Proposed fix
- return NextResponse.redirect(FALLBACK_URL, { status: 302 })
+ return NextResponse.redirect(FALLBACK_URL, {
+ status: 302,
+ headers: {
+ 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
+ },
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return NextResponse.redirect(FALLBACK_URL, { status: 302 }) | |
| return NextResponse.redirect(FALLBACK_URL, { | |
| status: 302, | |
| headers: { | |
| 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600', | |
| }, | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/app/download/route.ts` at line 86, The fallback redirect currently
returns NextResponse.redirect(FALLBACK_URL, { status: 302 }) with no caching,
causing repeated upstream calls; update the fallback branch that calls
NextResponse.redirect to attach a short Cache-Control header (for example
Cache-Control: public, max-age=60 or similar) so the 302 fallback response is
cached briefly when GitHub is degraded; locate the redirect call that references
FALLBACK_URL and NextResponse.redirect and add headers to the response before
returning it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb05faec14
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| assets?: ReleaseAsset[] | ||
| } | ||
|
|
||
| export const dynamic = 'force-dynamic' |
There was a problem hiding this comment.
Remove force-dynamic so upstream fetch can be cached
The dynamic = 'force-dynamic' segment config forces this route’s fetch() calls to run as no-store, so the next: { revalidate: 600 } option below is effectively negated. That means every uncached /download invocation can hit api.github.com, which risks tripping GitHub’s anonymous rate limit and pushing users into the fallback redirect instead of a direct APK download. Please use a cache-compatible segment setting (or drop force-dynamic) so the 10-minute upstream cache policy actually applies.
Useful? React with 👍 / 👎.
Summary
Replace the four Download APK buttons (Hero, Closing CTA, desktop navbar, mobile drawer) that navigated to the GitHub releases page with a single Route Handler at
/downloadthat resolves the latest release's .apk asset at request time and 302-redirects to it.Click flow: user taps Download APK → browser hits
/download→ server-side handler queries the GitHub API → 302 redirect tohttps://github.com/.../releases/download/v<X.Y.Z>/PocketNode-v<X.Y.Z>.apk→ browser starts the file download.Why not a static link
GitHub's
/releases/latest/download/<filename>redirect requires the asset name to be stable across releases. Our APKs are namedPocketNode-v<version>.apk, so a static link would go stale on every release. The handler always points at the current cut.Caching
Cache-Control: public, s-maxage=600, stale-while-revalidate=3600fetchto api.github.com:next.revalidate: 600GitHub's anonymous API quota is 60 requests/hour. With a 10-minute cache window the website would need 60 distinct edge nodes to hit the limit, which is well above typical traffic.
Failure mode
If the GitHub API call fails for any reason (rate limit, network blip, malformed response) the handler falls back to redirecting to the GitHub release page. No 500s; users can still grab the APK manually.
Test plan
npm run buildshows/downloadasƒ(dynamic), other 6 routes still staticPocketNode-v<latest>.apkreleases/latestinstead of an error pageSummary by CodeRabbit
New Features
Improvements