CLI Application for Managing Posts

CLI Application for Managing Posts

Daily short news for you
  • For over a week now, I haven't posted anything, not because I have nothing to write about, but because I'm looking for ways to distribute more valuable content in this rapidly exploding AI era.

    As I shared earlier this year, the number of visitors to my blog is gradually declining. When I looked at the statistics, the number of users in the first six months of 2025 has dropped by 30% compared to the same period last year, and by 15% compared to the last six months of 2024. This indicates a reality that users are gradually leaving. What is the reason for this?

    I think the biggest reason is that user habits have changed. They primarily discover the blog through search engines, with Google being the largest. Almost half of the users return to the blog without going through the search step. This is a positive signal, but it's still not enough to increase the number of new users. Not to mention that now, Google has launched the AI Search Labs feature, which means AI displays summarized content when users search, further reducing the likelihood of users accessing the website. Interestingly, when Search Labs was introduced, English articles have taken over the rankings for the most accessed content.

    My articles are usually very long, sometimes reaching up to 2000 words. Writing such an article takes a lot of time. It's normal for many articles to go unread. I know and accept this because not everyone encounters the issues being discussed. For me, writing is a way to cultivate patience and thoughtfulness. Being able to help someone through my writing is a wonderful thing.

    Therefore, I am thinking of focusing on shorter and medium-length content to be able to write more. Long content will only be used when I want to write in detail or delve deeply into a particular topic. So, I am looking for ways to redesign the blog. Everyone, please stay tuned! 😄

    » Read more
  • CloudFlare has introduced the pay per crawl feature to charge for each time AI "crawls" data from your website. What does that mean 🤔?

    The purpose of SEO is to help search engines see the website. When users search for relevant content, your website appears in the search results. This is almost a win-win situation where Google helps more people discover your site, and in return, Google gets more users.

    Now, the game with AI Agents is different. AI Agents have to actively seek out information sources and conveniently "crawl" your data, then mix it up or do something with it that we can't even know. So this is almost a game that benefits only one side 🤔!?

    CloudFlare's move is to make AI Agents pay for each time they retrieve data from your website. If they don’t pay, then I won’t let them read my data. Something like that. Let’s wait a bit longer and see 🤓.

    » Read more
  • Continuing to update on the lawsuit between the Deno group and Oracle over the name JavaScript: It seems that Deno is at a disadvantage as the court has dismissed the Deno group's complaint. However, in August, they (Oracle) must be held accountable for each reason, acknowledging or denying the allegations presented by the Deno group in the lawsuit.

    JavaScript™ Trademark Update

    » Read more

Issue

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.

CLI Application

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.

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (4)

Leave a comment...
Avatar
Xuân Hoài Tống7 months ago

Thôi nào các bác, đăng nhập vào có khi vui hơn ấy 😅

Reply
Avatar
Ẩn danh7 months ago

Gòi toàn ẩn danh thật thật giả giả 👈🫵👉

Reply
Avatar
Ẩn danh7 months ago

bài viết hữu ích

Reply
Avatar
Ẩn danh7 months ago

tuyệt's vời

Reply