feat: ship minecraft theme updates and platform workflow improvements

这个提交包含在:
Codex CLI
2026-02-15 17:36:56 +08:00
父节点 cd7540ab9d
当前提交 37266bb846
修改 32 个文件,包含 5297 行新增119 行删除

查看文件

@@ -8,6 +8,16 @@ export const runtime = "nodejs";
const CACHE_DIR = process.env.CSP_IMAGE_CACHE_DIR ?? "/tmp/csp-image-cache";
const MAX_BYTES = 5 * 1024 * 1024;
const IMAGE_EXT_TO_TYPE: Record<string, string> = {
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".webp": "image/webp",
".gif": "image/gif",
".svg": "image/svg+xml",
".bmp": "image/bmp",
".ico": "image/x-icon",
};
function toArrayBuffer(view: Uint8Array): ArrayBuffer {
return view.buffer.slice(
@@ -28,6 +38,25 @@ function pickExt(urlObj: URL, contentType: string): string {
return ".img";
}
function inferImageType(urlObj: URL, contentType: string): string {
const raw = contentType.split(";")[0].trim().toLowerCase();
if (raw.startsWith("image/")) return raw;
const ext = path.extname(urlObj.pathname || "").toLowerCase();
return IMAGE_EXT_TO_TYPE[ext] ?? raw;
}
function looksLikeImage(urlObj: URL, contentType: string): boolean {
if (contentType.startsWith("image/")) return true;
const ext = path.extname(urlObj.pathname || "").toLowerCase();
return Boolean(IMAGE_EXT_TO_TYPE[ext]);
}
function redirectToTarget(target: URL): NextResponse {
const res = NextResponse.redirect(target.toString(), 307);
res.headers.set("Cache-Control", "no-store");
return res;
}
async function readCachedByKey(
key: string
): Promise<{ data: Uint8Array; contentType: string } | null> {
@@ -94,14 +123,12 @@ export async function GET(req: NextRequest) {
});
if (!resp.ok) {
return NextResponse.json(
{ ok: false, error: `fetch image failed: HTTP ${resp.status}` },
{ status: 502 }
);
return redirectToTarget(target);
}
const contentType = (resp.headers.get("content-type") ?? "").toLowerCase();
if (!contentType.startsWith("image/")) {
const headerType = (resp.headers.get("content-type") ?? "").toLowerCase();
const contentType = inferImageType(target, headerType);
if (!looksLikeImage(target, contentType)) {
return NextResponse.json({ ok: false, error: "url is not an image" }, { status: 400 });
}
@@ -125,11 +152,8 @@ export async function GET(req: NextRequest) {
"Cache-Control": "public, max-age=31536000, immutable",
},
});
} catch (e: unknown) {
return NextResponse.json(
{ ok: false, error: `fetch image failed: ${String(e)}` },
{ status: 502 }
);
} catch {
return redirectToTarget(target);
} finally {
clearTimeout(timer);
}