@paroicms/server
Version:
The ParoiCMS server
168 lines • 6.97 kB
JavaScript
import { ApiError, applyPixelRatioToResizeRule, generateImageSlug, } from "@paroicms/public-server-lib";
import { parseSizeName } from "@paroicms/server-image-cache-engine";
import { HTTP_MAX_AGE } from "../../express/http-constants.js";
import { getRouteParameter, serve301Redirect, serve304NotModified, } from "../../express/http-helpers.js";
import { makeUrlOfImageVariant, makeUrlOfMediaFile, makeUrlOfUnversionedImageVariant, } from "../../helpers/url-helpers.js";
import { parseImageRawSizeNameAndExt, parseImageRenderedResizeRuleAndExt, } from "./_media-serve-helpers.js";
export async function serveFilecontroller(siteContext, httpContext, routeParams) {
const { req, res } = httpContext;
const mediaId = getRouteParameter(routeParams, "mediaId");
const nameAndExt = getRouteParameter(routeParams, "nameAndExt");
const cacheControl = "max-age=0";
const ifNoneMatch = req.headers["if-none-match"];
const media = await siteContext.mediaStorage.getMediaWithBinary(mediaId);
if (!media)
throw new ApiError(`media '${mediaId}' not found`, 404);
if (media.resourceVersion === ifNoneMatch) {
return serve304NotModified(httpContext, {
cacheControl,
etag: media.resourceVersion,
});
}
const { url, filename } = makeUrlOfMediaFile(siteContext, { mediaId, mediaType: media.mediaType, originalName: media.originalName }, { returnObj: true });
if (filename !== nameAndExt)
return serve301Redirect(httpContext, url);
res.append("Cache-Control", cacheControl);
res.append("Etag", media.resourceVersion);
res.append("Content-Type", media.mediaType);
res.append("Content-Length", media.binaryFile.byteLength.toString());
res.send(media.binaryFile);
}
export async function serveCacheImageController(siteContext, httpContext, routeParams) {
const mediaId = getRouteParameter(routeParams, "mediaId");
const resourceVersion = getRouteParameter(routeParams, "resourceVersion");
const nameAndExt = getRouteParameter(routeParams, "nameAndExt");
if (resourceVersion === "unversioned") {
await serveUnversionedFTextImage(siteContext, httpContext, {
mediaId,
nameAndExt,
});
return;
}
const { req, res } = httpContext;
const cacheControl = `public, max-age=${HTTP_MAX_AGE}, immutable`;
const ifNoneMatch = req.headers["if-none-match"];
const parsed = parseImageRawSizeNameAndExt(nameAndExt);
if (!parsed)
throw new ApiError(404);
const { rawSizeName, mediaType } = parsed;
const { width: rawWidth, height: rawHeight } = parseSizeName(rawSizeName);
const file = await siteContext.imageCache.getImageVariantWithBinary({
mediaId,
mediaType,
rawWidth,
rawHeight,
});
if (!file)
throw new ApiError(404);
if (ifNoneMatch === file.resourceVersion) {
return serve304NotModified(httpContext, {
cacheControl,
etag: file.resourceVersion,
});
}
const newUrl = makeUrlOfImageVariant(siteContext, {
mediaId,
mediaType: file.mediaType,
rawSizeName,
resourceVersion: file.resourceVersion,
slug: file.slug,
}, { returnObj: true });
if (file.resourceVersion !== resourceVersion || newUrl.filename !== nameAndExt) {
return serve301Redirect(httpContext, newUrl.url);
}
res.append("Cache-Control", cacheControl);
res.append("Etag", file.resourceVersion);
res.append("Content-Type", file.mediaType);
res.append("Content-Length", file.binaryFile.byteLength.toString());
res.send(file.binaryFile);
}
async function serveUnversionedFTextImage(siteContext, httpContext, routeParams) {
const { mediaId, nameAndExt } = routeParams;
const parsed = parseImageRenderedResizeRuleAndExt(nameAndExt);
if (!parsed)
throw new ApiError(404);
const { resizeRule, mediaType } = parsed;
if (!siteContext.themeConf.fTextImageResizeRules.includes(resizeRule))
throw new ApiError(404);
if (mediaType !== "image/webp")
throw new ApiError(404);
await serveUnversionedImage(siteContext, httpContext, {
mediaId,
mediaType,
ownerHandle: `fText:${resizeRule}`,
isHandleReusable: true,
resizeRule,
ensureUrlFilename: nameAndExt,
});
}
export async function serveUnversionedImage(siteContext, httpContext, options) {
const { req, res } = httpContext;
const cacheControl = "max-age=0";
const ifNoneMatch = req.headers["if-none-match"];
const { mediaId, mediaType, ownerHandle, isHandleReusable, resizeRule, ensureUrlFilename } = options;
const rawResizeR = applyPixelRatioToResizeRule(resizeRule, siteContext.themeConf.pixelRatio);
let variant = await siteContext.imageCache.getImageVariantWithBinaryByOwner({
mediaId,
mediaType,
ownerHandle,
rawResizeR,
});
if (variant) {
if (ifNoneMatch === variant.resourceVersion) {
return serve304NotModified(httpContext, {
cacheControl,
etag: variant.resourceVersion,
});
}
}
else {
variant = await createUnversionedImageVariant(siteContext, {
ownerHandle,
isHandleReusable,
mediaId,
mediaType,
rawResizeR,
});
if (!variant)
throw new ApiError(404);
}
if (ensureUrlFilename) {
const newUrl = makeUrlOfUnversionedImageVariant(siteContext, {
mediaId,
mediaType: variant.mediaType,
resizeRule,
slug: variant.slug,
}, { returnObj: true });
if (newUrl.filename !== ensureUrlFilename) {
return serve301Redirect(httpContext, newUrl.url);
}
}
res.append("Cache-Control", cacheControl);
res.append("Etag", variant.resourceVersion);
res.append("Content-Type", variant.mediaType);
res.append("Content-Length", variant.binaryFile.byteLength.toString());
res.send(variant.binaryFile);
}
async function createUnversionedImageVariant(siteContext, options) {
const { ownerHandle, isHandleReusable, mediaId, mediaType, rawResizeR } = options;
const media = await siteContext.mediaStorage.getMedia({ mediaId });
if (!media || media.kind !== "image")
return;
const variant = await siteContext.imageCache.makeImageAvailable({
ownerHandle,
isHandleReusable,
rawResizeR,
mediaType,
sourceImage: {
mediaId: media.id,
mediaType: media.mediaType,
rawWidth: media.rawWidth,
rawHeight: media.rawHeight,
slug: generateImageSlug(media.originalName),
},
canBeRetrievedByOwner: true,
});
return await siteContext.imageCache.getImageVariantWithBinary(variant);
}
//# sourceMappingURL=media-serve.controller.js.map