diff --git a/bun.lockb b/bun.lockb index 6927e45..da0768b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/db/blog.sqlite b/db/blog.sqlite new file mode 100644 index 0000000..cd949cd Binary files /dev/null and b/db/blog.sqlite differ diff --git a/package.json b/package.json index f5a1b81..da7cd70 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ }, "dependencies": { "@types/yaml-front-matter": "^4.1.3", - "hono": "^4.4.10", - "marked": "^13.0.1", + "hono": "^4.4.13", + "marked": "^13.0.2", "remark": "^15.0.1", "yaml-front-matter": "^4.1.1" }, diff --git a/src/config.ts b/src/config.ts index 9424dc1..aa64bb2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,4 @@ export const POST_PATH: string = __dirname + '/../posts'; export const STATIC_PATH: string = __dirname + '/../static'; export const POST_ROUTE_PREFIX: string = '/posts' +export const SQLITE_DATABASE_FILE: string = __dirname + '/../db/blog.sqlite'; diff --git a/src/db/init.ts b/src/db/init.ts index 424e73f..7cf4704 100644 --- a/src/db/init.ts +++ b/src/db/init.ts @@ -1,13 +1,11 @@ -import { readdir } from 'node:fs/promises'; -import { openPostMarkdownFile, parsePostMetadata } from '@blog/post/post-reader'; -import { PostMeta } from '@blog/model/PostMeta'; -import db from '@blog/db/memory'; +import { PostFileService } from '@blog/services/post-file'; +import { db } from '@blog/services/database'; const createPostSql: string = ` -CREATE TABLE posts ( +CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, - slug TEXT NOT NULL, + slug TEXT NOT NULL UNIQUE, is_draft INTEGER DEFAULT 0, description TEXT, published_date DATE, @@ -16,7 +14,7 @@ CREATE TABLE posts ( `; const createAuthorSql: string = ` -CREATE TABLE authors ( +CREATE TABLE IF NOT EXISTS authors ( id INTEGER PRIMARY KEY, name TEXT, email TEXT @@ -24,14 +22,14 @@ CREATE TABLE authors ( `; const createTagsSql: string = ` -CREATE TABLE tags ( +CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY, tag TEXT UNIQUE ) `; const createPostsTagsSql: string = ` -CREATE TABLE posts_tags ( +CREATE TABLE IF NOT EXISTS posts_tags ( tag_id TEXT, post_id INTEGER, @@ -41,7 +39,7 @@ CREATE TABLE posts_tags ( `; const createPostHtmlCache = ` -CREATE TABLE post_html_cache ( +CREATE TABLE IF NOT EXISTS post_html_cache ( id INTEGER PRIMARY KEY, post_id INTEGER, content TEXT, @@ -69,27 +67,44 @@ export async function setupDb(): Promise { } -export async function readPostsToDatabase(postPath: string): Promise { - const posts = new Array(); +export async function readPostsToDatabase(): Promise { + const postService = await PostFileService.create(); const createPostSql: string = ` -INSERT INTO posts (title, slug, description, is_draft, published_date, raw_content) -VALUES ($title, $slug, $description, $is_draft, $published_date, $raw_content); -`; + INSERT INTO posts (title, slug, description, is_draft, + published_date, raw_content) + VALUES ($title, $slug, $description, $is_draft, + $published_date, $raw_content);`; - const fileList = await readdir(postPath); - for (const file of fileList) { - const contents: string = await openPostMarkdownFile(postPath + "/" + file); - const post = parsePostMetadata(contents); - + const createPostHtmlCacheSql: string = ` + INSERT INTO post_html_cache (post_id, content) + VALUES ($posId, $content);`; + + for (const [ slug, post ] of postService.getPosts().entries()) { const postQuery = db.prepare(createPostSql); - postQuery.run({ + const { lastInsertRowId } = postQuery.run({ $title: post.meta.title, - $slug: file, + $slug: slug, $description: post.meta.description, $is_draft: post.meta.draft ?? false, $published_date: post.meta.date.toString(), $raw_content: post.content }); + + const htmlCacheQuery = db.prepare(createPostHtmlCacheSql); + htmlCacheQuery.run({ $postId: lastInsertRowId, $content: post.html }) + console.log({ message: "Added " + post.meta.title + " to the database." }); } } + +async function main() { + await setupDb(); + await readPostsToDatabase(); +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.log(e); + throw e; + }); diff --git a/src/models/Post.ts b/src/models/Post.ts index f86534c..b373990 100644 --- a/src/models/Post.ts +++ b/src/models/Post.ts @@ -1,4 +1,4 @@ -import { PostMeta } from '@blog/models/PostMeta'; +import { PostMeta } from '@blog/model/PostMeta'; export type Post = { meta: PostMeta, diff --git a/src/post/post-reader.ts b/src/post/post-reader.ts deleted file mode 100644 index 042ed99..0000000 --- a/src/post/post-reader.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { marked } from 'marked'; -import type { Post } from '@blog/models/Post'; -import type { PostMeta } from '@blog/models/PostMeta'; -import * as yamlFront from 'yaml-front-matter'; -import { readdir } from 'node:fs/promises'; - -export async function openPostMarkdownFile(filename: string): Promise { - const file = Bun.file(filename); - return file.text(); -} - -type PostMetaWithRawContent = { - meta: PostMeta, - content: string -}; - -export function parsePostMetadata(post: string): PostMetaWithRawContent { - const parsedData = yamlFront.loadFront(post); - - return { - meta: { - title: parsedData.title, - description: parsedData.description, - date: new Date(parsedData.date), - draft: parsedData?.draft || false, - tags: parsedData.tags, - }, - content: parsedData.__content - }; -} - -export async function getPostList(path: string): Promise { - const postList: PostMeta[] = new Array(); - const dir = await readdir(path); - - if (!dir.length) { - return postList; - } - - for (const file of dir) { - const fileContent = await openPostMarkdownFile(path + '/' + file); - const postMeta = parsePostMetadata(fileContent); - postMeta.meta.slug = "/posts/" + file.slice(0, -3); - postList.push(postMeta.meta); - } - - return postList; -} - -export async function readPostMarkdown(filename: string): Promise { - const contents = await openPostMarkdownFile(filename); - const post = parsePostMetadata(contents); - post.meta.slug = "/posts/" + filename.slice(0, -3); - const parsedPost = await marked.parse(post.content); - - return { - meta: post.meta, - content: post.content, - html: parsedPost - }; -} - diff --git a/src/services/database.ts b/src/services/database.ts index 3d4782e..8f63e36 100644 --- a/src/services/database.ts +++ b/src/services/database.ts @@ -1,7 +1,4 @@ -export type ConnectionConfig = { - host: string, - username: string, - password: string, - database: string, - port: number -}; +import { Database } from "bun:sqlite"; +import { SQLITE_DATABASE_FILE } from "@blog/config"; +export const db = new Database(SQLITE_DATABASE_FILE); + diff --git a/src/services/post-file.ts b/src/services/post-file.ts index 1a10f77..e65dc8f 100644 --- a/src/services/post-file.ts +++ b/src/services/post-file.ts @@ -41,8 +41,8 @@ export class PostFileService { const posts = new Map(); const postFiles: string[] = await readdir(POST_PATH); - for (const postFile in postFiles) { - const post = await readPostFile(postFile); + for (const postFile of postFiles) { + const post = await readPostFile(POST_PATH + "/" + postFile); const key = post.meta?.slug || postFile.slice(0, -3); posts.set(key, post); } @@ -50,6 +50,10 @@ export class PostFileService { return new PostFileService(posts); } + public getPosts(): Map { + return this.posts; + } + public getPost(slug: string): Post { const post = this.posts.get(slug); diff --git a/src/services/post-repository.ts b/src/services/post-repository.ts deleted file mode 100644 index 8099153..0000000 --- a/src/services/post-repository.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export class PostRepository { - -} diff --git a/src/services/repository/post-repository.ts b/src/services/repository/post-repository.ts new file mode 100644 index 0000000..caf2e6c --- /dev/null +++ b/src/services/repository/post-repository.ts @@ -0,0 +1,19 @@ +import { Post } from '@blog/models/Post'; +import { db } from '@blog/services/database'; + +export class PostRepository { + public async getPost(id: number): Promise { + const queryString = ` +SELECT title, slug, is_draft, description, published_date, raw_content +FROM posts +WHERE id = $id +`; + const query = db.query(queryString); + const post = query.get({ $id: id }).as(Post); + return post; + } + + public async getPostBySlug(slug: string): Post { + + } +}