mal api route
This commit is contained in:
72
src/routes/api/mal/animelist/[username]/+server.ts
Normal file
72
src/routes/api/mal/animelist/[username]/+server.ts
Normal 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,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user