Switch to using patch download proxy url

This commit is contained in:
Jared Schoeny 2026-04-26 00:02:17 -06:00
parent 99f2e05627
commit efe9ea1c72
3 changed files with 54 additions and 1 deletions

View File

@ -1,5 +1,6 @@
import { NextRequest } from "next/server";
import { getMinioClient, PATCHES_BUCKET } from "@/utils/minio/server";
import { buildPatchDownloadUrl } from "@/utils/patches/patch-download-url";
import { createClient } from "@/utils/supabase/server";
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
@ -45,6 +46,11 @@ export async function GET(req: NextRequest, { params }: { params: Promise<{ id:
return new Response("Not found", { status: 404 });
}
const workerUrl = buildPatchDownloadUrl(patch.filename);
if (workerUrl) {
return Response.redirect(workerUrl, 302);
}
const client = getMinioClient();
const bucket = patch.bucket || PATCHES_BUCKET;
const url = await client.presignedGetObject(bucket, patch.filename, 60 * 5);

View File

@ -2,6 +2,7 @@
import { createClient, createServiceClient } from "@/utils/supabase/server";
import { getMinioClient, PATCHES_BUCKET } from "@/utils/minio/server";
import { buildPatchDownloadUrl } from "@/utils/patches/patch-download-url";
import { isInformationalArchiveHack, canEditAsCreator, canEditAsAdmin } from "@/utils/hack";
import { sendDiscordMessageEmbed } from "@/utils/discord";
import { headers } from "next/headers";
@ -245,8 +246,11 @@ export async function getSignedPatchUrl(slug: string): Promise<{ ok: true; url:
return { ok: false, error: "Patch not found" };
}
// Sign the URL server-side
try {
const workerUrl = buildPatchDownloadUrl(patch.filename);
if (workerUrl) {
return { ok: true, url: workerUrl };
}
const client = getMinioClient();
const bucket = patch.bucket || PATCHES_BUCKET;
const signedUrl = await client.presignedGetObject(bucket, patch.filename, 60 * 5);
@ -433,6 +437,10 @@ export async function getPatchDownloadUrl(patchId: number): Promise<{ ok: true;
}
try {
const workerUrl = buildPatchDownloadUrl(patch.filename);
if (workerUrl) {
return { ok: true, url: workerUrl };
}
const client = getMinioClient();
const bucket = patch.bucket || PATCHES_BUCKET;
const signedUrl = await client.presignedGetObject(bucket, patch.filename, 60 * 5);

View File

@ -0,0 +1,39 @@
import { createHmac } from "node:crypto";
/** Aligned with Cloudflare Worker `patchTokenJsonPayload` (key order f, e). */
export function patchTokenJsonPayload(filename: string, expiresAtMs: number): string {
return JSON.stringify({ f: filename, e: expiresAtMs });
}
const PATCH_DOWNLOAD_TTL_MS = 5 * 60 * 1000;
/** Wire: base64url(utf8 JSON payload).hex(32-byte HMAC-SHA256), matching Worker `mintPatchToken`. */
export function mintPatchDownloadToken(
filename: string,
expiresAtMs: number,
secret: string
): string {
const buf = Buffer.from(patchTokenJsonPayload(filename, expiresAtMs), "utf8");
const hexMac = createHmac("sha256", secret).update(buf).digest("hex");
return `${buf.toString("base64url")}.${hexMac}`;
}
function trimTrailingSlash(s: string): string {
return s.replace(/\/+$/, "");
}
/**
* If `PATCH_TOKEN_SECRET` and `PATCHES_DOWNLOAD_BASE_URL` are set, returns a Worker URL with `token`.
* Otherwise `null` caller should use Minio `presignedGetObject`.
*/
export function buildPatchDownloadUrl(filename: string): string | null {
const secret = process.env.PATCH_TOKEN_SECRET?.trim();
const base = process.env.PATCHES_DOWNLOAD_BASE_URL?.trim();
if (!secret || !base) return null;
const expiresAtMs = Date.now() + PATCH_DOWNLOAD_TTL_MS;
const token = mintPatchDownloadToken(filename, expiresAtMs, secret);
const u = new URL(filename, `${trimTrailingSlash(base)}/`);
u.searchParams.set("token", token);
console.log("buildPatchDownloadUrl", u.href);
return u.href;
}