mal api route

This commit is contained in:
2026-02-05 14:37:44 -08:00
parent b2bb1439ee
commit c3d2295ed1

View File

@@ -0,0 +1,72 @@
import { error } from "@sveltejs/kit";
import { env } from "$env/dynamic/private";
import type { RequestHandler } from "./$types";
const UPSTREAM_ORIGIN = "https://api.myanimelist.net/v2";
function pickForwardHeader(request: Request, headerName: string) {
const v = request.headers.get(headerName);
return v ?? undefined;
}
export const GET: RequestHandler = async ({ params, request, fetch, url }) => {
const username = params.username;
if (!username) {
throw error(400, "Missing username");
}
if (!env.MAL_CLIENT_ID) {
throw error(500, "MAL_CLIENT_ID is not configured");
}
// Forward query params as-is (status/sort/limit/offset/etc).
// Note: url.searchParams is already decoded/encoded appropriately by URL.
const upstreamUrl = new URL(
`${UPSTREAM_ORIGIN}/users/${encodeURIComponent(username)}/animelist`,
);
upstreamUrl.search = url.search;
const ifNoneMatch = pickForwardHeader(request, "if-none-match");
const ifModifiedSince = pickForwardHeader(request, "if-modified-since");
const upstreamRes = await fetch(upstreamUrl, {
method: "GET",
headers: {
"X-MAL-CLIENT-ID": env.MAL_CLIENT_ID,
...(ifNoneMatch ? { "if-none-match": ifNoneMatch } : {}),
...(ifModifiedSince ? { "if-modified-since": ifModifiedSince } : {}),
},
});
// Map common statuses cleanly
if (upstreamRes.status === 401 || upstreamRes.status === 403) {
throw error(upstreamRes.status, "MAL auth failed");
}
if (upstreamRes.status === 404) {
throw error(404, "User not found");
}
if (!upstreamRes.ok && upstreamRes.status !== 304) {
throw error(upstreamRes.status, `Upstream error (${upstreamRes.status})`);
}
// Pass through caching/content headers that are useful and safe
const headers = new Headers();
const copy = (h: string) => {
const v = upstreamRes.headers.get(h);
if (v) headers.set(h, v);
};
copy("content-type");
copy("cache-control");
copy("etag");
copy("last-modified");
return new Response(upstreamRes.body, {
status: upstreamRes.status,
statusText: upstreamRes.statusText,
headers,
});
};