From 908141aeff9dd74ea6da67f82190484ac1273f82 Mon Sep 17 00:00:00 2001 From: Dave Smith-Hayes Date: Thu, 11 Jul 2024 21:37:12 -0400 Subject: [PATCH] init the database and move posts into the sqlite database. --- bun.lockb | Bin 28248 -> 28248 bytes db/blog.sqlite | Bin 0 -> 40960 bytes package.json | 4 +- src/config.ts | 1 + src/db/init.ts | 59 ++++++++++++-------- src/models/Post.ts | 2 +- src/post/post-reader.ts | 62 --------------------- src/services/database.ts | 11 ++-- src/services/post-file.ts | 8 ++- src/services/post-repository.ts | 4 -- src/services/repository/post-repository.ts | 19 +++++++ 11 files changed, 70 insertions(+), 100 deletions(-) create mode 100644 db/blog.sqlite delete mode 100644 src/post/post-reader.ts delete mode 100644 src/services/post-repository.ts create mode 100644 src/services/repository/post-repository.ts diff --git a/bun.lockb b/bun.lockb index 6927e45694a4ed020125359c3a59237a5fbf0949..da0768bad3489c00b78ed15e4f0bcb44a80d46f0 100755 GIT binary patch delta 246 zcmca{hw;W8#tC`~S;x=5f5h9QUdMT5({cVanXezmOs$qHJNHUe?%-p_+gcm_ehE$X zU^kdtAuPbiJ9#6E`sNM7UnT3+7xef1*YuvPuvThgsPq$#PRSPsV_iB9Pi8e}n4hkn zetX8bRg&zDH*~Ao^qGn;_?@%c7;m$sUt>$hC;2%CZ=7LdU|?kU$NvvVTQeX4OYy2z zRpqKP`8F)AdDY?Rlb!MQaqjsL%>``Rn``uPZUvuTl4)J;=_h$Ay~fezXu2BHvy@4j uS1ju^=I@%nK%aZszs*kGqzl0`t zuo_IR5EfwMn!J%kee(w4uafoe95Pz<+SX25&viIFT{SdTb@xBPM%j0^^Z08Mell~O z6bjmP?9Gu@9rcYHWiq!LFFTn#&wqN6mmE)dXw_`JvKmGP21bT|{QrQoH3I^$oL^u1 z>3w#rs3nKS8iVq(ovs;<<-8}ot{A0Bybb(%=^wjF-oLgLJd)ZU_at1od%a-S=B|VF uh9%#0{)A0lwER}voz1p-nYN6ElNqDrCm+ieoZOhCFgYNbee=KUC?)_s?P(4G diff --git a/db/blog.sqlite b/db/blog.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..cd949cd4e781273987d74563de7a5c4a5ceeb875 GIT binary patch literal 40960 zcmeHQOLH5?b|(Fzjf(ATTwL5un^sKHmjrd~+^R&R=}U>Qj0#Q35*^27IlutZ0Oo2i z1J4Wyqf)MvayO~`g#45&@(;4gpU67dWRXR_?{v?D04Py!UEXF<0x{^v>F+%HoEcPq zcfUCbGOc=XGW0U_+0vgcEiW(qPN}7(r4{_E;a~pqCjNLMe}mWM%HQYxxw2G0{pWYz z|KFvRKl}To_x|Vo|9J0T-h1ag@7+zjbw3UPhk!%CA>a^j2si{B0uBL(z@g3@1QA5qNRU0*>0$(+cPZ~EH zchv1W&94vce6Rkd@%n3z>acOtfG{@>?%p^!Y}7U%{P@<&>W3dL|KZJIDQPS7`YA^; zf33_dz+4yE(X<5tjWi1co)yrvKO-M!TPz;e`!vf_waveMV`cS&50)RiFU#Xp{P*UW zCGpB56oDqPM)mD+^P6vh+xnYtt?quXyc9&f{yF_AM77!S##t=y1;EyCIPP2h*H>15 z^{eFve<1>($skTr`|`#aG3>=hND_HNUBIn}UJ%MVX!5(|mDSbNA8 z(j(@`f-E$XsN-8F>iFBEBdQ_|#}$IB$ zqb!J{Lio|R69(x(`z_x?t$qk6VbjE$w5mp4+uVHZt<}$0&%@dn>Tl%bb%dF>8EW3e zzvb8dbqT-j$06_vA@FZMEZ3G=zkU0`_CNmjN4FENn+07jRE?j#;RsF__tXtUcwSeB z`c#MUXozr6hv2nszr1!JkzGv&I?+L-n%2oWT6tovCak zG7);;{_6H!+$lR4CF<&5sZXzd`WgPac9p;C=GQ0NSN{@!Y&5(y(@CTby=fTq2iS#z zq28?3nrh&kYSqy?Qp0f=asmuGoT`ELeBOqGOp`%?1xE2id7XHisR90Ks)>jF%vV{g zGB3PuKgtF{s!nx68HYo}48)J1AE}X-$^4LX5-0cT>W4%5U}ix-ZVa+)lwRA}(NTR8 z+z&?D54?Ju^mq7f$8>6SrV?lf!6xDtM3gDx(8rdnJ+2^)d)dTG0BM9x+N`n{kMU+W zO?BAg6;2NE_0OZwLzf_v#+gPgz&CLQ1?9-=1k>cg-;II6NcRBPl#|J-8OOFTRo9D@ z4uMp{;0OpNgQ*&)oXo3eE(Pe3?giZdMkHhC94v}lF&COv=p}uuWi$$5bPUF6y;eIR z*qt!$8+*bV9|K0%(I8e7XI6cw_R=_lstQc(;8@+ZqJ}dSqD`jKK|BVA?ApZKwj8cCrdcsRnTPPxW32KHXm4Y#ar&|<^@j&dQqx*IObcJmR1_>l!u(EIgdtl~Ce{t|qh#2mnhA~D z5(r-ZeX0txptGG!NBXX$Y>3dQfS5?cpLkOmTt7AbmZF*hEDX z2AVuj*46bfBDEK$@fH+m8Z}i<>rjQkeOa5V#sglXpbM!nLy$)6@MLUtP=?tDvBemX zDHDSnmJ|B))ryF+2Vp8;-GL{F)pQYV6DusWVKxl}K@CflRN@%LLOQap9YaffCL6Hk ze6!*n%xUvT1*_t|TC05-L?&pT#SW$oqmUE>?!hX7(wKE{oWq!YM^oTCYf-Wk=$vcF z5MZRq5b6$TScILy7lWRGMLY|7E&zu)aM1Rx1ktt)2rz&cm0Z2f0B5hlR-&REhQNk7 zPBX+N_|iD(4j2ORXoXig5KS&cKh?GaNjD3aVjv#Td~9ng!#bS{vHApJ-LM&2t%hxV zqSp~xOw7hW%qViV-%{d7UnprNm=u<20-XfW_8s0w2(mbtwpBOu(i9O^z{;p!fa9t5 zp^aylJHn2Fz}2>!i)o6e0OvN&hz@80si11xn11Z3ucvpvIV!{xT>+ka%3L)VXT*&T zw>wNe(9OnTNqwD3+@TVTO1M&BDgl94%at=W4W=j)rH8E3m_Z$AAha^@7-b-t0n|3R zG8MonhAs;~1$c!*?E3au+ybt!UqFw?~6fZSM>IUt=*(|AgZpf4*1h z{NBMR*say*#`4M6G%i>Y&y~79m%kTf!5`Nu}L%<>65O4@M1RMem0f&G?z#-rea0oaA90Gsj z2&}&T+jsJNm(^#ZeEyFw{ktEBfJ49`;1F;KI0PI54grUNL%<>65O4@M1THZGuKvHo z%5_U~2si{B0uBL(fJ49`;1F;KI0PI54grUNML-VtbI<>sJvam$0uBL(fJ49`;1F;K zI0PI54grUNL*P;);GX|qYW2FsIRqR64grUNL%<>65O4@M1RMem0f&G?KoF4g|Cj!A ziNBZR=ik?sK~?yFH}^*SFYUPXJx>i1-TQp)anDy@+h2H4``(VX&jWw|Bu`%7+ZpZG zpiMcGyFBFywbP5uf$QRz z;*svq_B0Z+fAtS;&u!?{J-&U9><{~L2K3&J{5^A$yJ;Y&u%Brlhq^E5E$PF>&v#!i zyo_+(QSb9Jnh01gHiPQv@5Oc?r@Kqbr#uMW%wL}Bonw+8&^~h}ylH+ocQAb3&2W#n zlkn+L?Dn%Xj3*pfLy{9OjR+>dAEhnxIg) zPz=Wj!c2Kqzt;y(2Dp;I7yx#NtyvBOUI6t;F#?ICa^8bD4C4uhEZ75{osHZI{XBM5 z=?9uoF9EYt0(Km5F5JtTl)MuqQ$Yz&Nmo!_E3~s)_IL=lfdsi(UV{>qboL_|9`(L_ z{oj&jynO9kob}-{z)xBV%Q&&~$tOZ1p*~T+I|WtEyxk};u9Q|pLK0GY0EVKi9bm`s zQ6#}B$-=Jqa~mAYbrH!$ixh9hayJ%Ce+AB0&c7)`7`6-!c7;Sk+xzpAsm~Ezsyz+c z#V?Cxcqq}v%#=Gj%8!Ld%TOZ;C(5~UAWV>H2<%=U-t^Q@W6){IfFgYoPq<_Xd)`8} z_odIq6?xe-%V6hX6bMw(w(!P*A$c*L$=SDZrk$dTp#s6QBwjIWA6Kj_KdcK)NGlo- zJ33kau`*+#5nCb~wa?%Q5$g4N>GonHhE@+uR|63D)Jn1ta1otCCWK%z=ov~EE)H}p z!r|hBpQ@XI0;x(|Lk+VF>BFkjHSLN+wn4-HL3jgNA;b8D{Uie=8_2)F z*p@SSkpYh!NNfl)KyVL%(+5Ola>oZmv;w^ z6M?_18Id>+G(WAJJC2gfQO7On0glF;GbR;e!1x9RIW-t#<^+r_++G%SgoSkykMLPz z`vDYH+lS4PV-X3+wjT();--+W5qOXX3iqBmYzj3Jb~~^yt$n4z1Bf*Wl?#PW2i{1? zn9!%f;UclSkho3Z+Orfn72OzCoN}9it`1P4bAmJTr`|NZrq&K7n&d-Nq!-;M0pEv) z6tEwY$BrD1Aj0w>YG4p2aTqzr5KFpHw53Nf)SIBNlZhTCJ??O(B>b|BfAd;9LR16U zTLf^76_7tF)tMTPgx-dpGzY-37XK!r-5dZpnNlWBm`5;6MpwAUVlBZWNjx;aV!#3Z zPY}LY8Z3lBD6LEx06PhLis9`7?CnhQ>XpQ377B9rR8=%bk#q%^lgAJ6n1FKFyoZQw zE^Q!w)QZg@Z!otvNV9@VX(pMe$UlqXZq0%MS6<8{4BXu#c->5%_xR@7j3)TpPvBQ+7qRa&mf1v8edcn z167t7Jys5Lrsosxxw4s6o>-6o5sYZeOA1~{ZkEO}Ek$dJG9o8=HTxV*^?c)kC4+*m z5B^6(gUSJg$x|K=fQSwTKbFnZ+OOSHIcj0T!gyp)42MW~CCIzL%&o~FnEBB8-`ltm4Mc8jQgdxNc$)o`bwq*fF?#+P+b;v!( zgtBe$p~fI6=AI1pw;oi89UK|d8J1-;hA>ltYjzzRxHLIZs)EE5n;2eyiRNtUY^H*vo;tn$DpPSxxOMIn*m@Z+-HKUuD-{ILnL#OQz7L9>#yZ`ZQ?qz zR5s*@wD*E=unjUn$*XudXF|1+kR!CDL-=xk5!ci>zxVMwIpLoL|wNPwN zSPgPq6e39d?0gtPFz1d__!(324HgxK{LxY?LX|_I_XZbtWRO)x$WmxrzOVJj@CH-E z{R*dPaeu0f;e)67Zt>jOFb%xf85V@^DVndAxFT@fh2@{DO;8 zxj9=cOy$BeEKV2Qc6_FVs=Ys1rs8(Sms7LyA>yK1(mYZ;rwA&Zr!4MiAF~(1<|(V# zxo@&ms@S!CQ{wX5DUT><&G!}_TGcMRbzCeXe=dUp*EF2Xl^;@`6gqxLt!o5aQ1Dj1 zaq%=&?*&))hyu8HP%>Kv=TFdPO5w#&JtQZ}VV2uz+^CuO<+1cRRK*sS1^$>X^(o6_ zJIqC&3Y8URUL;@YDw=!9lNQp}Q~I*s1vwO%>@e@~mS8gu=4Bq;l<;i~3 zBLsw!G8(dDdgUIk2$Gw~cQ%^Tsc= O;GYxy@oRqx3I7iz*l||? literal 0 HcmV?d00001 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 { + + } +}