From 762ee874a032f9c86546e4e232b6eadfe3e6c99a Mon Sep 17 00:00:00 2001 From: ic3w0lf Date: Tue, 3 Jun 2025 20:03:09 -0600 Subject: [PATCH 1/4] thumbnail fix - will this WORK THIS TIME? --- web/public/errors/404.png | Bin 0 -> 31021 bytes web/public/errors/500.png | Bin 0 -> 35908 bytes web/src/app/lib/errorImageResponse.ts | 28 ++++++++++++++++++ .../app/thumbnails/asset/[assetId]/route.tsx | 8 ++--- web/src/app/thumbnails/maps/[mapId]/route.tsx | 16 ++++++---- 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 web/public/errors/404.png create mode 100644 web/public/errors/500.png create mode 100644 web/src/app/lib/errorImageResponse.ts diff --git a/web/public/errors/404.png b/web/public/errors/404.png new file mode 100644 index 0000000000000000000000000000000000000000..0eaf9d0329373c62532aac0aa23c7d65bb383e8c GIT binary patch literal 31021 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 z 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 literal 0 HcmV?d00001 diff --git a/web/public/errors/500.png b/web/public/errors/500.png new file mode 100644 index 0000000000000000000000000000000000000000..cd83dd8fd328431f8d3879384c14dfd5b71ffa4a GIT binary patch literal 35908 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 NukvE&uH5|fe*te+2D$(M literal 0 HcmV?d00001 diff --git a/web/src/app/lib/errorImageResponse.ts b/web/src/app/lib/errorImageResponse.ts new file mode 100644 index 0000000..29302b8 --- /dev/null +++ b/web/src/app/lib/errorImageResponse.ts @@ -0,0 +1,28 @@ +import path from 'path' +import { promises as fs } from 'fs' + +export async function errorImageResponse(statusCode: number = 500): Promise { + const file = `${statusCode}.png` + const filePath = path.join(process.cwd(), 'public/errors', file) + + try { + const buffer = await fs.readFile(filePath) + return new Response(buffer, { + status: statusCode, + headers: { + 'Content-Type': 'image/png', + 'Content-Length': buffer.length.toString(), + }, + }) + } catch { + const fallback = path.join(process.cwd(), 'public/errors', '500.png') + const buffer = await fs.readFile(fallback) + return new Response(buffer, { + status: 500, + headers: { + 'Content-Type': 'image/png', + 'Content-Length': buffer.length.toString(), + }, + }) + } +} \ No newline at end of file diff --git a/web/src/app/thumbnails/asset/[assetId]/route.tsx b/web/src/app/thumbnails/asset/[assetId]/route.tsx index e7232d8..841ba44 100644 --- a/web/src/app/thumbnails/asset/[assetId]/route.tsx +++ b/web/src/app/thumbnails/asset/[assetId]/route.tsx @@ -49,7 +49,7 @@ export async function GET( ); if (!response.ok) { - throw new Error('Failed to fetch thumbnail JSON'); + throw new Error(`Failed to fetch thumbnail JSON [${response.status}]`) } const data = await response.json(); @@ -64,7 +64,7 @@ export async function GET( const imageResponse = await fetch(imageUrl); if (!imageResponse.ok) { - throw new Error('Failed to fetch the image'); + throw new Error(`Failed to fetch the image [${imageResponse.status}]`) } const arrayBuffer = await imageResponse.arrayBuffer(); @@ -79,9 +79,9 @@ export async function GET( 'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`, }, }); - } catch { + } catch (err) { return NextResponse.json( - { error: 'Failed to fetch or process thumbnail' }, + { error: `Failed to fetch or process thumbnail: ${err}` }, { status: 500 } ); } diff --git a/web/src/app/thumbnails/maps/[mapId]/route.tsx b/web/src/app/thumbnails/maps/[mapId]/route.tsx index a923d21..a6b21d3 100644 --- a/web/src/app/thumbnails/maps/[mapId]/route.tsx +++ b/web/src/app/thumbnails/maps/[mapId]/route.tsx @@ -1,16 +1,20 @@ -import { NextRequest, NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server" export async function GET( request: NextRequest, context: { params: Promise<{ mapId: string }> } ): Promise { // TODO: implement this, we need a cdn for in-game map thumbnails... + + if (!process.env.API_HOST) { + throw new Error('env variable "API_HOST" is not set') + } - const { mapId } = await context.params; + const { mapId } = await context.params - const protocol = request.headers.get("x-forwarded-proto") || "https"; - const host = request.headers.get("host"); - const origin = `${protocol}://${host}`; + const apiHost = process.env.API_HOST.replace(/\/api\/?$/, "") + const redirectPath = `/thumbnails/asset/${mapId}` + const redirectUrl = `${apiHost}${redirectPath}` - return NextResponse.redirect(`${origin}/thumbnails/asset/${mapId}`); + return NextResponse.redirect(redirectUrl) } \ No newline at end of file -- 2.49.1 From e1fc6376195a44016631c61473b68dc6831d81a9 Mon Sep 17 00:00:00 2001 From: ic3w0lf Date: Tue, 3 Jun 2025 20:42:37 -0600 Subject: [PATCH 2/4] Implement errorImageResponse --- .../app/thumbnails/asset/[assetId]/route.tsx | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/web/src/app/thumbnails/asset/[assetId]/route.tsx b/web/src/app/thumbnails/asset/[assetId]/route.tsx index 841ba44..c3f39f5 100644 --- a/web/src/app/thumbnails/asset/[assetId]/route.tsx +++ b/web/src/app/thumbnails/asset/[assetId]/route.tsx @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; +import { errorImageResponse } from '@/app/lib/errorImageResponse'; const cache = new Map(); const CACHE_TTL = 15 * 60 * 1000; @@ -10,10 +11,9 @@ export async function GET( const { assetId } = await context.params; if (!assetId) { - return NextResponse.json( - { error: 'Missing asset ID' }, - { status: 400 } - ); + return errorImageResponse(400, { + message: "Missing asset ID", + }) } let finalAssetId = assetId; @@ -31,17 +31,17 @@ export async function GET( } catch { } const now = Date.now(); - const cached = cache.get(finalAssetId); + const cached = cache.get(finalAssetId); - if (cached && cached.expires > now) { - return new NextResponse(cached.buffer, { - headers: { - 'Content-Type': 'image/png', - 'Content-Length': cached.buffer.length.toString(), - 'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`, - }, - }); - } + if (cached && cached.expires > now) { + return new NextResponse(cached.buffer, { + headers: { + 'Content-Type': 'image/png', + 'Content-Length': cached.buffer.length.toString(), + 'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`, + }, + }); + } try { const response = await fetch( @@ -56,10 +56,9 @@ export async function GET( const imageUrl = data.data[0]?.imageUrl; if (!imageUrl) { - return NextResponse.json( - { error: 'No image URL found in the response' }, - { status: 404 } - ); + return errorImageResponse(404, { + message: "No image URL found in the response", + }) } const imageResponse = await fetch(imageUrl); @@ -80,9 +79,8 @@ export async function GET( }, }); } catch (err) { - return NextResponse.json( - { error: `Failed to fetch or process thumbnail: ${err}` }, - { status: 500 } - ); + return errorImageResponse(500, { + message: `Failed to fetch or process thumbnail: ${err}`, + }) } } \ No newline at end of file -- 2.49.1 From 8d5bd9e523306fadb08e4892026046b78de2a879 Mon Sep 17 00:00:00 2001 From: ic3w0lf Date: Tue, 3 Jun 2025 20:52:43 -0600 Subject: [PATCH 3/4] Fix error & include error message in response headers --- web/src/app/lib/errorImageResponse.ts | 47 +++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/web/src/app/lib/errorImageResponse.ts b/web/src/app/lib/errorImageResponse.ts index 29302b8..63b0ac0 100644 --- a/web/src/app/lib/errorImageResponse.ts +++ b/web/src/app/lib/errorImageResponse.ts @@ -1,28 +1,35 @@ -import path from 'path' -import { promises as fs } from 'fs' +import path from 'path'; +import { promises as fs } from 'fs'; +import { NextResponse } from 'next/server'; -export async function errorImageResponse(statusCode: number = 500): Promise { - const file = `${statusCode}.png` - const filePath = path.join(process.cwd(), 'public/errors', file) +export async function errorImageResponse( + statusCode: number = 500, + options?: { message?: string } +): Promise { + const file = `${statusCode}.png`; + const filePath = path.join(process.cwd(), 'public/errors', file); + + const headers: Record = { + 'Content-Type': 'image/png', + }; + if (options?.message) { + headers['X-Error-Message'] = encodeURIComponent(options.message); + } try { - const buffer = await fs.readFile(filePath) - return new Response(buffer, { + const buffer = await fs.readFile(filePath); + headers['Content-Length'] = buffer.length.toString(); + return new NextResponse(buffer, { status: statusCode, - headers: { - 'Content-Type': 'image/png', - 'Content-Length': buffer.length.toString(), - }, - }) + headers, + }); } catch { - const fallback = path.join(process.cwd(), 'public/errors', '500.png') - const buffer = await fs.readFile(fallback) - return new Response(buffer, { + const fallback = path.join(process.cwd(), 'public/errors', '500.png'); + const buffer = await fs.readFile(fallback); + headers['Content-Length'] = buffer.length.toString(); + return new NextResponse(buffer, { status: 500, - headers: { - 'Content-Type': 'image/png', - 'Content-Length': buffer.length.toString(), - }, - }) + headers, + }); } } \ No newline at end of file -- 2.49.1 From 3f848a35c86b166ec9cdf5ccaa3d96ce1b7d46c1 Mon Sep 17 00:00:00 2001 From: ic3w0lf Date: Thu, 5 Jun 2025 17:42:34 -0600 Subject: [PATCH 4/4] implement cache de-exister --- web/src/app/thumbnails/asset/[assetId]/route.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/web/src/app/thumbnails/asset/[assetId]/route.tsx b/web/src/app/thumbnails/asset/[assetId]/route.tsx index c3f39f5..7c02e75 100644 --- a/web/src/app/thumbnails/asset/[assetId]/route.tsx +++ b/web/src/app/thumbnails/asset/[assetId]/route.tsx @@ -4,6 +4,15 @@ import { errorImageResponse } from '@/app/lib/errorImageResponse'; const cache = new Map(); const CACHE_TTL = 15 * 60 * 1000; +setInterval(() => { + const now = Date.now(); + for (const [key, value] of cache.entries()) { + if (value.expires <= now) { + cache.delete(key); + } + } +}, 60 * 5 * 1000); + export async function GET( request: NextRequest, context: { params: Promise<{ assetId: number }> } -- 2.49.1