feat: correctly cache images with redis
This commit is contained in:
@ -5,7 +5,7 @@ import {
|
||||
MagickGeometry,
|
||||
} from "https://deno.land/x/imagemagick_deno@0.0.14/mod.ts";
|
||||
import { parseMediaType } from "https://deno.land/std@0.175.0/media_types/parse_media_type.ts";
|
||||
import * as cache from "@lib/cache.ts";
|
||||
import * as cache from "@lib/cache/image.ts";
|
||||
|
||||
await initializeImageMagick();
|
||||
|
||||
@ -56,6 +56,11 @@ function modifyImage(
|
||||
}
|
||||
|
||||
function parseParams(reqUrl: URL) {
|
||||
const image = reqUrl.searchParams.get("image")?.replace(/^\//, "");
|
||||
if (image == null) {
|
||||
return "Missing 'image' query parameter.";
|
||||
}
|
||||
|
||||
const height = Number(reqUrl.searchParams.get("height")) || 0;
|
||||
const width = Number(reqUrl.searchParams.get("width")) || 0;
|
||||
if (height === 0 && width === 0) {
|
||||
@ -69,50 +74,36 @@ function parseParams(reqUrl: URL) {
|
||||
return `Width and height cannot exceed ${maxDimension}.`;
|
||||
}
|
||||
return {
|
||||
image,
|
||||
height,
|
||||
width,
|
||||
};
|
||||
}
|
||||
|
||||
async function getImageResponse(
|
||||
remoteImage: { buffer: Uint8Array; mediaType: string },
|
||||
params: { width: number; height: number },
|
||||
): Promise<Response> {
|
||||
const modifiedImage = await modifyImage(remoteImage.buffer, {
|
||||
...params,
|
||||
mode: "resize",
|
||||
});
|
||||
|
||||
const response = new Response(modifiedImage, {
|
||||
headers: {
|
||||
"Content-Type": remoteImage.mediaType,
|
||||
},
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const handler = async (
|
||||
_req: Request,
|
||||
_ctx: HandlerContext,
|
||||
): Promise<Response> => {
|
||||
const imageUrl = Deno.env.get("SILVERBULLET_SERVER") + "/Recipes/images/" +
|
||||
_ctx.params.image;
|
||||
|
||||
const url = new URL(_req.url);
|
||||
|
||||
const params = parseParams(url);
|
||||
|
||||
if (typeof params === "string") {
|
||||
return new Response(params, { status: 400 });
|
||||
}
|
||||
|
||||
const imageId = `${imageUrl}.${params.width}.${params.height}`;
|
||||
const cachedResponse = await cache.get(imageId);
|
||||
const imageUrl = Deno.env.get("SILVERBULLET_SERVER") + "/" + params.image;
|
||||
|
||||
const cachedResponse = await cache.getImage({
|
||||
url: imageUrl,
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
});
|
||||
if (cachedResponse) {
|
||||
const _r = await cachedResponse;
|
||||
console.log({ cachedResponse, _r });
|
||||
return (await cachedResponse).clone();
|
||||
return new Response(cachedResponse.buffer.slice(), {
|
||||
headers: {
|
||||
"Content-Type": cachedResponse.mediaType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const remoteImage = await getRemoteImage(imageUrl);
|
||||
@ -120,9 +111,21 @@ export const handler = async (
|
||||
return new Response(remoteImage, { status: 400 });
|
||||
}
|
||||
|
||||
const response = getImageResponse(remoteImage, params);
|
||||
const modifiedImage = await modifyImage(remoteImage.buffer, {
|
||||
...params,
|
||||
mode: "resize",
|
||||
});
|
||||
|
||||
await cache.set(imageId, response);
|
||||
cache.setImage(modifiedImage, {
|
||||
url: imageUrl,
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
mediaType: remoteImage.mediaType,
|
||||
});
|
||||
|
||||
return response;
|
||||
return new Response(modifiedImage, {
|
||||
headers: {
|
||||
"Content-Type": remoteImage.mediaType,
|
||||
},
|
||||
});
|
||||
};
|
@ -1,6 +1,8 @@
|
||||
import { Head } from "$fresh/runtime.ts";
|
||||
import { useSignal } from "@preact/signals";
|
||||
import Counter from "@islands/Counter.tsx";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Card } from "@components/Card.tsx";
|
||||
|
||||
export default function Home() {
|
||||
const count = useSignal(3);
|
||||
@ -9,23 +11,15 @@ export default function Home() {
|
||||
<Head>
|
||||
<title>app</title>
|
||||
</Head>
|
||||
<div class="px-4 py-8 mx-auto bg-[#86efac]">
|
||||
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
|
||||
<img
|
||||
class="my-6"
|
||||
src="/logo.svg"
|
||||
width="128"
|
||||
height="128"
|
||||
alt="the fresh logo: a sliced lemon dripping with juice"
|
||||
<MainLayout>
|
||||
<div class="flex flex-wrap justify-center items-center gap-4 px-4">
|
||||
<Card
|
||||
title="Recipes"
|
||||
image="/api/images?image=Recipes/images/placeholder.jpg&width=200&height=200"
|
||||
link="/recipes"
|
||||
/>
|
||||
<h1 class="text-4xl font-bold">Welcome to fresh</h1>
|
||||
<p class="my-4">
|
||||
Try updating this message in the
|
||||
<code class="mx-2">./routes/index.tsx</code> file, and refresh.
|
||||
</p>
|
||||
<Counter count={count} />
|
||||
</div>
|
||||
</div>
|
||||
</MainLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { RecipeCard } from "@components/RecipeCard.tsx";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Recipe } from "@lib/recipes.ts";
|
||||
import { getRecipes } from "../api/recipes/index.ts";
|
||||
|
||||
import IconArrowLeft from "https://deno.land/x/tabler_icons_tsx@0.0.3/tsx/arrow-left.tsx";
|
||||
export const handler: Handlers<Recipe[] | null> = {
|
||||
async GET(_, ctx) {
|
||||
const recipes = await getRecipes();
|
||||
@ -14,6 +14,17 @@ export const handler: Handlers<Recipe[] | null> = {
|
||||
export default function Greet(props: PageProps<Recipe[] | null>) {
|
||||
return (
|
||||
<MainLayout>
|
||||
<header class="flex gap-4 items-center mb-5">
|
||||
<a
|
||||
class="px-4 ml-4 py-2 bg-gray-300 text-gray-800 rounded-lg flex items-center gap-1"
|
||||
href="/"
|
||||
>
|
||||
<IconArrowLeft class="w-5 h-5" />
|
||||
Back
|
||||
</a>
|
||||
|
||||
<h3 class="text-2xl text-white font-light">Recipes</h3>
|
||||
</header>
|
||||
<div class="flex flex-wrap justify-center items-center gap-4 px-4">
|
||||
{props.data?.map((doc) => {
|
||||
return <RecipeCard recipe={doc} />;
|
||||
|
Reference in New Issue
Block a user