Hello readers of 2coffee.dev. Another week has passed, so quickly that when looking back at the homepage, I couldn't believe that I had stopped writing for such a long time. To be honest, last week was quite busy as I was focused on releasing an important update for the product at work. Perhaps that's why the concept of time has temporarily been buried. Hmm... Need to think of something to talk about!
Alongside that, I am trying to optimize the user experience for the blog. By fixing some lingering bugs, making links display more clearly, adding a few fun effects, and tweaking the interface. Just that alone took up a lot of time. Working in the morning, coming back in the evening to spin in a never-ending optimization cycle.
A new issue arose during optimization: the markdown rendering process encountered problems. Specifically, when using some new tags, the website's layout broke. Oh! When will this ordeal end? If I remember correctly, there must be 2-3 articles discussing the issues encountered with markdown. Perhaps I took it too lightly before, so now I have to face problems like this.
Since I have to deal with it anyway, I decided to fix it all at once. I no longer use showdown but switch to another library called marked.js. I also choose the gfm standard of Github for formatting. Doing so will limit future changes.
In many previous articles, I used a markdown tag to display the thumbnail image for the post. But after rebuilding, the thumbnail image display was written directly into the code. The image at the beginning of the article is temporarily hidden using code. Now with the new feature, all unnecessary markdown tags need to be removed.
So the next task is to fix a series of articles. The website operates quite interestingly, with a very simple management page. Unlike previous versions with a "fancy" management page, this version only has a few essential features like creating/updating posts. To access the edit page, just add a /edit
postfix to the post's URL, and it will lead to the edit page. Of course, you need to log in with an admin account; otherwise, you'll see an error message stating that the page is unavailable.
Roughly speaking, there are over 300 articles that need to be edited, equivalent to performing the edit operations over 300 times 🤮. Not to mention, the network doesn't always load the webpage within 3 seconds. Therefore, once again, I turned to the CLI solution.
The idea of a CLI application to manage posts is not new. In fact, since building the new version for the blog, I've thought about managing posts with command lines. Each post is a markdown file, stored locally, edited, and then published with a command. It's both fast and convenient. But when seriously "breaking" down what needs to be done, oh my... perhaps I should do it the simpler way. Remember the mission of 2coffee.dev, to bring quality articles, not to be stuck for half a month without achieving anything.
Now the time has come, with some groundwork already built and reusable. The CLI application stems from the need to manage posts and edit them quickly. Therefore, the basic function just needs to pull the list of posts, batch edit, and push the posts, inspired by using git.
If you remember the article using CLI to optimize images, I created a CLI to process images before. Initially, I planned to continue writing new code here, but in reality, I had to write a lot more code, so the quickest way was to write it together with the page-fresh project - which is this blog. It contains a lot of code to fetch data from the server, which I can leverage without rewriting.
To simplify as much as possible, files like pull.ts
, push.ts
... represent the pull
and push
commands. pull
is to fetch posts, and push
is to publish posts. Every time you need to run, the syntax will be:
$ deno run -A pull.ts
$ deno run -A push.ts
To support additional flags, like --change
, --force
similar to mainstream CLI applications, just add them to deno run
and use Deno.args
to parse and process data.
const args = Deno.args;
const flags: { [key: string]: string | boolean } = {};
for (let i = 0; i < args.length; i++) {
if (args[i].startsWith("--")) {
const flag = args[i].substring(2); // remove '--'
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
flags[flag] = args[i + 1]; // get the flag's value
i++;
} else {
flags[flag] = true;
}
}
}
pull.ts
contains the code to fetch data. When running the pull
command, it will pull all the post content and save it as *.md
files, with * being the post's URL.
$ deno run -A cli/commands/pull.ts
ℹ Pulled 212 posts, skipped 0 posts, changed 0 posts (VI)
ℹ Pulled 202 posts, skipped 0 posts, changed 0 posts (EN)
✔ Done
After that, I created an article.ts
file to check for posts that have changed locally. These are the edited posts and are ready to be published. For example:
$ deno run -A cli/commands/article.ts --change
ℹ Changes in Vietnamese posts:
- 1-thang-hoc-rust-nhung-dieu-co-ban-cua-ngon-ngu-lap-trinh-rust.md
ℹ Changes in English posts:
- a-compilation-of-free-and-high-quality-serverless-databases.md
✔ Done
After editing, use the push
command to publish a specified post. For example:
$ deno run -A cli/commands/push.ts --file data/1-thang-hoc-rust-nhung-dieu-co-ban-cua-ngon-ngu-lap-trinh-rust.md
Or push
all the edited posts. For example:
$ deno run -A cli/commands/push.ts --change
ℹ Found 1 Vietnamese post, 1 English post
- 1-thang-hoc-rust-nhung-dieu-co-ban-cua-ngon-ngu-lap-trinh-rust.md
- a-compilation-of-free-and-high-quality-serverless-databases.md
✔ Updated 1-thang-hoc-rust-nhung-dieu-co-ban-cua-ngon-ngu-lap-trinh-rust.md
✔ Updated a-compilation-of-free-and-high-quality-serverless-databases.md
✔ Done
Immediately, the found posts are pushed. In push.ts
, there is a function that calls the API to publish posts with admin rights. So it is necessary to have the admin account's cookie. The cookie can be obtained by copying it from the website and pasting it directly into the code. However, the cookie has a rather short expiration, so writing an additional login.ts
file to handle fetching the cookie will be more convenient for future use.
The login mechanism is simple. Inside login.ts
, a local server is initialized listening on port 3069
, with an endpoint named /callback
. When calling to 2coffee.dev, for example, https://2coffee.dev/auth?redirect_url=http://localhost:3069/callback?token=<cookie>
, it will call back to the /callback
endpoint with the token
, which is the admin's cookie, provided the admin has logged in beforehand. Save the token into certificate.txt
for future use.
Combining it all, I have 4 files: login.ts
, pull.ts
, push.ts
, articles.ts
. If you keep typing deno run
repeatedly, it will be very long, so think of a way to simplify it. A command like 2cf
, so then just type 2cf login
, 2cf pull
, 2cf push
... which is much more convenient.
Create a cli.sh
file right in the page-fresh project directory:
#!/bin/bash
case $1 in
login)
cd /Users/hoaitx/src/hoaitx/page-fresh && deno run -A cli/commands/login.ts $2
;;
pull)
cd /Users/hoaitx/src/hoaitx/page-fresh && deno run -A cli/commands/pull.ts $2
;;
push)
cd /Users/hoaitx/src/hoaitx/page-fresh && deno run -A cli/commands/push.ts $2
;;
article)
cd /Users/hoaitx/src/hoaitx/page-fresh && deno run -A cli/commands/article.ts $2
;;
*)
echo "Usage: $0 {login|pull|push|article}"
;;
esac
So now you just need ./cli.sh login
, ./cli.sh pull
...
Here I use Mac and have Oh My Zsh installed, so I can set an alias for the command line. Continue to open the .zshrc
file and add function 2cf()
.
function 2cf() {
/Users/hoaitx/src/hoaitx/page-fresh/cli.sh "$@"
}
Reload the .zshrc
:
$ source ~/.zshrc
And that's it, from now on you can use the 2cf
command:
$ 2cf login
$ 2cf pull
$ 2cf push
...
Finally, to make the CLI more lively, I use two packages: ora and chalk. Ora is used for processing effects, and chalk is used to change the text color in the terminal.
In the future, I will continue to develop this CLI application with the aim of completely replacing the management page and moving towards some other automated features.
Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.
Comments (4)