Fix some of the blog post, get the container built, and remove config for the database.

This commit is contained in:
Dave Smith-Hayes 2024-08-07 21:53:04 -04:00
parent 4c8f3063b2
commit bd883ac4ef
7 changed files with 29 additions and 24 deletions

1
.env.sample Normal file
View File

@ -0,0 +1 @@
DEPLOY_MODE="development"

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
# deps
node_modules/
.env

View File

@ -4,9 +4,9 @@ WORKDIR /app
COPY bun.lockb .
COPY package.json .
COPY tsconfig.json .
RUN bun install --frozen-lockfile
RUN mkdir src posts
COPY ./src ./src
COPY ./posts ./posts

3
build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
docker build --pull -t dsh-blog . --no-cache

View File

@ -11,7 +11,7 @@ slug: fullstack-hono
draft: true
---
# Context and My Blog
## Context and My Blog
I like my blog. I don't write nearly as much as I should for it but at the endof the day I like the three small posts that I have put into it. However, I never really liked how I had it set up. I have no real qualms with the SSG I chose ([11ty](https://www.11ty.dev/)) or the web server to host the HTML (NGiNX) but I never really felt like it was _mine_.
@ -27,7 +27,7 @@ But that made no sense once I _actually_ thought it. What advantage does having
_Note:_ I am going to talk a lot about JavaScript and TypeScript, and I want to make it very clear that I am talking about running JavaScript on the server. I feel like when people talk about JavaScript, there is an assumption we are talking about building frontend SPAs and not high-IO server applications (for good reasons, honestly). However, in my career and experience, the bulk of JavaScript I have written runs on Node.js. Anyways, after such a long introduction, here is my blog post about what I learned building a very basic blog with Hono and Bun.
# Organizing Hono applications
## Organizing Hono applications
I make no effort to hide the fact that I come from the MVC Web Framework world. I have spent a lot of time in my career thinking of web applications in the terms of Models, Views, and Controllers. This has made a lot of sense to me over the years, and I posit that it's still a great framework for organizing your code. However when it comes to JavaScript, it feels verbose to create classes full of methods to handle requests and responses. I think this verbosity comes from how JavaScript code is actually organized for Node.js (Deno, Bun, etc).
@ -37,7 +37,7 @@ So with my understanding that a Class in JavaScript should be a collection of me
I like to call this pattern Handler, Service, Presentation. This pattern is nearly identical to MVC and you can immediately see the analogues to the original acronym. I don't think there is a clear advantage of using these words in particular, other than it can hopefully erode some of the _web-brainrot_ on how we organize our we applications.
## An Example of a Service
### An Example of a Service
Let's think of a Blog. This blog. What services do we have in the code for this blog? Right now, it is solely the `PostService`. This is a class that is given a list of `Post` types, and creates an internal Map of that with the `slug` as a key. From there we can do things like, get all the posts, get the latest post, get a post by slugs, get un-published posts, etc.
@ -45,7 +45,7 @@ Within the `PostService` module is two helper functions. One of these is an `asy
Another good way to think of a service, is a Repository class. Think of a class that should handle querying data to and from a database. If you need another example, image an HTTP Client for a specific HTTP API. Think of something that provides data to something else. I guess that's how I'd describe it.
## What Handlers Are
### What Handlers Are
Handlers should be thought of as callback functions for particular requests and responses. The _handle_ the request and response. In the world of Hono, we get to decide what Middleware is type'd into the Application and can be accessed within a handler. This allows us to bootstrap our middleware elsewhere and be assured it will be there when it runs.
@ -95,7 +95,7 @@ _`src/handlers/posts.tsx`_
Now I haven't done this yet - but if I need to test the `handleSinglePost` function, I can properly mock the `Context` object with the right `PostService` class.
## How Presentation Works
### How Presentation Works
Like I mentioned earlier, one of the selling points of using Hono for the framework was its suppose of rendering JSX with `jsxRenderer` middleware. Its trivial to set up, but you have to remember to re-save your files as `tsx` and `jsx` if you want to use it. I have not spent a lot of time writing JSX in my life but once I got some of the basics it became super easy to understand. I can understand why people like React, honestly.
@ -151,12 +151,12 @@ app.get(
);
```
# Development
## Development
If the environment variable `ENVIRONMENT` is set and the value is `DEVELOPMENT`
then we can easily grab all the Posts, including Drafts, from the
`PostService`.
# Deployment
## Deployment
We can simply depoy the application within a Docker container into Dokku.

View File

@ -1,4 +1,3 @@
export const POST_PATH: string = __dirname + '/../posts';
export const STATIC_PATH: string = __dirname + '/assets';
export const POST_ROUTE_PREFIX: string = '/posts'
export const SQLITE_DATABASE_FILE: string = __dirname + '/../db/blog.sqlite';

View File

@ -61,6 +61,16 @@ export async function createPostService(path: string): Promise<PostService> {
return new PostService(posts);
}
export function sortPostsByDate(posts: Post[], desc: boolean = true): Post[] {
return posts.sort((p1: Post, p2: Post) => {
if (desc) {
return p2.meta.date.getTime() - p1.meta.date.getTime();
}
return p1.meta.date.getTime() - p2.meta.date.getTime();
});
}
export class PostService {
private posts: Map<string, Post>;
@ -73,28 +83,19 @@ export class PostService {
}
public getAllPosts(): Post[] {
return Array.from(this.posts.values());
return this.getPostsSortedByDate();
}
public getPostsSortedByDate(desc: boolean = true): Post[] {
return Array.from(this.posts.values())
.sort((p1: Post, p2: Post) => {
if (desc) {
return p2.meta.date.getTime() - p1.meta.date.getTime();
}
return p1.meta.date.getTime() - p2.meta.date.getTime();
});
return sortPostsByDate(Array.from(this.getPosts().values()), desc);
}
public getPublishedPosts(desc: boolean = true): Post[] {
return this.getPostsSortedByDate(desc)
.filter((p: Post) => p.meta.draft == false);
public getPublishedPosts(): Post[] {
return this.getAllPosts().filter((p: Post) => p.meta.draft == false);
}
public getDraftPosts(desc: boolean = true): Post[] {
return this.getPostsSortedByDate(desc)
.filter((p: Post) => p.meta.draft == true);
public getDraftPosts(): Post[] {
return this.getAllPosts().filter((p: Post) => p.meta.draft == true);
}
public getPost(slug: string): Post {