* Prerender public hack pages so anonymous visits can be served from cache.
Signed-in owners and admins still get the session-gated page at the same URL via a cookie-only rewrite.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep the site notice off the static hack page cache.
Signed-in visits to /session redirect to the canonical hack URL; signed-out visits 404.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Prerender the top 750 public hack pages at build time.
Almost the whole catalog is visited within a week, so first-hit generation after deploys is wasted compared to a slightly longer SSG step.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Show the public hack page when session auth is missing.
An expired or leftover cookie was rewriting visitors onto /session and 404ing approved hacks that anonymous users can see.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Note that forks must replace the hardcoded Supabase project ID.
Middleware cookie matchers cannot use env vars, so other communities need to swap in their own project ref.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Opening a screenshot barely grew it on mobile, and Safari stretched every PixelImage. The lightbox now fits the screen by default on phones, with pixel-perfect still available, and WebKit keeps the original aspect ratio.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Help new visitors find the download path on hack pages.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep the download help tab above the mobile action bar shadow.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Give the version chip a pointer cursor.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Make the download help tab a bit larger.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Expand patch download diagnostics
Separate request and body failures, capture response sizes, and sample successful fetches so protocol and truncation patterns can be compared.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Move the details migration after hack review threads.
CI rejects new migration filenames that sort before the tip on main.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
The 500k thank-you stays live in code but only appears when NEXT_PUBLIC_DOWNLOADS_MILESTONE is set, so we can wait for 1M without another copy change later.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Recommend auto-generating patches from a modified ROM.
Manual .bps/.xdelta upload is now a fallback so submitted patches more reliably match the chosen base ROM.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Match generate-mode file pickers to the patch upload control.
Browse stays a distinct button so it does not blend into the filename text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Mark manual patch upload as a fallback with a caution icon.
Makes it clearer that an existing .bps/.xdelta may not match the chosen base ROM.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Approved version uploads now notify only the non-admin Hackdex webhook. Pending submissions still go to the review thread or admin webhook fallback. Also drop the tada emoji from the pending-approval embed.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* Add a Discord review thread and Resend reply loop for submitted hacks.
Admins can email submitters from a forum thread and inbound replies land back in that same thread.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add an admin endpoint to register Discord guild commands.
Local still uses the npm script; production can hit /api/discord/register while logged in as an admin.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add an admin backup to create a missing Discord review thread.
If submit fails to open a thread, an admin can create one from the hack details menu.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Match inbound review replies when the plus-address token is lowercased.
Mail delivers To in lowercase, so tokens are now hex and lookups are case-insensitive.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Use distinct review embeds for /reply and inbound creator mail.
/reply posts a green embed with subject, Discord avatar, and the creator username; thread replies drop the To field.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Gate /reply by Discord role and mark inbound From as verified.
DISCORD_REPLY_ROLE_IDS is required; creator mail still posts when the address does not match the account.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Retry inbound review mail when Discord does not post the embed.
A failed thread post no longer records the email as processed or returns 200 to Resend.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Notify the admin webhook when a review thread cannot be loaded.
Patch upload no longer stays silent if getHackReviewThread throws.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep the /reply ack honest after the review email is sent.
A later Discord or deferred-response failure no longer claims the email failed.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Make the admin review message stand out in the reply email.
The quote sits in a rose-tinted box so it is not just another paragraph.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Tag review threads as claimed or unclaimed and announce claims.
New and backup-created threads get the matching forum tag; dashboard claims swap Unclaimed to Claimed.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Details are now required for every report type. Non-stolen reports can include an optional email so staff can follow up; stolen reports still require email.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
crypto.randomUUID is unavailable on HTTP, which breaks download counting during local development. Production still uses the native UUID.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add xdelta patch support
* Fix potential race on reupload to new format + update typings
* Fix waiting too long to call showSaveFilePicker
* Allow seeing patch format in version history
* Add `hack_patcher_patches` table
* Implement patcher version server actions
* Use first curated patch as default when custom patcher list is active
* Allow saving custom patcher lists with unpublished patch auto-publish
* Keep custom patcher list consistent across archive and new uploads
* Add creator UI for Latest vs Custom patcher version settings
* Add patch version picker to hack page downloader
* Add custom public version names for Custom patcher mode
* Fix direct patch download consistency between Latest vs Custom modes
* Consolidate download and patch to one button press
* Fix rom ready for patching checks
* Fix Select ROM pop-in while base roms loading
* Tighten patcher patches db insertion
* Harden getSignedPatchUrl permission checks
* Fix not using selected patch's filename
* Acknowledge Custom patcher setting in HackPatchForm
* Update types/db.ts
* Refresh discover cache on patch published
* Add migration ordering check to ci.yaml
* Fix new migrations ordering
* Persist Discover state in url query params
* Add button to copy link to current discover state
* Make tags clickable in hack detail page
* Improve CollapsibleTags styling