feat: initial refactor to use marka as backend
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getArticle } from "@lib/resource/articles.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_, ctx) {
|
||||
const article = await getArticle(ctx.params.name);
|
||||
const article = await fetchResource(`articles/${ctx.params.name}`);
|
||||
return json(article);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createStreamResponse, isValidUrl } from "@lib/helpers.ts";
|
||||
import * as openai from "@lib/openai.ts";
|
||||
|
||||
import tds from "https://cdn.skypack.dev/turndown@7.2.0";
|
||||
import { Article, createArticle } from "@lib/resource/articles.ts";
|
||||
import { Article } from "@lib/resource/articles.ts";
|
||||
import { getYoutubeVideoDetails } from "@lib/youtube.ts";
|
||||
import {
|
||||
extractYoutubeId,
|
||||
@@ -170,7 +170,7 @@ async function processCreateArticle(
|
||||
|
||||
streamResponse.enqueue("writing to disk");
|
||||
|
||||
await createArticle(newArticle.id, newArticle);
|
||||
// await createArticle(newArticle.id, newArticle);
|
||||
|
||||
streamResponse.enqueue("id: " + newArticle.id);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ async function processCreateYoutubeVideo(
|
||||
|
||||
streamResponse.enqueue("creating article");
|
||||
|
||||
await createArticle(newArticle.id, newArticle);
|
||||
// await createArticle(newArticle.id, newArticle);
|
||||
|
||||
streamResponse.enqueue("finished");
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getAllArticles } from "@lib/resource/articles.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET() {
|
||||
const movies = await getAllArticles();
|
||||
return json(movies);
|
||||
const articles = await fetchResource("articles");
|
||||
return json(articles?.content);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { FreshContext, Handlers } from "$fresh/server.ts";
|
||||
import { getImageContent } from "@lib/image.ts";
|
||||
import { SILVERBULLET_SERVER } from "@lib/env.ts";
|
||||
import { createLogger } from "@lib/log/index.ts";
|
||||
import { isLocalImage } from "@lib/string.ts";
|
||||
|
||||
const log = createLogger("api/image");
|
||||
|
||||
@@ -64,11 +62,10 @@ function parseParams(reqUrl: URL): ImageParams | string {
|
||||
// Helper function to generate ETag
|
||||
async function generateETag(content: ArrayBuffer): Promise<string> {
|
||||
const hashBuffer = await crypto.subtle.digest("SHA-256", content);
|
||||
return `"${
|
||||
Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("")
|
||||
}"`;
|
||||
return `"${Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("")
|
||||
}"`;
|
||||
}
|
||||
|
||||
async function GET(req: Request, _ctx: FreshContext): Promise<Response> {
|
||||
@@ -83,8 +80,8 @@ async function GET(req: Request, _ctx: FreshContext): Promise<Response> {
|
||||
});
|
||||
}
|
||||
|
||||
const imageUrl = isLocalImage(params.image)
|
||||
? `${SILVERBULLET_SERVER}/${params.image.replace(/^\//, "")}`
|
||||
const imageUrl = params.image.startsWith("resources")
|
||||
? `https://marka.max-richter.dev/${params.image.replace(/^\//, "")}`
|
||||
: params.image;
|
||||
|
||||
log.debug("Processing image request:", { imageUrl, params });
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getDocuments } from "@lib/documents.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET() {
|
||||
const documents = await getDocuments();
|
||||
return json(documents);
|
||||
return json([]);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { createMovie, getMovie, Movie } from "@lib/resource/movies.ts";
|
||||
import { Movie } from "@lib/resource/movies.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import * as tmdb from "@lib/tmdb.ts";
|
||||
import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts";
|
||||
import { isString, safeFileName } from "@lib/string.ts";
|
||||
import { createDocument } from "@lib/documents.ts";
|
||||
import { AccessDeniedError } from "@lib/errors.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_, ctx) {
|
||||
const movie = await getMovie(ctx.params.name);
|
||||
return json(movie);
|
||||
const movie = await fetchResource(`movies/${ctx.params.name}`);
|
||||
return json(movie?.content);
|
||||
},
|
||||
async POST(_, ctx) {
|
||||
const session = ctx.state.session;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { FreshContext, Handlers } from "$fresh/server.ts";
|
||||
import { createDocument } from "@lib/documents.ts";
|
||||
import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts";
|
||||
import { createMovie, getMovie } from "@lib/resource/movies.ts";
|
||||
import * as tmdb from "@lib/tmdb.ts";
|
||||
import { isString, safeFileName } from "@lib/string.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
@@ -11,6 +9,7 @@ import {
|
||||
NotFoundError,
|
||||
} from "@lib/errors.ts";
|
||||
import { createRecommendationResource } from "@lib/recommendation.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
const POST = async (
|
||||
req: Request,
|
||||
@@ -21,7 +20,7 @@ const POST = async (
|
||||
throw new AccessDeniedError();
|
||||
}
|
||||
|
||||
const movie = await getMovie(ctx.params.name);
|
||||
const movie = await fetchResource(`movies/${ctx.params.name}`);
|
||||
if (!movie) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
@@ -72,12 +71,12 @@ const POST = async (
|
||||
const poster = await tmdb.getMoviePoster(posterPath);
|
||||
const extension = fileExtension(posterPath);
|
||||
finalPath = `Media/movies/images/${safeFileName(name)}_cover.${extension}`;
|
||||
await createDocument(finalPath, poster);
|
||||
// await createDocument(finalPath, poster);
|
||||
movie.meta = movie.meta || {};
|
||||
movie.meta.image = finalPath;
|
||||
}
|
||||
|
||||
await createMovie(movie.id, movie);
|
||||
// await createMovie(movie.id, movie);
|
||||
|
||||
createRecommendationResource(movie, movieDetails.overview);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getAllMovies } from "@lib/resource/movies.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET() {
|
||||
const movies = await getAllMovies();
|
||||
const movies = await fetchResource("movies");
|
||||
return json(movies);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getRecipe } from "@lib/resource/recipes.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_, ctx) {
|
||||
const recipe = await getRecipe(ctx.params.name);
|
||||
const recipe = await fetchResource(`recipes/${ctx.params.name}`);
|
||||
return json(recipe);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ import { createStreamResponse, isValidUrl } from "@lib/helpers.ts";
|
||||
import * as openai from "@lib/openai.ts";
|
||||
import tds from "https://cdn.skypack.dev/turndown@7.2.0";
|
||||
import { createLogger } from "@lib/log/index.ts";
|
||||
import { createRecipe, Recipe } from "@lib/resource/recipes.ts";
|
||||
import recipeSchema, { isValidRecipe } from "@lib/recipeSchema.ts";
|
||||
import { Recipe } from "@lib/resource/recipes.ts";
|
||||
import recipeSchema from "@lib/recipeSchema.ts";
|
||||
import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts";
|
||||
import { safeFileName } from "@lib/string.ts";
|
||||
import { createDocument } from "@lib/documents.ts";
|
||||
@@ -205,10 +205,10 @@ async function processCreateRecipeFromUrl(
|
||||
streamResponse.enqueue("downloading image");
|
||||
try {
|
||||
streamResponse.enqueue("downloading image");
|
||||
const res = await fetch(src);
|
||||
// const res = await fetch(src);
|
||||
streamResponse.enqueue("saving image");
|
||||
const buffer = await res.arrayBuffer();
|
||||
await createDocument(finalPath, buffer);
|
||||
// const buffer = await res.arrayBuffer();
|
||||
// await createDocument(finalPath, buffer);
|
||||
newRecipe.meta.image = finalPath;
|
||||
} catch (err) {
|
||||
console.log("Failed to save image", err);
|
||||
@@ -218,7 +218,7 @@ async function processCreateRecipeFromUrl(
|
||||
|
||||
streamResponse.enqueue("finished processing, creating file");
|
||||
|
||||
await createRecipe(newRecipe.id, newRecipe);
|
||||
// await createRecipe(newRecipe.id, newRecipe);
|
||||
|
||||
streamResponse.enqueue("id: " + newRecipe.id);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getAllRecipes } from "@lib/resource/recipes.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET() {
|
||||
const recipes = await getAllRecipes();
|
||||
const recipes = await fetchResource("recipes");
|
||||
return json(recipes);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { createStreamResponse } from "@lib/helpers.ts";
|
||||
import { getAllMovies, Movie } from "@lib/resource/movies.ts";
|
||||
import { Movie } from "@lib/resource/movies.ts";
|
||||
import * as tmdb from "@lib/tmdb.ts";
|
||||
import {
|
||||
createRecommendationResource,
|
||||
getRecommendation,
|
||||
} from "@lib/recommendation.ts";
|
||||
import { AccessDeniedError } from "@lib/errors.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
async function processUpdateRecommendations(
|
||||
streamResponse: ReturnType<typeof createStreamResponse>,
|
||||
) {
|
||||
const allMovies = await getAllMovies();
|
||||
const allMovies = await fetchResource("movies");
|
||||
|
||||
const movies = allMovies.filter((m) => {
|
||||
if (!m?.meta) return false;
|
||||
if (!m.meta.rating) return false;
|
||||
if (!m.meta.tmdbId) return false;
|
||||
const movies = allMovies?.content.filter((m) => {
|
||||
if (!m?.content) return false;
|
||||
if (!m.content.reviewRating) return false;
|
||||
if (!m.content.tmdbId) return false;
|
||||
return true;
|
||||
}) as Movie[];
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import { fileExtension } from "https://deno.land/x/file_extension@v2.1.0/mod.ts"
|
||||
import { isString, safeFileName } from "@lib/string.ts";
|
||||
import { createDocument } from "@lib/documents.ts";
|
||||
import { AccessDeniedError } from "@lib/errors.ts";
|
||||
import { createSeries, getSeries, Series } from "@lib/resource/series.ts";
|
||||
import { Series } from "@lib/resource/series.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_, ctx) {
|
||||
const series = await getSeries(ctx.params.name);
|
||||
const series = await fetchResource(`series/${ctx.params.name}`);
|
||||
return json(series);
|
||||
},
|
||||
async POST(_, ctx) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
BadRequestError,
|
||||
NotFoundError,
|
||||
} from "@lib/errors.ts";
|
||||
import { createSeries, getSeries } from "@lib/resource/series.ts";
|
||||
|
||||
const isString = (input: string | undefined): input is string => {
|
||||
return typeof input === "string";
|
||||
@@ -64,15 +63,15 @@ const POST = async (
|
||||
|
||||
let finalPath = "";
|
||||
if (posterPath && !series.meta?.image) {
|
||||
const poster = await tmdb.getMoviePoster(posterPath);
|
||||
// const poster = await tmdb.getMoviePoster(posterPath);
|
||||
const extension = fileExtension(posterPath);
|
||||
|
||||
finalPath = `Media/series/images/${safeFileName(name)}_cover.${extension}`;
|
||||
await createDocument(finalPath, poster);
|
||||
// await createDocument(finalPath, poster);
|
||||
series.meta = series.meta || {};
|
||||
series.meta.image = finalPath;
|
||||
}
|
||||
await createSeries(series.id, series);
|
||||
// await createSeries(series.id, series);
|
||||
|
||||
return json(series);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Article, getArticle } from "@lib/resource/articles.ts";
|
||||
import { Article } from "@lib/resource/articles.ts";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { YoutubePlayer } from "@components/Youtube.tsx";
|
||||
import { HashTags } from "@components/HashTags.tsx";
|
||||
@@ -10,10 +10,11 @@ import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import PageHero from "@components/PageHero.tsx";
|
||||
import { Star } from "@components/Stars.tsx";
|
||||
import { MetaTags } from "@components/MetaTags.tsx";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers<{ article: Article; session: unknown }> = {
|
||||
async GET(_, ctx) {
|
||||
const article = await getArticle(ctx.params.name);
|
||||
const article = await fetchResource(`articles/${ctx.params.name}.md`);
|
||||
if (!article) {
|
||||
return ctx.renderNotFound();
|
||||
}
|
||||
@@ -26,34 +27,39 @@ export default function Greet(
|
||||
) {
|
||||
const { article, session } = props.data;
|
||||
|
||||
const { author = "", date = "" } = article.meta;
|
||||
const { author = "", date = "", articleBody = "" } = article?.content || {};
|
||||
|
||||
const content = renderMarkdown(
|
||||
removeImage(article.content, article.meta.image),
|
||||
removeImage(articleBody, article.content.image),
|
||||
);
|
||||
|
||||
console.log({ article });
|
||||
|
||||
return (
|
||||
<MainLayout
|
||||
url={props.url}
|
||||
title={`Article > ${article.name}`}
|
||||
title={`Article > ${article.content.headline}`}
|
||||
context={article}
|
||||
>
|
||||
<RedirectSearchHandler />
|
||||
<KMenu type="main" context={{ type: "article" }} />
|
||||
<MetaTags resource={article} />
|
||||
|
||||
<PageHero image={article.meta.image} thumbnail={article.meta.thumbnail}>
|
||||
<PageHero
|
||||
image={article.content.image}
|
||||
thumbnail={article.content.thumbnail}
|
||||
>
|
||||
<PageHero.Header>
|
||||
<PageHero.BackLink href="/articles" />
|
||||
{session && (
|
||||
<PageHero.EditLink
|
||||
href={`https://notes.max-richter.dev/Media/articles/${article.id}`}
|
||||
href={`https://notes.max-richter.dev/resources/articles/${article.name}`}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Header>
|
||||
<PageHero.Footer>
|
||||
<PageHero.Title link={article.meta.link}>
|
||||
{article.name}
|
||||
<PageHero.Title link={article.content.url}>
|
||||
{article.content.headline}
|
||||
</PageHero.Title>
|
||||
<PageHero.Subline
|
||||
entries={[
|
||||
@@ -64,20 +70,20 @@ export default function Greet(
|
||||
date.toString(),
|
||||
]}
|
||||
>
|
||||
{article.meta.rating && <Star rating={article.meta.rating} />}
|
||||
{article.content.rating && <Star rating={article.content.rating} />}
|
||||
</PageHero.Subline>
|
||||
</PageHero.Footer>
|
||||
</PageHero>
|
||||
{article.tags.length > 0 && (
|
||||
{article.content?.tags?.length > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<HashTags tags={article.tags} />
|
||||
<HashTags tags={article.content.tags} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<div class="px-8 text-white mt-10">
|
||||
{isYoutubeLink(article.meta.link) && (
|
||||
<YoutubePlayer link={article.meta.link} />
|
||||
{isYoutubeLink(article.content.url) && (
|
||||
<YoutubePlayer link={article.content.url} />
|
||||
)}
|
||||
<pre
|
||||
class="whitespace-break-spaces markdown-body"
|
||||
@@ -85,7 +91,7 @@ export default function Greet(
|
||||
data-dark-theme="dark"
|
||||
dangerouslySetInnerHTML={{ __html: content || "" }}
|
||||
>
|
||||
{content||""}
|
||||
{content || ""}
|
||||
</pre>
|
||||
</div>
|
||||
</MainLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Article, getAllArticles } from "@lib/resource/articles.ts";
|
||||
import { Article } from "@lib/resource/articles.ts";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { Grid } from "@components/Grid.tsx";
|
||||
import { IconArrowLeft } from "@components/icons.tsx";
|
||||
@@ -9,12 +9,14 @@ import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
import { GenericResource } from "@lib/types.ts";
|
||||
import { ResourceCard } from "@components/Card.tsx";
|
||||
import { Link } from "@islands/Link.tsx";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
|
||||
export const handler: Handlers<
|
||||
{ articles: Article[] | null; searchResults?: GenericResource[] }
|
||||
> = {
|
||||
async GET(req, ctx) {
|
||||
const articles = await getAllArticles();
|
||||
const { content: articles } = await fetchResource("articles");
|
||||
const searchParams = parseResourceUrl(req.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, types: ["article"] });
|
||||
|
||||
@@ -4,7 +4,7 @@ import { PageProps } from "$fresh/server.ts";
|
||||
import { resources } from "@lib/resources.ts";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import "@lib/telegram.ts";
|
||||
// import "@lib/telegram.ts";
|
||||
|
||||
export default function Home(props: PageProps) {
|
||||
return (
|
||||
@@ -22,11 +22,10 @@ export default function Home(props: PageProps) {
|
||||
<Card
|
||||
title={`${m.name}`}
|
||||
backgroundSize={80}
|
||||
image={`${
|
||||
m.emoji.endsWith(".png")
|
||||
image={`${m.emoji.endsWith(".png")
|
||||
? `/emojis/${encodeURIComponent(m.emoji)}`
|
||||
: "/placeholder.svg"
|
||||
}`}
|
||||
}`}
|
||||
link={m.link}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PageProps, RouteContext } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { getMovie, Movie } from "@lib/resource/movies.ts";
|
||||
import { HashTags } from "@components/HashTags.tsx";
|
||||
import { Movie } from "@lib/resource/movies.ts";
|
||||
import { removeImage, renderMarkdown } from "@lib/documents.ts";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
@@ -9,22 +8,24 @@ import { Recommendations } from "@islands/Recommendations.tsx";
|
||||
import PageHero from "@components/PageHero.tsx";
|
||||
import { Star } from "@components/Stars.tsx";
|
||||
import { MetaTags } from "@components/MetaTags.tsx";
|
||||
import { parseRating } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export default async function Greet(
|
||||
props: PageProps<{ movie: Movie; session: Record<string, string> }>,
|
||||
ctx: RouteContext,
|
||||
) {
|
||||
const movie = await getMovie(ctx.params.name);
|
||||
const movie = await fetchResource(`movies/${ctx.params.name}.md`);
|
||||
const session = ctx.state.session;
|
||||
|
||||
if (!movie) {
|
||||
return ctx.renderNotFound();
|
||||
}
|
||||
|
||||
const { author = "", date = "" } = movie.meta;
|
||||
const { author = "", date = "" } = movie.content;
|
||||
|
||||
const content = renderMarkdown(
|
||||
removeImage(movie.description || "", movie.meta.image),
|
||||
removeImage(movie.content.reviewBody || "", movie.content.image),
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -33,14 +34,14 @@ export default async function Greet(
|
||||
<KMenu type="main" context={movie} />
|
||||
<MetaTags resource={movie} />
|
||||
<PageHero
|
||||
image={movie.meta.image}
|
||||
thumbnail={movie.meta.thumbnail}
|
||||
image={movie.content.image}
|
||||
thumbnail={movie.content.thumbnail}
|
||||
>
|
||||
<PageHero.Header>
|
||||
<PageHero.BackLink href="/movies" />
|
||||
{session && (
|
||||
<PageHero.EditLink
|
||||
href={`https://notes.max-richter.dev/Media/movies/${movie.id}`}
|
||||
href={`https://notes.max-richter.dev/resources/movies/${movie.name}`}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Header>
|
||||
@@ -49,13 +50,17 @@ export default async function Greet(
|
||||
<PageHero.Subline
|
||||
entries={[
|
||||
author && {
|
||||
title: author,
|
||||
href: `/?q=${encodeURIComponent(author)}`,
|
||||
title: author?.name,
|
||||
href: `/?q=${encodeURIComponent(author?.name)}`,
|
||||
},
|
||||
date.toString(),
|
||||
]}
|
||||
>
|
||||
{movie.meta.rating && <Star rating={movie.meta.rating} />}
|
||||
{movie.content.reviewRating && (
|
||||
<Star
|
||||
rating={parseRating(movie.content.reviewRating?.ratingValue)}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Subline>
|
||||
</PageHero.Footer>
|
||||
</PageHero>
|
||||
@@ -65,14 +70,8 @@ export default async function Greet(
|
||||
type="movie"
|
||||
/>
|
||||
)}
|
||||
{movie.tags.length > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<HashTags tags={movie.tags} />
|
||||
</>
|
||||
)}
|
||||
<div class="px-8 text-white mt-10">
|
||||
{movie?.description?.length > 80
|
||||
{movie?.content?.reviewBody?.length > 80
|
||||
? <h2 class="text-4xl font-bold mb-4">Review</h2>
|
||||
: <></>}
|
||||
<pre
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { getAllMovies, Movie } from "@lib/resource/movies.ts";
|
||||
import { Movie } from "@lib/resource/movies.ts";
|
||||
import { ResourceCard } from "@components/Card.tsx";
|
||||
import { Grid } from "@components/Grid.tsx";
|
||||
import { IconArrowLeft } from "@components/icons.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
import { GenericResource } from "@lib/types.ts";
|
||||
import { PageProps } from "$fresh/server.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
|
||||
export default async function Greet(
|
||||
props: PageProps<
|
||||
{ movies: Movie[] | null; searchResults: GenericResource[] }
|
||||
>,
|
||||
) {
|
||||
const allMovies = await getAllMovies();
|
||||
const { content: allMovies } = await fetchResource("movies");
|
||||
const searchParams = parseResourceUrl(props.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, types: ["movie"] });
|
||||
const movies = allMovies.sort((a, b) =>
|
||||
a?.meta?.rating > b?.meta?.rating ? -1 : 1
|
||||
a?.content?.reviewRating?.ratingValue > b?.content?.reviewRating?.ratingValue ? -1 : 1
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IngredientsList } from "@islands/IngredientsList.tsx";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import Counter from "@islands/Counter.tsx";
|
||||
import { Signal, useSignal } from "@preact/signals";
|
||||
import { getRecipe, Recipe } from "@lib/resource/recipes.ts";
|
||||
import { Recipe } from "@lib/recipeSchema.ts";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import PageHero from "@components/PageHero.tsx";
|
||||
@@ -11,11 +11,12 @@ import { Star } from "@components/Stars.tsx";
|
||||
import { renderMarkdown } from "@lib/documents.ts";
|
||||
import { isValidRecipe } from "@lib/recipeSchema.ts";
|
||||
import { MetaTags } from "@components/MetaTags.tsx";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers<{ recipe: Recipe; session: unknown } | null> = {
|
||||
async GET(_, ctx) {
|
||||
try {
|
||||
const recipe = await getRecipe(ctx.params.name);
|
||||
const recipe = await fetchResource(`recipes/${ctx.params.name}.md`);
|
||||
if (!recipe) {
|
||||
return ctx.renderNotFound();
|
||||
}
|
||||
@@ -38,22 +39,24 @@ function ValidRecipe({
|
||||
{portion && <Counter count={amount} />}
|
||||
</div>
|
||||
<IngredientsList
|
||||
ingredients={recipe.ingredients}
|
||||
ingredients={recipe.content.recipeIngredient}
|
||||
amount={amount}
|
||||
portion={portion}
|
||||
/>
|
||||
<h3 class="text-3xl my-5">Preparation</h3>
|
||||
<div class="pl-2">
|
||||
<ol class="list-decimal grid gap-4">
|
||||
{recipe.instructions && (recipe.instructions.map((instruction) => {
|
||||
return (
|
||||
<li
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMarkdown(instruction),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}))}
|
||||
{recipe.content.recipeInstructions &&
|
||||
(recipe.content.recipeInstructions.filter((inst) => !!inst?.length)
|
||||
.map((instruction) => {
|
||||
return (
|
||||
<li
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMarkdown(instruction),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}))}
|
||||
</ol>
|
||||
</div>
|
||||
</>
|
||||
@@ -65,35 +68,38 @@ export default function Page(
|
||||
) {
|
||||
const { recipe, session } = props.data;
|
||||
|
||||
const portion = recipe.meta?.portion;
|
||||
const portion = recipe.recipeYield;
|
||||
const amount = useSignal(portion || 1);
|
||||
|
||||
const subline = [
|
||||
recipe?.meta?.time && `Duration ${recipe.meta.time}`,
|
||||
recipe?.content?.prepTime && `Duration ${recipe?.content?.prepTime}`,
|
||||
].filter(Boolean) as string[];
|
||||
|
||||
return (
|
||||
<MainLayout
|
||||
url={props.url}
|
||||
title={`Recipes > ${recipe.name}`}
|
||||
title={`Recipes > ${recipe.content?.name}`}
|
||||
context={recipe}
|
||||
>
|
||||
<RedirectSearchHandler />
|
||||
<KMenu type="main" context={recipe} />
|
||||
<MetaTags resource={recipe} />
|
||||
|
||||
<PageHero image={recipe.meta?.image} thumbnail={recipe.meta?.thumbnail}>
|
||||
<PageHero
|
||||
image={recipe.content?.image}
|
||||
thumbnail={recipe.content?.thumbnail}
|
||||
>
|
||||
<PageHero.Header>
|
||||
<PageHero.BackLink href="/recipes" />
|
||||
{session && (
|
||||
<PageHero.EditLink
|
||||
href={`https://notes.max-richter.dev/Recipes/${recipe.id}`}
|
||||
href={`https://notes.max-richter.dev/resources/recipes/${recipe.name}`}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Header>
|
||||
<PageHero.Footer>
|
||||
<PageHero.Title link={recipe.meta?.link}>
|
||||
{recipe.name}
|
||||
<PageHero.Title link={recipe.content?.link}>
|
||||
{recipe.content.name}
|
||||
</PageHero.Title>
|
||||
<PageHero.Subline
|
||||
entries={subline}
|
||||
@@ -113,12 +119,9 @@ export default function Page(
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div
|
||||
class="whitespace-break-spaces markdown-body"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMarkdown(recipe?.markdown || ""),
|
||||
}}
|
||||
/>
|
||||
<div class="whitespace-break-spaces markdown-body">
|
||||
{JSON.stringify(recipe)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</MainLayout>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { getAllRecipes, Recipe } from "@lib/resource/recipes.ts";
|
||||
import { Recipe } from "@lib/recipeSchema.ts";
|
||||
import { Grid } from "@components/Grid.tsx";
|
||||
import { IconArrowLeft } from "@components/icons.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
import { GenericResource } from "@lib/types.ts";
|
||||
import { ResourceCard } from "@components/Card.tsx";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
|
||||
export const handler: Handlers<
|
||||
{ recipes: Recipe[] | null; searchResults?: GenericResource[] }
|
||||
> = {
|
||||
async GET(req, ctx) {
|
||||
const recipes = await getAllRecipes();
|
||||
const { content: recipes } = await fetchResource("recipes");
|
||||
const searchParams = parseResourceUrl(req.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, types: ["recipe"] });
|
||||
@@ -48,8 +49,8 @@ export default function Greet(
|
||||
<h3 class="text-2xl text-white font-light">🍽️ Recipes</h3>
|
||||
</header>
|
||||
<Grid>
|
||||
{recipes?.map((doc) => {
|
||||
return <ResourceCard sublink="recipes" res={doc} />;
|
||||
{recipes?.filter((s) => !!s?.content?.name).map((doc) => {
|
||||
return <ResourceCard sublink="recipes" key={doc.name} res={doc} />;
|
||||
})}
|
||||
</Grid>
|
||||
</MainLayout>
|
||||
|
||||
@@ -2,16 +2,18 @@ import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { HashTags } from "@components/HashTags.tsx";
|
||||
import { removeImage, renderMarkdown } from "@lib/documents.ts";
|
||||
import { getSeries, Series } from "@lib/resource/series.ts";
|
||||
import { Series } from "@lib/resource/series.ts";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import PageHero from "@components/PageHero.tsx";
|
||||
import { Star } from "@components/Stars.tsx";
|
||||
import { MetaTags } from "@components/MetaTags.tsx";
|
||||
import { parseRating } from "@lib/helpers.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
|
||||
export const handler: Handlers<{ serie: Series; session: unknown }> = {
|
||||
async GET(_, ctx) {
|
||||
const serie = await getSeries(ctx.params.name);
|
||||
const serie = await fetchResource(`series/${ctx.params.name}`);
|
||||
|
||||
if (!serie) {
|
||||
return ctx.renderNotFound();
|
||||
@@ -25,24 +27,28 @@ export default function Greet(
|
||||
) {
|
||||
const { serie, session } = props.data;
|
||||
|
||||
const { author = "", date = "" } = serie.meta;
|
||||
const { author = "", date = "" } = serie.content;
|
||||
|
||||
const content = renderMarkdown(
|
||||
removeImage(serie.description || "", serie.meta.image),
|
||||
removeImage(serie.description || "", serie.content.image),
|
||||
);
|
||||
|
||||
return (
|
||||
<MainLayout url={props.url} title={`Serie > ${serie.name}`} context={serie}>
|
||||
<MainLayout
|
||||
url={props.url}
|
||||
title={`Serie > ${serie.content.name}`}
|
||||
context={serie}
|
||||
>
|
||||
<RedirectSearchHandler />
|
||||
<KMenu type="main" context={serie} />
|
||||
|
||||
<MetaTags resource={serie} />
|
||||
<PageHero image={serie.meta.image} thumbnail={serie.meta.thumbnail}>
|
||||
<PageHero image={serie.content.image} thumbnail={serie.content.thumbnail}>
|
||||
<PageHero.Header>
|
||||
<PageHero.BackLink href="/series" />
|
||||
{session && (
|
||||
<PageHero.EditLink
|
||||
href={`https://notes.max-richter.dev/Media/series/${serie.id}`}
|
||||
href={`https://notes.max-richter.dev/resources/series/${serie.name}`}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Header>
|
||||
@@ -57,18 +63,22 @@ export default function Greet(
|
||||
date.toString(),
|
||||
]}
|
||||
>
|
||||
{serie.meta.rating && <Star rating={serie.meta.rating} />}
|
||||
{serie.content.reviewRating && (
|
||||
<Star
|
||||
rating={parseRating(serie.content.reviewRating.ratingValue)}
|
||||
/>
|
||||
)}
|
||||
</PageHero.Subline>
|
||||
</PageHero.Footer>
|
||||
</PageHero>
|
||||
{serie.tags.length > 0 && (
|
||||
{serie.content?.tags?.length > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<HashTags tags={serie.tags} />
|
||||
<HashTags tags={serie.content.tags} />
|
||||
</>
|
||||
)}
|
||||
<div class="px-8 text-white mt-10">
|
||||
{serie?.description?.length > 80
|
||||
{serie?.content?.reviewBody?.length > 80
|
||||
? <h2 class="text-4xl font-bold mb-4">Review</h2>
|
||||
: <></>}
|
||||
<pre
|
||||
|
||||
@@ -2,18 +2,19 @@ import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Grid } from "@components/Grid.tsx";
|
||||
import { IconArrowLeft } from "@components/icons.tsx";
|
||||
import { getAllSeries, Series } from "@lib/resource/series.ts";
|
||||
import { Series } from "@lib/resource/series.ts";
|
||||
import { RedirectSearchHandler } from "@islands/Search.tsx";
|
||||
import { KMenu } from "@islands/KMenu.tsx";
|
||||
import { ResourceCard } from "@components/Card.tsx";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
import { GenericResource } from "@lib/types.ts";
|
||||
import { fetchResource } from "@lib/resources.ts";
|
||||
import { parseResourceUrl, searchResource } from "@lib/search.ts";
|
||||
|
||||
export const handler: Handlers<
|
||||
{ series: Series[] | null; searchResults?: GenericResource[] }
|
||||
> = {
|
||||
async GET(req, ctx) {
|
||||
const series = await getAllSeries();
|
||||
const { content: series } = await fetchResource("series");
|
||||
const searchParams = parseResourceUrl(req.url);
|
||||
const searchResults = searchParams &&
|
||||
await searchResource({ ...searchParams, types: ["series"] });
|
||||
|
||||
Reference in New Issue
Block a user