API_HOST changes, thumbnail fix & cache, "list is empty" fix

API_HOST was replaced in order for thumbnail/any redirects to work properly, this also assumes the API will be at `{BASE_URL}/api`, assuming the reverse proxy causes issues with the way redirects were initially setup to work.

Also no more "Submissions list is empty." while it's loading.
This commit is contained in:
ic3w0lf
2025-06-03 15:58:33 -06:00
parent d39a8c0208
commit 740e3c8932
6 changed files with 53 additions and 28 deletions

View File

@@ -26,10 +26,10 @@ Prerequisite: golang installed
Prerequisite: bun installed
The environment variables `API_HOST` and `AUTH_HOST` will need to be set for the middleware.
The environment variables `BASE_URL` and `AUTH_HOST` will need to be set for the middleware.
Example `.env` in web's root:
```
API_HOST="http://localhost:8082/v1/"
BASE_URL="http://localhost:8082/"
AUTH_HOST="http://localhost:8083/"
```

View File

@@ -11,7 +11,7 @@ import "./(styles)/page.scss";
import { ListSortConstants } from "../ts/Sort";
export default function MapfixInfoPage() {
const [mapfixes, setMapfixes] = useState<MapfixList>({Total:0,Mapfixes:[]})
const [mapfixes, setMapfixes] = useState<MapfixList>({Total:-1,Mapfixes:[]})
const [currentPage, setCurrentPage] = useState(1);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
@@ -47,7 +47,7 @@ export default function MapfixInfoPage() {
}, 50);
}, [currentPage])
if (!mapfixes) {
if (mapfixes.Total < 0) {
return <Webpage>
<main>
Loading...
@@ -55,7 +55,7 @@ export default function MapfixInfoPage() {
</Webpage>
}
if (mapfixes && mapfixes.Total == 0) {
if (mapfixes.Total == 0) {
return <Webpage>
<main>
Mapfixes list is empty.

View File

@@ -9,7 +9,7 @@ import "./(styles)/page.scss";
import { ListSortConstants } from "../ts/Sort";
export default function SubmissionInfoPage() {
const [submissions, setSubmissions] = useState<SubmissionList>({Total:0,Submissions:[]})
const [submissions, setSubmissions] = useState<SubmissionList>({Total:-1,Submissions:[]})
const [currentPage, setCurrentPage] = useState(1);
const cardsPerPage = 24; // built to fit on a 1920x1080 monitor
@@ -45,7 +45,7 @@ export default function SubmissionInfoPage() {
}, 50);
}, [currentPage])
if (!submissions) {
if (submissions.Total < 0) {
return <Webpage>
<main>
Loading...
@@ -53,7 +53,7 @@ export default function SubmissionInfoPage() {
</Webpage>
}
if (submissions && submissions.Total == 0) {
if (submissions.Total == 0) {
return <Webpage>
<main>
Submissions list is empty.

View File

@@ -1,5 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
const cache = new Map<number, { buffer: Buffer; expires: number }>();
const CACHE_TTL = 15 * 60 * 1000;
export async function GET(
request: NextRequest,
context: { params: Promise<{ assetId: number }> }
@@ -27,6 +30,19 @@ export async function GET(
}
} catch { }
const now = Date.now();
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}`,
},
});
}
try {
const response = await fetch(
`https://thumbnails.roblox.com/v1/assets?format=png&size=512x512&assetIds=${finalAssetId}`
@@ -54,10 +70,13 @@ export async function GET(
const arrayBuffer = await imageResponse.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
cache.set(finalAssetId, { buffer, expires: now + CACHE_TTL });
return new NextResponse(buffer, {
headers: {
'Content-Type': 'image/png',
'Content-Length': buffer.length.toString(),
'Cache-Control': `public, max-age=${CACHE_TTL / 1000}`,
},
});
} catch {

View File

@@ -5,9 +5,12 @@ export async function GET(
context: { params: Promise<{ mapId: string }> }
): Promise<NextResponse> {
// TODO: implement this, we need a cdn for in-game map thumbnails...
const { mapId } = await context.params;
const baseUrl = request.nextUrl.origin; // Gets the current base URL
return NextResponse.redirect(`${baseUrl}/thumbnails/asset/${mapId}`);
const protocol = request.headers.get("x-forwarded-proto") || "https";
const host = request.headers.get("host");
const origin = `${protocol}://${host}`;
return NextResponse.redirect(`${origin}/thumbnails/asset/${mapId}`);
}

View File

@@ -1,29 +1,32 @@
import { NextRequest, NextResponse } from "next/server"
export const config = {
matcher: ["/api/:path*", "/auth/:path*"],
matcher: ["/api/:path*", "/auth/:path*"],
}
export function middleware(request: NextRequest) {
const { pathname, search } = request.nextUrl
const { pathname, search } = request.nextUrl
if (pathname.startsWith("/api")) {
if (!process.env.API_HOST) {
throw new Error('env variable "API_HOST" is not set')
}
const apiUrl = new URL(process.env.API_HOST + pathname.replace(/^\/api/, '') + search)
return NextResponse.rewrite(apiUrl, { request })
} else if (pathname.startsWith("/auth")) {
if (pathname.startsWith("/api")) {
if (!process.env.BASE_URL) {
throw new Error('env variable "BASE_URL" is not set')
}
const baseUrl = process.env.BASE_URL.replace(/\/$/, "");
const apiUrl = new URL(baseUrl + pathname + search);
return NextResponse.rewrite(apiUrl, { request });
} else if (pathname.startsWith("/auth")) {
if (!process.env.AUTH_HOST) {
throw new Error('env variable "AUTH_HOST" is not set')
}
const authHost = process.env.AUTH_HOST.replace(/\/$/, "")
const path = pathname.replace(/^\/auth/, "")
const redirectUrl = new URL(authHost + path + search)
return NextResponse.redirect(redirectUrl, 302)
}
const authHost = process.env.AUTH_HOST.replace(/\/$/, "");
const path = pathname.replace(/^\/auth/, "");
const redirectUrl = new URL(authHost + path + search);
return NextResponse.next()
return NextResponse.redirect(redirectUrl, 302);
}
return NextResponse.next()
}