@anthusai/papyrus 1.0.0-next.29 → 1.0.0-next.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { NextResponse } from "next/server";
2
+ import { buildMediaRouteResponse } from "../../../../lib/media-route-response";
2
3
  import { isGuestReadableStoragePath, signStorageUrl } from "../../../../lib/reader-storage-url";
3
4
 
4
5
  export const dynamic = "force-dynamic";
@@ -16,20 +17,8 @@ export async function GET(_request: Request, context: MediaRouteContext) {
16
17
  return NextResponse.json({ error: "Forbidden storage path." }, { status: 403 });
17
18
  }
18
19
 
19
- const signedUrl = await signStorageUrl(storagePath);
20
- const upstream = await fetch(signedUrl, { cache: "no-store" });
21
- if (!upstream.ok) {
22
- return NextResponse.json(
23
- { error: `Upstream media fetch failed (${upstream.status}).` },
24
- { status: upstream.status === 404 ? 404 : 502 },
25
- );
26
- }
27
-
28
- const headers = new Headers();
29
- const contentType = upstream.headers.get("content-type");
30
- if (contentType) {
31
- headers.set("content-type", contentType);
32
- }
33
- headers.set("cache-control", "public, max-age=3600, stale-while-revalidate=86400");
34
- return new NextResponse(upstream.body, { status: 200, headers });
20
+ return buildMediaRouteResponse(storagePath, {
21
+ signStorageUrl,
22
+ fetchUpstream: (signedUrl) => fetch(signedUrl, { cache: "no-store" }),
23
+ });
35
24
  }
@@ -0,0 +1,45 @@
1
+ const PROXIED_IMAGE_EXTENSIONS = new Set(["jpg", "jpeg", "png", "gif", "webp", "avif", "svg"]);
2
+
3
+ export type MediaRouteDependencies = {
4
+ signStorageUrl: (storagePath: string) => Promise<string>;
5
+ fetchUpstream: (signedUrl: string) => Promise<Response>;
6
+ };
7
+
8
+ export function isProxiedImagePath(storagePath: string): boolean {
9
+ const lastSegment = storagePath.split("/").pop() ?? "";
10
+ const dotIndex = lastSegment.lastIndexOf(".");
11
+ if (dotIndex < 0) {
12
+ return false;
13
+ }
14
+ return PROXIED_IMAGE_EXTENSIONS.has(lastSegment.slice(dotIndex + 1).toLowerCase());
15
+ }
16
+
17
+ export async function buildMediaRouteResponse(
18
+ storagePath: string,
19
+ dependencies: MediaRouteDependencies,
20
+ ): Promise<Response> {
21
+ const signedUrl = await dependencies.signStorageUrl(storagePath);
22
+
23
+ if (!isProxiedImagePath(storagePath)) {
24
+ return new Response(null, {
25
+ status: 307,
26
+ headers: { location: signedUrl, "cache-control": "private, max-age=300" },
27
+ });
28
+ }
29
+
30
+ const upstream = await dependencies.fetchUpstream(signedUrl);
31
+ if (!upstream.ok) {
32
+ return Response.json(
33
+ { error: `Upstream media fetch failed (${upstream.status}).` },
34
+ { status: upstream.status === 404 ? 404 : 502 },
35
+ );
36
+ }
37
+
38
+ const headers = new Headers();
39
+ const contentType = upstream.headers.get("content-type");
40
+ if (contentType) {
41
+ headers.set("content-type", contentType);
42
+ }
43
+ headers.set("cache-control", "public, max-age=3600, stale-while-revalidate=86400");
44
+ return new Response(upstream.body, { status: 200, headers });
45
+ }
package/package.json CHANGED
@@ -163,6 +163,7 @@
163
163
  "./lib/markus-ir": "./lib/markus-ir.ts",
164
164
  "./lib/markus-projection": "./lib/markus-projection.ts",
165
165
  "./lib/markus-to-article": "./lib/markus-to-article.ts",
166
+ "./lib/media-route-response": "./lib/media-route-response.ts",
166
167
  "./lib/news-desk-session": "./lib/news-desk-session.ts",
167
168
  "./lib/newspaper-layout": "./lib/newspaper-layout.ts",
168
169
  "./lib/newsroom-articles": "./lib/newsroom-articles.ts",
@@ -294,7 +295,7 @@
294
295
  "optional": true
295
296
  }
296
297
  },
297
- "version": "1.0.0-next.29",
298
+ "version": "1.0.0-next.30",
298
299
  "dependencies": {
299
300
  "@aws-amplify/adapter-nextjs": "^1.7.3",
300
301
  "@aws-sdk/client-cognito-identity-provider": "3.1045.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.0-next.29",
2
+ "version": "1.0.0-next.30",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",