feat: move all routes to resources folder

This commit is contained in:
2023-07-27 15:02:23 +02:00
parent e5b12d3476
commit c7b5d3eac3
9 changed files with 0 additions and 0 deletions

28
routes/resources/_404.tsx Normal file
View File

@ -0,0 +1,28 @@
import { Head } from "$fresh/runtime.ts";
export default function Error404() {
return (
<>
<Head>
<title>404 - Page not found</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"
/>
<h1 class="text-4xl font-bold">404 - Page not found</h1>
<p class="my-4">
The page you were looking for doesn't exist.
</p>
<a href="/" class="underline">Go back home</a>
</div>
</div>
</>
);
}

23
routes/resources/_app.tsx Normal file
View File

@ -0,0 +1,23 @@
import { AppProps } from "$fresh/server.ts";
import { Head } from "$fresh/runtime.ts";
export default function App({ Component }: AppProps) {
return (
<>
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin=""
/>
<link
href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@100;400;700&display=swap"
rel="stylesheet"
/>
<link href="/global.css" rel="stylesheet" />
</Head>
<Component />
</>
);
}

View File

@ -0,0 +1,11 @@
import { HandlerContext } from "$fresh/server.ts";
import { getDocuments } from "../../lib/documents.ts";
export const handler = async (
_req: Request,
_ctx: HandlerContext,
): Promise<Response> => {
const documents = await getDocuments();
const response = new Response(JSON.stringify(documents));
return response;
};

View File

@ -0,0 +1,23 @@
import { HandlerContext } from "$fresh/server.ts";
import { getDocument } from "../../../lib/documents.ts";
import { parseRecipe } from "../../../lib/recipes.ts";
export async function getRecipe(name: string) {
const document = await getDocument(`Recipes/${name}.md`);
const recipe = parseRecipe(document, name);
return recipe;
}
export const handler = async (
_req: Request,
_ctx: HandlerContext,
): Promise<Response> => {
const recipe = await getRecipe(_ctx.params.name);
const headers = new Headers();
headers.append("Content-Type", "application/json");
return new Response(JSON.stringify(recipe));
};

View File

@ -0,0 +1,128 @@
import { HandlerContext } from "$fresh/server.ts";
import {
ImageMagick,
initializeImageMagick,
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";
await initializeImageMagick();
async function getRemoteImage(image: string) {
const sourceRes = await fetch(image);
if (!sourceRes.ok) {
return "Error retrieving image from URL.";
}
const mediaType = parseMediaType(sourceRes.headers.get("Content-Type")!)[0];
if (mediaType.split("/")[0] !== "image") {
return "URL is not image type.";
}
return {
buffer: new Uint8Array(await sourceRes.arrayBuffer()),
mediaType,
};
}
function getWidthHeight(
current: { width: number; height: number },
final: { width: number; height: number },
) {
const ratio = (current.width / final.width) > (current.height / final.height)
? (current.height / final.height)
: (current.width / final.width);
return new MagickGeometry(
current.width / ratio,
current.height / ratio,
);
}
function modifyImage(
imageBuffer: Uint8Array,
params: { width: number; height: number; mode: "resize" | "crop" },
) {
return new Promise<Uint8Array>((resolve) => {
ImageMagick.read(imageBuffer, (image) => {
const sizingData = getWidthHeight(image, params);
if (params.mode === "resize") {
image.resize(sizingData);
} else {
image.crop(sizingData);
}
image.write((data) => resolve(data));
});
});
}
function parseParams(reqUrl: URL) {
const height = Number(reqUrl.searchParams.get("height")) || 0;
const width = Number(reqUrl.searchParams.get("width")) || 0;
if (height === 0 && width === 0) {
//return "Missing non-zero 'height' or 'width' query parameter.";
}
if (height < 0 || width < 0) {
return "Negative height or width is not supported.";
}
const maxDimension = 2048;
if (height > maxDimension || width > maxDimension) {
return `Width and height cannot exceed ${maxDimension}.`;
}
return {
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);
if (cachedResponse) {
const _r = await cachedResponse;
console.log({ cachedResponse, _r });
return (await cachedResponse).clone();
}
const remoteImage = await getRemoteImage(imageUrl);
if (typeof remoteImage === "string") {
return new Response(remoteImage, { status: 400 });
}
const response = getImageResponse(remoteImage, params);
await cache.set(imageId, response);
return response;
};

View File

@ -0,0 +1,34 @@
import { HandlerContext } from "$fresh/server.ts";
import { getDocument, getDocuments } from "../../../lib/documents.ts";
import { parseRecipe } from "../../../lib/recipes.ts";
export async function getRecipes() {
const documents = await getDocuments();
return Promise.all(
documents.filter((d) => {
return d.name.startsWith("Recipes/") &&
d.contentType === "text/markdown" &&
!d.name.endsWith("index.md");
}).map(async (doc) => {
const document = await getDocument(doc.name);
const recipe = parseRecipe(document, doc.name);
return {
...recipe,
id: recipe.id.replace(/^Recipes\//, "").replace(/\.md$/, ""),
};
}),
);
}
export const handler = async (
_req: Request,
_ctx: HandlerContext,
): Promise<Response> => {
const headers = new Headers();
headers.append("Content-Type", "application/json");
const recipes = await getRecipes();
return new Response(JSON.stringify(recipes), { headers });
};

View File

@ -0,0 +1,31 @@
import { Head } from "$fresh/runtime.ts";
import { useSignal } from "@preact/signals";
import Counter from "../islands/Counter.tsx";
export default function Home() {
const count = useSignal(3);
return (
<>
<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"
/>
<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>
</>
);
}

View File

@ -0,0 +1,26 @@
import { Handlers, PageProps } from "$fresh/server.ts";
import { IngredientsList } from "../../components/IngredientsList.tsx";
import { RecipeHero } from "../../components/RecipeHero.tsx";
import { MainLayout } from "../../components/layouts/main.tsx";
import { Recipe } from "../../lib/recipes.ts";
import { getRecipe } from "../api/recipes/[name].ts";
export const handler: Handlers<Recipe | null> = {
async GET(_, ctx) {
const recipe = await getRecipe(ctx.params.name);
return ctx.render(recipe);
},
};
export default function Greet(props: PageProps<Recipe>) {
return (
<MainLayout>
<RecipeHero recipe={props.data} />
<div class="px-8 text-white mt-10">
<h3 class="text-3xl my-5">Ingredients</h3>
<IngredientsList ingredients={props.data.ingredients} />
<h3 class="text-3xl my-5">Preparation</h3>
</div>
</MainLayout>
);
}

View File

@ -0,0 +1,25 @@
import { Handlers, PageProps } from "$fresh/server.ts";
import { RecipeCard } from "../../components/RecipeCard.tsx";
import { MainLayout } from "../../components/layouts/main.tsx";
import type { Document } from "../../lib/documents.ts";
import { Recipe } from "../../lib/recipes.ts";
import { getRecipes } from "../api/recipes/index.ts";
export const handler: Handlers<Recipe[] | null> = {
async GET(_, ctx) {
const recipes = await getRecipes();
return ctx.render(recipes);
},
};
export default function Greet(props: PageProps<Recipe[] | null>) {
return (
<MainLayout>
<div class="flex flex-wrap justify-center items-center gap-4 px-4">
{props.data?.map((doc) => {
return <RecipeCard recipe={doc} />;
})}
</div>
</MainLayout>
);
}