Make avatar imager resilient with upstream fallback everywhere
This commit is contained in:
1 parent
1bbd809b43
commit
1df1ffc3e9
13 files changed
+277
-210
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -6,8 +6,24 @@
|
||||
|
||||
export type { AvatarOptions } from "@/types/admin";
|
||||
|
||||
import type { SyntheticEvent } from "react";
|
||||
import type { AvatarOptions } from "@/types/admin";
|
||||
|
||||
/**
|
||||
* Neutral silhouette (grayscale SVG) shown when even the fallback upstream
|
||||
* cannot produce a render — keeps a broken avatar from showing a broken-image
|
||||
* glyph anywhere on the site.
|
||||
*/
|
||||
export const AVATAR_FALLBACK_SRC =
|
||||
"data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23e2e2e2'/%3E%3Ccircle cx='32' cy='24' r='11' fill='%239d9d9d'/%3E%3Cpath d='M10 60c5-10 13-14 22-14s17 4 22 14z' fill='%239d9d9d'/%3E%3C/svg%3E";
|
||||
|
||||
export function avatarImageOnError(event: SyntheticEvent<HTMLImageElement>) {
|
||||
const img = event.currentTarget;
|
||||
if (img.dataset.fallback) return;
|
||||
img.dataset.fallback = "1";
|
||||
img.src = AVATAR_FALLBACK_SRC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an avatar image URL using the configured imager.
|
||||
* Uses effect=14 and img_format=apng by default for epicnabbo.nl compatibility.
|
||||
|
||||
@@ -42,3 +42,27 @@ it("rejects malformed figure input without contacting a renderer", async () => {
|
||||
).toBe(400);
|
||||
expect(fetcher).not.toHaveBeenCalled();
|
||||
});
|
||||
it("falls back to the public Habbo renderer when the configured imager is down", async () => {
|
||||
const fetcher = vi.fn(async (input: string | URL) => {
|
||||
const url = new URL(String(input));
|
||||
if (url.origin === "https://hotel.test")
|
||||
return new Response("unavailable", { status: 503 });
|
||||
return new Response(new Uint8Array([4, 5, 6]), {
|
||||
headers: { "content-type": "image/png" },
|
||||
});
|
||||
});
|
||||
vi.stubGlobal("fetch", fetcher);
|
||||
vi.stubEnv("IMAGER_URL", "https://hotel.test/avatarimage");
|
||||
const result = await GET(
|
||||
new NextRequest(
|
||||
"https://hotel.test/api/imaging/avatar?figure=hd-180-1&img_format=apng&effect=0",
|
||||
),
|
||||
);
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.headers.get("x-imager-source")).toBe("fallback");
|
||||
expect(result.headers.get("cache-control")).toContain("max-age=300");
|
||||
const target = new URL(String(fetcher.mock.calls.at(-1)?.[0]));
|
||||
expect(target.host).toBe("www.habbo.com");
|
||||
expect(target.searchParams.get("img_format")).toBe("png");
|
||||
expect(target.searchParams.has("effect")).toBe(false);
|
||||
});
|
||||
Reference in new issue
Block a user