Most all posts into memory at startup and render those on requests.

This commit is contained in:
Dave Smith-Hayes 2024-07-26 22:31:09 -04:00
parent 3c4be7f78f
commit 6e400f4263
10 changed files with 87 additions and 49 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,17 +1,25 @@
import { Hono } from 'hono';
import { Home } from '@blog/templates/Pages/Home';
import { getPostList } from '@blog/post/post-reader';
import { PostMeta } from '@blog/model/PostMeta';
import { Hono } from "hono";
import { Home } from "@blog/templates/Pages/Home";
import { PostMeta } from "@blog/models/PostMeta";
import { Post } from '@blog/models/Post';
import { PostFileService } from '@blog/services/post-file';
const app = new Hono();
type Posts = {
postService: PostFileService
};
app.get('/', async (c) => {
const postDir = __dirname + '/../../posts';
const postList: PostMeta[] = await getPostList(postDir);
const app = new Hono<{ Variables: Posts }>();
return c.render(<Home posts={postList}/>, { meta: {
description: "The blog for Dave Smith-Hayes, a dad and developer."
}});
app.get("/", async (c) => {
const postService: PostFileService = c.get('postService');
const posts = postService.getPosts();
const postList: PostMeta[] = Array.from(posts.values()).map((p: Post) => p.meta);
return c.render(<Home posts={postList} />, {
meta: {
description: "The blog for Dave Smith-Hayes, a dad and developer.",
},
});
});
export default app;

View File

@ -1,34 +1,37 @@
import { Hono } from 'hono';
import { PostPage } from '@blog/templates/Pages/PostPage';
import { FourOhFour } from '@blog/templates/Pages/FourOhFour';
import { readdir } from 'node:fs/promises';
import { SiteMeta } from '@blog/model/SiteMeta';
import { readPostMarkdown } from '@blog/post/post-reader';
import { SiteMeta } from '@blog/models/SiteMeta';
import { PostFileService } from '@blog/services/post-file';
const app = new Hono();
type Posts = {
postService: PostFileService
}
const app = new Hono<{ Variables: Posts }>();
app.get('/:slug', async (c) => {
const postSlug: string = c.req.param("slug");
const fileName: string = postSlug + '.md';
const files = await readdir(__dirname + "/../../posts");
const postService: PostFileService = c.get('postService');
const postFile = files.find(f => f === fileName);
if (!postFile) {
c.status(404);
try {
const post = postService.getPost(postSlug);
const meta: SiteMeta = {
description: "Page does not exist."
description: post.meta.description,
tags: post.meta.tags,
author: "Dave Smith-Hayes"
};
return c.render(<PostPage post={post} />, { meta });
} catch (e) {
const description: string = "Page does not exist.";
console.error(description);
console.error(e);
c.status(404);
const meta: SiteMeta = { description };
return c.render(<FourOhFour />, { meta });
}
const post = await readPostMarkdown(__dirname + "/../../posts/" + postFile);
const meta: SiteMeta = {
description: post.meta.description,
tags: post.meta.tags,
author: "Dave Smith-Hayes"
};
return c.render(<PostPage post={post} />, { meta });
});
export default app;

View File

@ -4,6 +4,8 @@ import { Page } from '@blog/templates/Page';
import home from '@blog/handlers/home';
import posts from '@blog/handlers/posts';
import type { SiteMeta } from '@blog/models/SiteMeta';
import { postFileMiddleware } from '@blog/middleware/post-service';
import { logger } from 'hono/logger';
declare module 'hono' {
interface ContextRenderer {
@ -13,7 +15,10 @@ declare module 'hono' {
const app = new Hono();
// Render the JSX views
app.use(postFileMiddleware);
app.use(logger());
// JSX Rendering for Templates
app.get(
'*',
jsxRenderer(
@ -22,7 +27,7 @@ app.get(
)
);
console.log({ message: "Bootstrapping the routes..." });
console.log(JSON.stringify({ message: "Bootstrapping the routes..." }));
app.route('/', home);
app.route('/posts', posts);

View File

@ -0,0 +1,14 @@
import { createFactory } from 'hono/factory';
import { PostFileService } from '@blog/services/post-file';
const factory = createFactory();
let postFileService: PostFileService;
export const postFileMiddleware = factory.createMiddleware(async (c, next) => {
if (!postFileService) {
postFileService = await PostFileService.create();
}
c.set('postService', postFileService);
await next();
});

View File

@ -1,7 +1,7 @@
import { PostMeta } from '@blog/model/PostMeta';
import { PostMeta } from "@blog/models/PostMeta";
export type Post = {
meta: PostMeta,
content: string,
html?: string
meta: PostMeta;
content: string;
html?: string;
};

View File

@ -1,4 +1,3 @@
import { Database } from "bun:sqlite";
import { SQLITE_DATABASE_FILE } from "@blog/config";
export const db = new Database(SQLITE_DATABASE_FILE);

View File

@ -1,19 +1,19 @@
import { POST_PATH, POST_ROUTE_PREFIX } from '@blog/config';
import { readdir } from 'node:fs/promises';
import type { Post } from '@blog/models/Post';
import * as yamlFront from 'yaml-front-matter';
import { marked } from 'marked';
import { POST_PATH } from "@blog/config";
import { readdir } from "node:fs/promises";
import type { Post } from "@blog/models/Post";
import * as yamlFront from "yaml-front-matter";
import { marked } from "marked";
export async function readPostFile(path: string): Promise<Post> {
const file = Bun.file(path);
const fileContent = await file.text();
const parsedData = yamlFront.loadFront(fileContent);
const parsedData = yamlFront.loadFront(fileContent);
const postHtml = await marked.parse(parsedData.__content);
let slug = parsedData.slug;
if (!slug) {
slug = path.split('/').pop().slice(0, -3);
slug = path.split("/").pop()?.slice(0, -3);
}
return {
@ -23,15 +23,15 @@ export async function readPostFile(path: string): Promise<Post> {
date: new Date(parsedData.date),
draft: parsedData?.draft || false,
tags: parsedData.tags,
slug: slug
slug: slug,
},
content: parsedData.__content,
html: postHtml
html: postHtml,
};
}
export class PostFileService {
private posts: Map<string, Post>;
private posts: Map<string, Post>;
public constructor(posts: Map<string, Post>) {
this.posts = posts;

View File

@ -11,7 +11,16 @@ WHERE id = $id
const query = db.query(queryString);
const results = query.get({ $id: id });
return {}
return {
meta: {
title: results.title,
slug: results.slug,
description: results.description,
date: new Date(results.published_date),
draft: results.is_draft,
},
content: results.raw_content,
}
}
public async getPostBySlug(slug: string): Post {

View File

@ -6,7 +6,7 @@ export function PostList({ posts }: { posts: PostMeta[] }) {
{posts.map(p => {
return (
<li>
<a href={p.slug}>{p.title}</a>
<a href={"/posts/" + p.slug}>{p.title}</a>
</li>
);
})}