diff --git a/src/app/api/card/[username]/route.test.ts b/src/app/api/card/[username]/route.test.ts index cc8adb95..3793be9f 100644 --- a/src/app/api/card/[username]/route.test.ts +++ b/src/app/api/card/[username]/route.test.ts @@ -1,3 +1,4 @@ +import { NextRequest } from "next/server"; import { describe, expect, it, vi } from "vitest"; vi.mock("@/lib/cardDataFetcher", () => ({ @@ -23,7 +24,7 @@ describe("GET /api/card/[username] cache headers", () => { }); const { GET } = await import("./route"); - const req = new Request("http://localhost/api/card/alice"); + const req = new NextRequest("http://localhost/api/card/alice"); const response = await GET(req, { params: Promise.resolve({ username: "alice" }) }); expect(response.headers.get("Cache-Control")).toBe("public, s-maxage=1800, stale-while-revalidate=3600"); @@ -34,7 +35,7 @@ describe("GET /api/card/[username] cache headers", () => { vi.mocked(fetchCardData).mockResolvedValueOnce(null); const { GET } = await import("./route"); - const req = new Request("http://localhost/api/card/ghost"); + const req = new NextRequest("http://localhost/api/card/ghost"); const response = await GET(req, { params: Promise.resolve({ username: "ghost" }) }); expect(response.status).toBe(404); @@ -46,7 +47,7 @@ describe("GET /api/card/[username] cache headers", () => { vi.mocked(fetchCardData).mockRejectedValueOnce(new Error("API Error")); const { GET } = await import("./route"); - const req = new Request("http://localhost/api/card/erroruser"); + const req = new NextRequest("http://localhost/api/card/erroruser"); const response = await GET(req, { params: Promise.resolve({ username: "erroruser" }) }); expect(response.status).toBe(503); @@ -66,7 +67,7 @@ describe("GET /api/card/[username] error responses", () => { } const { GET } = await import("./route"); - const req = new Request(`http://localhost/api/card/${username}`); + const req = new NextRequest(`http://localhost/api/card/${username}`); await GET(req, { params: Promise.resolve({ username }) }); expect(renderErrorCardResponse).toHaveBeenCalledWith(expect.objectContaining({ @@ -92,7 +93,7 @@ describe("GET /api/card/[username] rate limiting", () => { const { fetchCardData } = await import("@/lib/cardDataFetcher"); const { renderErrorCardResponse } = await import("@/lib/cardRenderer"); - const req1 = new Request("http://localhost/api/card/testuser", { + const req1 = new NextRequest("http://localhost/api/card/testuser", { headers: { "x-forwarded-for": "127.0.0.1", }, diff --git a/src/app/api/card/[username]/route.ts b/src/app/api/card/[username]/route.ts index f8cd2111..f2eac0e7 100644 --- a/src/app/api/card/[username]/route.ts +++ b/src/app/api/card/[username]/route.ts @@ -9,8 +9,10 @@ const rateLimiter = new RateLimiter(50, 60 * 1000); // 50 requests per minute const SUCCESS_CACHE = "public, s-maxage=1800, stale-while-revalidate=3600"; const ERROR_CACHE = "public, s-maxage=60, stale-while-revalidate=120"; +import { NextRequest } from "next/server"; + export async function GET( - request: Request, + request: NextRequest, { params }: { params: Promise<{ username: string }> } ): Promise { const { username } = await params; @@ -19,7 +21,8 @@ export async function GET( const allowedOrigin = process.env.APP_URL || "http://localhost:3000"; const fontUrl = `${allowedOrigin}/fonts/NotoSans-Regular.ttf`; - const ip = request.headers.get("x-forwarded-for") ?? "unknown"; + const forwarded = request.headers.get("x-forwarded-for"); + const ip = forwarded ? forwarded.split(",")[0]?.trim() ?? "unknown" : "unknown"; const rateLimitResult = rateLimiter.check(ip); if (!rateLimitResult.success) { diff --git a/src/app/api/og/[username]/route.test.ts b/src/app/api/og/[username]/route.test.ts index 76ea55a1..b6360b10 100644 --- a/src/app/api/og/[username]/route.test.ts +++ b/src/app/api/og/[username]/route.test.ts @@ -33,8 +33,8 @@ describe("OG Image Route", () => { }); it("should generate image for valid username", async () => { - const mockFetch = vi.spyOn(global, "fetch").mockResolvedValue( - new Response(JSON.stringify({ name: "Valid User" }), { status: 200 }) + const mockFetch = vi.spyOn(global, "fetch").mockImplementation(() => + Promise.resolve(new Response(JSON.stringify({ name: "Valid User" }), { status: 200 })) ); const req = new NextRequest("http://localhost/api/og/validuser"); @@ -47,8 +47,8 @@ describe("OG Image Route", () => { }); it("should return 429 and Retry-After header when rate limit is exceeded", async () => { - const mockFetch = vi.spyOn(global, "fetch").mockResolvedValue( - new Response(JSON.stringify({ name: "Valid User" }), { status: 200 }) + const mockFetch = vi.spyOn(global, "fetch").mockImplementation(() => + Promise.resolve(new Response(JSON.stringify({ name: "Valid User" }), { status: 200 })) ); // Generate more than 50 requests to hit the rate limit (limit is 50 per minute) diff --git a/src/app/api/og/[username]/route.tsx b/src/app/api/og/[username]/route.tsx index bfc09e8b..1f3b3eb4 100644 --- a/src/app/api/og/[username]/route.tsx +++ b/src/app/api/og/[username]/route.tsx @@ -18,7 +18,7 @@ export async function GET( const { username } = await params; const forwarded = request.headers.get("x-forwarded-for"); - const ip = forwarded ? forwarded.split(",").at(-1)?.trim() ?? "unknown" : "unknown"; + const ip = forwarded ? forwarded.split(",")[0]?.trim() ?? "unknown" : "unknown"; const rateLimitResult = rateLimiter.check(ip); if (!rateLimitResult.success) {