Generate OG images for all remaining posts
Deploy / build (push) Successful in 37s

This commit is contained in:
2026-09-09 06:17:30 +08:00
parent 493dab88ba
commit 1a61c30674
20 changed files with 411 additions and 0 deletions
@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1200px; height: 630px; overflow: hidden; }
body {
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
background: #0d1220;
color: #e6edf3;
display: flex;
flex-direction: column;
position: relative;
}
.topbar {
display: flex; align-items: center; gap: 14px;
padding: 34px 48px 0;
}
.avatar {
width: 48px; height: 48px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
background: #ffffff; padding: 4px;
}
.avatar img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand {
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 22px; color: #ffffff; letter-spacing: 0.3px;
}
.brand .domain { color: #6b7c99; font-weight: 500; font-size: 19px; }
.tag {
margin: 40px 48px 0;
display: inline-flex; align-items: center; gap: 8px;
width: fit-content; padding: 7px 16px; border-radius: 6px;
background: rgba(41, 92, 255, 0.16);
border: 1px solid rgba(41, 92, 255, 0.5);
color: #7599ff; font-size: 16px; letter-spacing: 0.8px; text-transform: uppercase;
}
.tag .dot { width: 8px; height: 8px; border-radius: 50%; background: #295cff; }
.title {
margin: 30px 48px 0;
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 40px;
line-height: 1.15; color: #ffffff; max-width: 1000px;
}
.title .accent { color: #ff5c5c; }
.terminal {
margin: 34px 48px 0;
background: #161e2e; border: 1px solid #2a3550; border-radius: 10px;
padding: 22px 28px;
font-family: "JetBrains Mono", "Fira Code", Menlo, monospace;
font-size: 21px; line-height: 1.6; max-width: 1040px;
}
.terminal .line { display: flex; gap: 12px; }
.terminal .prompt { color: #7599ff; }
.terminal .cmd { color: #e6edf3; }
.terminal .err { color: #ff5c5c; background: rgba(255, 92, 92, 0.10); padding: 1px 8px; border-radius: 4px; }
.terminal .fix { color: #3fb950; }
.footer {
margin-top: auto; padding: 0 48px 34px;
display: flex; align-items: center; justify-content: space-between;
}
.footer .tags { color: #6b7c99; font-size: 17px; }
.footer .tags span { margin-right: 18px; color: #7599ff; }
.footer .url { color: #6b7c99; font-size: 17px; }
</style>
</head>
<body>
<div class="topbar">
<div class="avatar"><img src="file:///D:/dev/hoelee-blog/public/logo-square.png" alt=""></div>
<div class="brand">Mr Hoelee <span class="domain">· blog.hoelee.com</span></div>
</div>
<div class="tag"><span class="dot"></span>devops · DevOps</div>
<div class="title">Authentik 2025.8 to 2026.8: The Breakable<br>Parts Nobody Warns You About</div>
<div class="terminal">
<div class="line"><span class="prompt">$</span><span class="cmd">docker compose pull authentik-worker</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">authorization_flow not found · SSO broken</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">uptime: 2025.8 → 2026.8</span><span class="fix">fixed ✓</span></div>
</div>
<div class="footer">
<div class="tags"><span>#authentik</span><span>#sso</span><span>#docker</span><span>#portainer</span><span>#self-hosting</span><span>#oidc</span><span>#upgrade</span></div>
<div class="url">blog.hoelee.com</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

+96
View File
@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1200px; height: 630px; overflow: hidden; }
body {
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
background: #0d1220;
color: #e6edf3;
display: flex;
flex-direction: column;
position: relative;
}
.topbar {
display: flex; align-items: center; gap: 14px;
padding: 34px 48px 0;
}
.avatar {
width: 48px; height: 48px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
background: #ffffff; padding: 4px;
}
.avatar img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand {
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 22px; color: #ffffff; letter-spacing: 0.3px;
}
.brand .domain { color: #6b7c99; font-weight: 500; font-size: 19px; }
.tag {
margin: 40px 48px 0;
display: inline-flex; align-items: center; gap: 8px;
width: fit-content; padding: 7px 16px; border-radius: 6px;
background: rgba(41, 92, 255, 0.16);
border: 1px solid rgba(41, 92, 255, 0.5);
color: #7599ff; font-size: 16px; letter-spacing: 0.8px; text-transform: uppercase;
}
.tag .dot { width: 8px; height: 8px; border-radius: 50%; background: #295cff; }
.title {
margin: 30px 48px 0;
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 52px;
line-height: 1.15; color: #ffffff; max-width: 1000px;
}
.title .accent { color: #ff5c5c; }
.terminal {
margin: 34px 48px 0;
background: #161e2e; border: 1px solid #2a3550; border-radius: 10px;
padding: 22px 28px;
font-family: "JetBrains Mono", "Fira Code", Menlo, monospace;
font-size: 21px; line-height: 1.6; max-width: 1040px;
}
.terminal .line { display: flex; gap: 12px; }
.terminal .prompt { color: #7599ff; }
.terminal .cmd { color: #e6edf3; }
.terminal .err { color: #ff5c5c; background: rgba(255, 92, 92, 0.10); padding: 1px 8px; border-radius: 4px; }
.terminal .fix { color: #3fb950; }
.footer {
margin-top: auto; padding: 0 48px 34px;
display: flex; align-items: center; justify-content: space-between;
}
.footer .tags { color: #6b7c99; font-size: 17px; }
.footer .tags span { margin-right: 18px; color: #7599ff; }
.footer .url { color: #6b7c99; font-size: 17px; }
</style>
</head>
<body>
<div class="topbar">
<div class="avatar"><img src="file:///D:/dev/hoelee-blog/public/logo-square.png" alt=""></div>
<div class="brand">Mr Hoelee <span class="domain">· blog.hoelee.com</span></div>
</div>
<div class="tag"><span class="dot"></span>notes · DevOps</div>
<div class="title">Hello, World —<br>About This Blog</div>
<div class="terminal">
<div class="line"><span class="prompt">$</span><span class="cmd">git init hoelee-blog · first commit</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">technical writing · self-hosting · build log</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">about → blog.hoelee.com</span></div>
</div>
<div class="footer">
<div class="tags"><span>#intro</span></div>
<div class="url">blog.hoelee.com</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1200px; height: 630px; overflow: hidden; }
body {
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
background: #0d1220;
color: #e6edf3;
display: flex;
flex-direction: column;
position: relative;
}
.topbar {
display: flex; align-items: center; gap: 14px;
padding: 34px 48px 0;
}
.avatar {
width: 48px; height: 48px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
background: #ffffff; padding: 4px;
}
.avatar img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand {
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 22px; color: #ffffff; letter-spacing: 0.3px;
}
.brand .domain { color: #6b7c99; font-weight: 500; font-size: 19px; }
.tag {
margin: 40px 48px 0;
display: inline-flex; align-items: center; gap: 8px;
width: fit-content; padding: 7px 16px; border-radius: 6px;
background: rgba(41, 92, 255, 0.16);
border: 1px solid rgba(41, 92, 255, 0.5);
color: #7599ff; font-size: 16px; letter-spacing: 0.8px; text-transform: uppercase;
}
.tag .dot { width: 8px; height: 8px; border-radius: 50%; background: #295cff; }
.title {
margin: 30px 48px 0;
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 46px;
line-height: 1.15; color: #ffffff; max-width: 1000px;
}
.title .accent { color: #ff5c5c; }
.terminal {
margin: 34px 48px 0;
background: #161e2e; border: 1px solid #2a3550; border-radius: 10px;
padding: 22px 28px;
font-family: "JetBrains Mono", "Fira Code", Menlo, monospace;
font-size: 21px; line-height: 1.6; max-width: 1040px;
}
.terminal .line { display: flex; gap: 12px; }
.terminal .prompt { color: #7599ff; }
.terminal .cmd { color: #e6edf3; }
.terminal .err { color: #ff5c5c; background: rgba(255, 92, 92, 0.10); padding: 1px 8px; border-radius: 4px; }
.terminal .fix { color: #3fb950; }
.footer {
margin-top: auto; padding: 0 48px 34px;
display: flex; align-items: center; justify-content: space-between;
}
.footer .tags { color: #6b7c99; font-size: 17px; }
.footer .tags span { margin-right: 18px; color: #7599ff; }
.footer .url { color: #6b7c99; font-size: 17px; }
</style>
</head>
<body>
<div class="topbar">
<div class="avatar"><img src="file:///D:/dev/hoelee-blog/public/logo-square.png" alt=""></div>
<div class="brand">Mr Hoelee <span class="domain">· blog.hoelee.com</span></div>
</div>
<div class="tag"><span class="dot"></span>case-studies · DevOps</div>
<div class="title">How I Built the<br>DigiKedai Telegram AI Bot</div>
<div class="terminal">
<div class="line"><span class="prompt">$</span><span class="cmd">user:"does this course have a free trial?"</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">bot → yes, here's your account ✓ (24/7, no human)</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">grammY · LiteLLM · Cloudflare tunnel</span></div>
</div>
<div class="footer">
<div class="tags"><span>#telegram</span><span>#docker</span><span>#cloudflare</span><span>#litellm</span><span>#typescript</span><span>#ai</span></div>
<div class="url">blog.hoelee.com</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+96
View File
@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1200px; height: 630px; overflow: hidden; }
body {
font-family: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
background: #0d1220;
color: #e6edf3;
display: flex;
flex-direction: column;
position: relative;
}
.topbar {
display: flex; align-items: center; gap: 14px;
padding: 34px 48px 0;
}
.avatar {
width: 48px; height: 48px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
background: #ffffff; padding: 4px;
}
.avatar img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand {
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 22px; color: #ffffff; letter-spacing: 0.3px;
}
.brand .domain { color: #6b7c99; font-weight: 500; font-size: 19px; }
.tag {
margin: 40px 48px 0;
display: inline-flex; align-items: center; gap: 8px;
width: fit-content; padding: 7px 16px; border-radius: 6px;
background: rgba(41, 92, 255, 0.16);
border: 1px solid rgba(41, 92, 255, 0.5);
color: #7599ff; font-size: 16px; letter-spacing: 0.8px; text-transform: uppercase;
}
.tag .dot { width: 8px; height: 8px; border-radius: 50%; background: #295cff; }
.title {
margin: 30px 48px 0;
font-family: "Space Grotesk", "Plus Jakarta Sans", sans-serif;
font-weight: 700; font-size: 40px;
line-height: 1.15; color: #ffffff; max-width: 1000px;
}
.title .accent { color: #ff5c5c; }
.terminal {
margin: 34px 48px 0;
background: #161e2e; border: 1px solid #2a3550; border-radius: 10px;
padding: 22px 28px;
font-family: "JetBrains Mono", "Fira Code", Menlo, monospace;
font-size: 21px; line-height: 1.6; max-width: 1040px;
}
.terminal .line { display: flex; gap: 12px; }
.terminal .prompt { color: #7599ff; }
.terminal .cmd { color: #e6edf3; }
.terminal .err { color: #ff5c5c; background: rgba(255, 92, 92, 0.10); padding: 1px 8px; border-radius: 4px; }
.terminal .fix { color: #3fb950; }
.footer {
margin-top: auto; padding: 0 48px 34px;
display: flex; align-items: center; justify-content: space-between;
}
.footer .tags { color: #6b7c99; font-size: 17px; }
.footer .tags span { margin-right: 18px; color: #7599ff; }
.footer .url { color: #6b7c99; font-size: 17px; }
</style>
</head>
<body>
<div class="topbar">
<div class="avatar"><img src="file:///D:/dev/hoelee-blog/public/logo-square.png" alt=""></div>
<div class="brand">Mr Hoelee <span class="domain">· blog.hoelee.com</span></div>
</div>
<div class="tag"><span class="dot"></span>case-studies · DevOps</div>
<div class="title">How I Host This Blog: Astro,<br>Gitea Actions, and Self-Hosted CI/CD</div>
<div class="terminal">
<div class="line"><span class="prompt">$</span><span class="cmd">git push origin main</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">Gitea Actions → unRaid runner → nginx → Cloudflare ✓</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">git-as-CMS · zero-downtime deploy</span></div>
</div>
<div class="footer">
<div class="tags"><span>#astro</span><span>#gitea</span><span>#ci-cd</span><span>#self-hosting</span><span>#docker</span><span>#cloudflare</span></div>
<div class="url">blog.hoelee.com</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+20
View File
@@ -38,6 +38,26 @@ const TERMINALS = {
<div class="line"><span class="prompt">$</span><span class="cmd">docker exec monitor python check_notify.py</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">telegram sendPhoto failed: 400 nginx/1.30.1</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">fix = IPv6 · DNS · multipart</span><span class="fix">→ delivered ✓</span></div>`,
'hello-world': `
<div class="line"><span class="prompt">$</span><span class="cmd">git init hoelee-blog · first commit</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">technical writing · self-hosting · build log</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">about → blog.hoelee.com</span></div>`,
'how-i-host-this-blog': `
<div class="line"><span class="prompt">$</span><span class="cmd">git push origin main</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">Gitea Actions → unRaid runner → nginx → Cloudflare ✓</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">git-as-CMS · zero-downtime deploy</span></div>`,
'how-i-built-the-digikedai-telegram-bot': `
<div class="line"><span class="prompt">$</span><span class="cmd">user:"does this course have a free trial?"</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">bot → yes, here's your account ✓ (24/7, no human)</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">grammY · LiteLLM · Cloudflare tunnel</span></div>`,
'authentik-major-upgrade-gotchas': `
<div class="line"><span class="prompt">$</span><span class="cmd">docker compose pull authentik-worker</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">authorization_flow not found · SSO broken</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">uptime: 2025.8 → 2026.8</span><span class="fix">fixed ✓</span></div>`,
};
const DEFAULT_TERMINAL = `
@@ -4,6 +4,7 @@ description: "A year's worth of authentik major-version upgrade pain: storage mo
pubDate: 2026-09-09
category: devops
tags: [authentik, sso, docker, portainer, self-hosting, oidc, upgrade]
ogImage: /og/authentik-major-upgrade-gotchas.png
---
I run authentik as the single sign-on gate in front of my self-hosted stack —
+1
View File
@@ -4,6 +4,7 @@ description: "What this blog is for: technical writing, self-hosting experience,
pubDate: 2026-09-05
category: notes
tags: [intro]
ogImage: /og/hello-world.png
---
Welcome. This is where I write about what I build and learn — mostly
@@ -4,6 +4,7 @@ description: "A customer-support AI bot that answers questions and provisions fr
pubDate: 2026-09-06
category: case-studies
tags: [telegram, docker, cloudflare, litellm, typescript, ai]
ogImage: /og/how-i-built-the-digikedai-telegram-bot.png
---
Digi Kedai sells digital products — online courses, ebooks, and templates — and
@@ -4,6 +4,7 @@ description: "A walkthrough of the end-to-end pipeline that builds and serves th
pubDate: 2026-09-04
category: case-studies
tags: [astro, gitea, ci-cd, self-hosting, docker, cloudflare]
ogImage: /og/how-i-host-this-blog.png
---
This blog is itself a project I built to demonstrate the kind of work I do.
@@ -4,6 +4,7 @@ description: "一年份的 authentik 大版本升级踩坑记录:存储挂载
pubDate: 2026-09-09
category: devops
tags: [authentik, sso, docker, portainer, self-hosting, oidc, upgrade]
ogImage: /og/authentik-major-upgrade-gotchas.png
---
我把 authentik 当作自托管体系前面的单点登录闸门——邮箱、仪表盘、Synology
@@ -4,6 +4,7 @@ description: "一个全天候解答顾客问题、并在 Telegram 和 WhatsApp
pubDate: 2026-09-06
category: case-studies
tags: [telegram, docker, cloudflare, litellm, typescript, ai]
ogImage: /og/how-i-built-the-digikedai-telegram-bot.png
---
Digi Kedai 销售数字产品——在线课程、电子书、模板——每一单都会带来一连串同样的顾客问题:*「这门课有免费试看吗?」「我怎么拿到账号?」「我该买哪个套餐?」*。对一个人的生意来说,人工回复根本忙不过来。于是我打造了一个 AI 客服机器人,全天候解答这些问题,甚至能在无人介入的情况下自动发放免费试用账号。
@@ -4,6 +4,7 @@ description: "这篇文章完整拆解了这个网站背后的构建与部署管
pubDate: 2026-09-06
category: case-studies
tags: [astro, gitea, ci-cd, self-hosting, docker, cloudflare]
ogImage: /og/how-i-host-this-blog.png
---
这个博客本身,就是我用来说明「我到底会做什么」的一个项目。下面是完整的