Repository Public MIT
1import { createHash } from "node:crypto";2import { EXAMPLE_LIMITS, imageKindForPath, readArchive, sniffImage, type ExampleClient, type ManifestExample } from "@skills/registry-core";3import { ApiError, notFound } from "./errors";4import { env } from "./env";5import type { ArtifactRow, ReleaseRow } from "./registry";6import { readBlob } from "./storage";78/**9 * Usage examples (screenshots) of a release. The only files ever served are the ones the release10 * manifest lists under `examples`, read from the immutable archive and re-checked as raster images.11 * Nothing is rendered, resized or executed on the server.12 */13export interface ExampleMedia {14 path: string;15 mime: string;16 width: number;17 height: number;18 data: Buffer;19 etag: string;20}2122interface Entry {23 media: Map<string, ExampleMedia>;24 bytes: number;25}26const g = globalThis as unknown as { __skillsMedia?: Map<string, Entry> };27const cache = (g.__skillsMedia ??= new Map());28const MAX_CACHE_BYTES = 96 * 1024 * 1024;2930function listed(r: ReleaseRow): ManifestExample[] {31 const ex = r.manifest_json?.examples;32 return Array.isArray(ex) ? ex.slice(0, EXAMPLE_LIMITS.maxExamples) : [];33}3435/** Example images of one release, keyed by archive path. Cached by archive digest (content never changes). */36function loadMedia(r: ReleaseRow): Map<string, ExampleMedia> {37 const wanted = new Set(listed(r).map((e) => e.image));38 if (wanted.size === 0) return new Map();39 const hit = cache.get(r.sha256);40 if (hit) {41 cache.delete(r.sha256);42 cache.set(r.sha256, hit); // most recently used last43 return hit.media;44 }45 const media = new Map<string, ExampleMedia>();46 let bytes = 0;47 for (const f of readArchive(readBlob(r.archive_object_key))) {48 if (!wanted.has(f.path) || f.data.length > EXAMPLE_LIMITS.maxImageBytes) continue;49 const info = sniffImage(f.data);50 if (!info || info.kind !== imageKindForPath(f.path)) continue;51 const data = Buffer.from(f.data); // detach from the unpacked archive so it can be collected52 media.set(f.path, { path: f.path, mime: info.mime, width: info.width, height: info.height, data, etag: `"sha256-${createHash("sha256").update(data).digest("hex")}"` });53 bytes += data.length;54 }55 cache.set(r.sha256, { media, bytes });56 let total = 0;57 for (const e of cache.values()) total += e.bytes;58 for (const [k, e] of cache) {59 if (total <= MAX_CACHE_BYTES || k === r.sha256) break;60 cache.delete(k);61 total -= e.bytes;62 }63 return media;64}6566export const mediaUrl = (a: Pick<ArtifactRow, "namespace" | "slug">, version: string, path: string) =>67 `${env.publicUrl}/api/v1/artifacts/${a.namespace}/${a.slug}/versions/${encodeURIComponent(version)}/media/${path.split("/").map(encodeURIComponent).join("/")}`;6869export interface ExampleDto {70 title: string;71 client: ExampleClient;72 image: string;73 alt: string;74 prompt: string | null;75 caption: string | null;76 capturedAt: string | null;77 url: string;78}79/** Examples as declared in the manifest, each with the URL the registry serves it from. No archive access. */80export function exampleDtos(a: Pick<ArtifactRow, "namespace" | "slug">, r: ReleaseRow): ExampleDto[] {81 return listed(r).map((e) => ({ title: e.title, client: e.client, image: e.image, alt: e.alt, prompt: e.prompt ?? null, caption: e.caption ?? null, capturedAt: e.capturedAt ?? null, url: mediaUrl(a, r.version, e.image) }));82}8384/** Examples with the pixel size and byte size of each image (reads the archive once, then cached). */85export function examplesWithSize(a: Pick<ArtifactRow, "namespace" | "slug">, r: ReleaseRow): (ExampleDto & { width: number | null; height: number | null; sizeBytes: number | null; mime: string | null })[] {86 const dtos = exampleDtos(a, r);87 if (dtos.length === 0 || r.state === "revoked") return dtos.map((d) => ({ ...d, width: null, height: null, sizeBytes: null, mime: null }));88 let media = new Map<string, ExampleMedia>();89 try { media = loadMedia(r); } catch { /* a missing blob must not break the page; the image route reports it */ }90 return dtos.map((d) => {91 const m = media.get(d.image);92 return { ...d, width: m?.width ?? null, height: m?.height ?? null, sizeBytes: m?.data.length ?? null, mime: m?.mime ?? null };93 });94}9596/** Serve one example image. Visibility was already enforced by the artifact and release lookups. */97export function serveExampleMedia(a: ArtifactRow, r: ReleaseRow, path: string, ifNoneMatch: string | null): Response {98 if (r.state === "revoked") throw new ApiError(410, "release_revoked", `@${a.namespace}/${a.slug}@${r.version} was revoked: ${r.revocation_reason ?? "no reason recorded"}.`);99 if (!listed(r).some((e) => e.image === path)) throw notFound("Example image");100 const m = loadMedia(r).get(path);101 if (!m) throw notFound("Example image");102 const isPublic = a.visibility !== "private" && r.state === "published";103 const headers: Record<string, string> = {104 ETag: m.etag,105 "Cache-Control": isPublic ? "public, max-age=31536000, immutable" : "private, no-store",106 "X-Content-Type-Options": "nosniff",107 "Content-Security-Policy": "default-src 'none'; sandbox",108 "Content-Disposition": "inline",109 "Cross-Origin-Resource-Policy": "same-origin",110 };111 if (ifNoneMatch && ifNoneMatch.split(",").map((s) => s.trim()).includes(m.etag)) return new Response(null, { status: 304, headers });112 return new Response(new Uint8Array(m.data), { status: 200, headers: { ...headers, "Content-Type": m.mime, "Content-Length": String(m.data.length) } });113}114