fix: server-only-Kette aus dem Browser-Bundle lösen
Die Posterkachel zog über lib/images.ts die serverseitige Cache-Logik in den Browser-Bundle, sobald sie aus einer Client-Komponente heraus gerendert wurde — der Build brach ab, sobald eine solche Seite dazukam. Die reine URL-Bildung liegt jetzt in lib/image-url.ts und darf überall verwendet werden; Abruf und Dateicache bleiben hinter server-only.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
import { posterUrl, type ImageSize } from "@/lib/images";
|
import { posterUrl, type ImageSize } from "@/lib/image-url";
|
||||||
import { FlimmerMark } from "@/components/brand/FlimmerLogo";
|
import { FlimmerMark } from "@/components/brand/FlimmerLogo";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { posterUrl } from "@/lib/images";
|
import { posterUrl } from "@/lib/image-url";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streamingdienste als Badges.
|
* Streamingdienste als Badges.
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Bild-URLs und ihre Prüfung.
|
||||||
|
*
|
||||||
|
* Bewusst **ohne** `server-only`: Posterkacheln werden auch aus
|
||||||
|
* Client-Komponenten heraus gerendert (etwa in der sortierbaren Liste eines
|
||||||
|
* Universums). Alles, was tatsächlich den Server braucht — Abruf bei TMDB und
|
||||||
|
* der Dateicache — liegt in `images.ts`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Zugelassene Größen — verhindert, dass über den Parameter beliebige URLs entstehen. */
|
||||||
|
export const IMAGE_SIZES = [
|
||||||
|
"w92",
|
||||||
|
"w154",
|
||||||
|
"w185",
|
||||||
|
"w300",
|
||||||
|
"w342",
|
||||||
|
"w500",
|
||||||
|
"w780",
|
||||||
|
"w1280",
|
||||||
|
"original",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type ImageSize = (typeof IMAGE_SIZES)[number];
|
||||||
|
|
||||||
|
const PATH_PATTERN = /^\/[A-Za-z0-9]{8,64}\.(jpg|jpeg|png|svg|webp)$/;
|
||||||
|
|
||||||
|
export function isValidImagePath(path: string): boolean {
|
||||||
|
return PATH_PATTERN.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isValidSize(size: string): size is ImageSize {
|
||||||
|
return (IMAGE_SIZES as readonly string[]).includes(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** URL des eigenen Bild-Endpunkts — überall im UI statt der TMDB-Adresse benutzen. */
|
||||||
|
export function posterUrl(
|
||||||
|
path: string | null | undefined,
|
||||||
|
size: ImageSize = "w342",
|
||||||
|
): string | null {
|
||||||
|
if (!path || !isValidImagePath(path)) return null;
|
||||||
|
return `/api/image?size=${size}&path=${encodeURIComponent(path)}`;
|
||||||
|
}
|
||||||
+6
-30
@@ -4,6 +4,7 @@ import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { env } from "./env";
|
import { env } from "./env";
|
||||||
import { IMAGE_BASE } from "./tmdb";
|
import { IMAGE_BASE } from "./tmdb";
|
||||||
|
import type { ImageSize } from "./image-url";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lokaler Bild-Zwischenspeicher.
|
* Lokaler Bild-Zwischenspeicher.
|
||||||
@@ -12,32 +13,13 @@ import { IMAGE_BASE } from "./tmdb";
|
|||||||
* eigenen Server ausgeliefert. Das spart Anfragen, macht die Oberfläche auch
|
* eigenen Server ausgeliefert. Das spart Anfragen, macht die Oberfläche auch
|
||||||
* ohne Internetverbindung nutzbar und verhindert, dass jeder Seitenaufruf
|
* ohne Internetverbindung nutzbar und verhindert, dass jeder Seitenaufruf
|
||||||
* einen Abruf bei einem Dritten auslöst.
|
* einen Abruf bei einem Dritten auslöst.
|
||||||
|
*
|
||||||
|
* Die URL-Bildung selbst steht in `image-url.ts` — die brauchen auch
|
||||||
|
* Client-Komponenten, und sie darf deshalb nicht hinter `server-only` liegen.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Zugelassene Größen — verhindert, dass jemand über den Parameter beliebige URLs baut. */
|
export { IMAGE_SIZES, isValidImagePath, isValidSize, posterUrl } from "./image-url";
|
||||||
export const IMAGE_SIZES = [
|
export type { ImageSize } from "./image-url";
|
||||||
"w92",
|
|
||||||
"w154",
|
|
||||||
"w185",
|
|
||||||
"w300",
|
|
||||||
"w342",
|
|
||||||
"w500",
|
|
||||||
"w780",
|
|
||||||
"w1280",
|
|
||||||
"original",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type ImageSize = (typeof IMAGE_SIZES)[number];
|
|
||||||
|
|
||||||
const PATH_PATTERN = /^\/[A-Za-z0-9]{8,64}\.(jpg|jpeg|png|svg|webp)$/;
|
|
||||||
|
|
||||||
export function isValidImagePath(path: string): boolean {
|
|
||||||
return PATH_PATTERN.test(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isValidSize(size: string): size is ImageSize {
|
|
||||||
return (IMAGE_SIZES as readonly string[]).includes(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cacheDir() {
|
function cacheDir() {
|
||||||
return join(env.dataDir, "images");
|
return join(env.dataDir, "images");
|
||||||
@@ -99,9 +81,3 @@ export async function getCachedImage(size: ImageSize, path: string): Promise<Cac
|
|||||||
fromCache: false,
|
fromCache: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** URL des eigenen Bild-Endpunkts — überall im UI statt der TMDB-Adresse benutzen. */
|
|
||||||
export function posterUrl(path: string | null | undefined, size: ImageSize = "w342"): string | null {
|
|
||||||
if (!path || !isValidImagePath(path)) return null;
|
|
||||||
return `/api/image?size=${size}&path=${encodeURIComponent(path)}`;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user