feat: initial release of pengzhan.dev personal editorial blog platform

- Editorial web platform built with Node.js, Express, EJS, and Vanilla CSS
- Markdown content engine with YAML frontmatter, bilingual variants, and hot-reload watcher
- Unified SQLite with FTS5 search indexing, query logging, and schema filters
- Centered editorial post layout with margin-docked left TOC and mobile bottom sheet
- WordPress-grade comment system with moderation dashboard and bulk operations under /guanli
- Complete SEO suite (JSON-LD structured data, RSS/Atom feeds, XML sitemap, robots.txt)
- CI/CD workflows for linting, security audits (Gitleaks), automated tests, and Docker releases
This commit is contained in:
2026-09-16 23:08:53 -07:00
parent ac2c4c09ef
commit 8c0b04db0e
126 changed files with 8515 additions and 3984 deletions
@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600" width="100%" height="100%">
<defs>
<linearGradient id="kidBg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#fed7aa"/>
<stop offset="100%" stop-color="#fb923c"/>
</linearGradient>
<filter id="kidShadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="6" stdDeviation="10" flood-opacity="0.15"/>
</filter>
</defs>
<rect width="600" height="600" fill="url(#kidBg)" rx="32"/>
<!-- Creative Building Blocks / Robot Icon -->
<g transform="translate(300, 250)" filter="url(#kidShadow)">
<rect x="-90" y="-80" width="180" height="150" rx="24" fill="#ffffff"/>
<!-- Robot antenna -->
<line x1="0" y1="-80" x2="0" y2="-120" stroke="#ffffff" stroke-width="8" stroke-linecap="round"/>
<circle cx="0" cy="-125" r="14" fill="#ea580c"/>
<!-- Eyes -->
<circle cx="-40" cy="-15" r="16" fill="#0284c7"/>
<circle cx="40" cy="-15" r="16" fill="#0284c7"/>
<!-- Smile -->
<path d="M -40 30 Q 0 60 40 30" stroke="#ea580c" stroke-width="8" stroke-linecap="round" fill="none"/>
</g>
<!-- Educational Blocks -->
<g transform="translate(180, 420)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#38bdf8"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">A</text>
</g>
<g transform="translate(260, 400)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#ec4899"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">I</text>
</g>
<g transform="translate(340, 420)" filter="url(#kidShadow)">
<rect x="0" y="0" width="60" height="60" rx="12" fill="#10b981"/>
<text x="30" y="42" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="32" font-weight="900">&lt;&gt;</text>
</g>
<text x="300" y="530" text-anchor="middle" fill="#7c2d12" font-family="-apple-system, sans-serif" font-size="28" font-weight="800">Coding for Kids</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,26 @@
---
title: "AI可以改变什么:为孩子编程与创造力"
date: 2025-05-10
author: "haopengzhan"
category: "Non-tech"
tags: ["AI", "Parenting", "Education", "Kids"]
excerpt: "当AI能够直接生成代码,我们应该如何引导下一代理解技术、培养解决真实问题的创造力?"
cover: images/cover.svg
lang: zh
---
随着人工智能技术的迅猛普及,编程教育的门槛正在发生前所未有的变化。
过去,学习编程首先要克服繁琐的语法、编译器配置和环境搭建;而如今,孩子们只需要用自己的语言描述心中的想法,AI 就能协助将其变为可视化的交互程序。
## 从“写语法”到“定义问题”
在传统的教学中,很多孩子在记忆循环和变量类型时失去了兴趣。生成式 AI 带来的改变在于:
- **即时反馈**:孩子可以随时看到想法被渲染出来。
- **系统性思考**:重点转移为理解逻辑流程与规则边界。
- **激发创造力**:从消费者转变为创造者。
## 结语
技术工具永远在演进,不变的是面对未知问题时的探究欲望与审美能力。
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600" width="100%" height="100%">
<defs>
<linearGradient id="kubeBg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#93c5fd"/>
<stop offset="100%" stop-color="#3b82f6"/>
</linearGradient>
<filter id="kubeShadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="8" stdDeviation="12" flood-opacity="0.25"/>
</filter>
</defs>
<rect width="600" height="600" fill="url(#kubeBg)" rx="32"/>
<!-- Kubernetes / Kubelet Hexagon -->
<g transform="translate(300, 260)" filter="url(#kubeShadow)">
<polygon points="0,-130 112,-65 112,65 0,130 -112,65 -112,-65" fill="#1e40af" stroke="#ffffff" stroke-width="8"/>
<!-- Central Ship Wheel / Helm Spokes -->
<circle cx="0" cy="0" r="45" fill="#3b82f6" stroke="#ffffff" stroke-width="6"/>
<circle cx="0" cy="0" r="18" fill="#ffffff"/>
<!-- Spokes -->
<line x1="0" y1="-75" x2="0" y2="75" stroke="#ffffff" stroke-width="6"/>
<line x1="-65" y1="-37" x2="65" y2="37" stroke="#ffffff" stroke-width="6"/>
<line x1="-65" y1="37" x2="65" y2="-37" stroke="#ffffff" stroke-width="6"/>
</g>
<!-- Large Kubelet Text -->
<text x="300" y="470" text-anchor="middle" fill="#ffffff" font-family="-apple-system, sans-serif" font-size="52" font-weight="900" letter-spacing="-1">Kubelet</text>
<text x="300" y="510" text-anchor="middle" fill="#dbeafe" font-family="-apple-system, sans-serif" font-size="20" font-weight="600" letter-spacing="2">RUNTIME &amp; PLEG DEEP DIVE</text>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,60 @@
---
title: "Kubelet Source Code Deep Dive: SyncLoop and PLEG"
date: 2025-07-15
author: "haopengzhan"
category: "Tech"
tags: ["k8s", "kubernetes", "go", "cloud", "containers"]
excerpt: "An in-depth exploration into the Kubernetes Kubelet runtime architecture, focusing on syncLoop mechanics and Pod Lifecycle Event Generator (PLEG)."
cover: images/cover.svg
lang: en
---
Kubelet is the primary "node agent" that runs on each Kubernetes worker node. It registers the node with the apiserver, watches for PodSpec changes, and ensures containers described in those PodSpecs are running and healthy[^1].
## Architectural Overview
At the heart of the Kubelet daemon is the `syncLoop`. The syncLoop monitors multiple channels for pod changes:
1. **Config channel**: Pods from API server, file manifests, or HTTP endpoints.
2. **PLEG channel**: Pod Lifecycle Event Generator events.
3. **Probe channel**: Liveness and readiness probe results.
4. **Housekeeping channel**: Periodic cleanup tasks.
### The SyncLoop Implementation
Here is a simplified view of how Kubelet receives events in Go:
```go
func (kl *Kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
klog.InfoS("Starting kubelet main sync loop")
for {
select {
case u, ok := <-updates:
if !ok {
klog.ErrorS(nil, "Updates channel closed")
return
}
handler.HandlePodAdditions(u.Pods)
case e := <-kl.pleg.Watch():
handler.HandlePLEGEvent(e)
}
}
}
```
## Pod Lifecycle Event Generator (PLEG)
PLEG periodically relists containers from the container runtime via CRI:
```bash
# Inspecting runtime events with crictl
crictl ps --state Running
```
If PLEG does not complete within the expected relist period (default 3 minutes), the node becomes `NotReady` with the condition `PLEG is not healthy`.
## Conclusion
Understanding the syncLoop provides clear insights into why nodes report PLEG issues during disk I/O bottlenecks or heavy container churn.
[^1]: Kubernetes documentation: https://kubernetes.io/docs/concepts/architecture/
@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600" width="100%" height="100%">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#cbd5e1"/>
<stop offset="100%" stop-color="#94a3b8"/>
</linearGradient>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="8" stdDeviation="12" flood-opacity="0.18"/>
</filter>
</defs>
<rect width="600" height="600" fill="url(#bg)" rx="32"/>
<!-- Terminal Card -->
<g filter="url(#shadow)">
<rect x="70" y="140" width="280" height="200" rx="16" fill="#1e293b"/>
<!-- Terminal dots -->
<circle cx="95" cy="165" r="5" fill="#ef4444"/>
<circle cx="112" cy="165" r="5" fill="#f59e0b"/>
<circle cx="129" cy="165" r="5" fill="#10b981"/>
<!-- Terminal text -->
<text x="95" y="210" fill="#f8fafc" font-family="-apple-system, sans-serif" font-size="16" font-weight="700">Using Gemini-CLI</text>
<text x="95" y="235" fill="#93c5fd" font-family="-apple-system, sans-serif" font-size="14">and MCP Server</text>
<text x="95" y="260" fill="#cbd5e1" font-family="-apple-system, sans-serif" font-size="13">to Organize My</text>
<text x="95" y="285" fill="#38bdf8" font-family="-apple-system, sans-serif" font-size="15" font-weight="700">Dropbox</text>
</g>
<!-- Server Rack / MCP node -->
<g filter="url(#shadow)">
<rect x="400" y="130" width="130" height="220" rx="16" fill="#e2e8f0" stroke="#94a3b8" stroke-width="4"/>
<circle cx="465" cy="165" r="14" fill="#64748b"/>
<line x1="430" y1="210" x2="500" y2="210" stroke="#cbd5e1" stroke-width="4" stroke-linecap="round"/>
<line x1="430" y1="235" x2="500" y2="235" stroke="#cbd5e1" stroke-width="4" stroke-linecap="round"/>
<line x1="430" y1="260" x2="500" y2="260" stroke="#cbd5e1" stroke-width="4" stroke-linecap="round"/>
<line x1="430" y1="285" x2="500" y2="285" stroke="#cbd5e1" stroke-width="4" stroke-linecap="round"/>
</g>
<!-- Connecting Arrows -->
<path d="M 350 240 L 400 240" stroke="#0284c7" stroke-width="6" stroke-dasharray="6,4" stroke-linecap="round"/>
<path d="M 210 340 L 210 390 L 300 390 L 300 420" stroke="#0284c7" stroke-width="5" stroke-linecap="round"/>
<polygon points="300,435 292,418 308,418" fill="#0284c7"/>
<!-- Dropbox Box Icon -->
<g transform="translate(230, 440)" filter="url(#shadow)">
<!-- Top Left Diamond -->
<polygon points="70,10 35,32 70,55 105,32" fill="#0061ff"/>
<!-- Top Right Diamond -->
<polygon points="70,10 105,32 140,10 105,-12" fill="#3b82f6"/>
<!-- Bottom diamond front -->
<polygon points="70,55 35,78 70,100 105,78" fill="#1d4ed8"/>
<!-- Open flap left -->
<polygon points="35,32 0,55 35,78 70,55" fill="#2563eb"/>
<!-- Open flap right -->
<polygon points="105,32 70,55 105,78 140,55" fill="#1d4ed8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1,45 @@
---
title: "Using Gemini-CLI and MCP server to organize my dropbox"
date: 2025-08-31
author: "haopengzhan"
category: "Tech"
tags: ["AI Era", "Dropbox", "Gemini", "LLM", "Productivity", "k8s"]
excerpt: "I think many people face the same dilemma I did: Dropbox fills up with thousands of disorganized files. Here is how I leveraged Gemini-CLI and Model Context Protocol to automate organization."
cover: images/cover.svg
lang: en
---
I think many people face the same dilemma I did: years of accumulated receipts, research papers, camera uploads, and archives scattered across folders.
When managing workloads in modern infrastructure, we treat our nodes cleanly with a streamlined **container image** deployed across clusters. But our personal storage often looks like a monolithic legacy system.
## The Problem with Cloud Storage
Cloud storage services like Dropbox offer immense space, but search and organization remain rudimentary. You often remember *concepts* rather than filenames:
- "That invoice from AWS last quarter"
- "The PDF paper on distributed consensus"
- "A screenshot of my terminal settings"
## Introducing MCP (Model Context Protocol)
The Model Context Protocol connects LLMs with local or remote toolsets. By creating an MCP server for Dropbox, we expose simple tools:
```bash
# Running the MCP server locally or in a container
npx -y @modelcontextprotocol/server-dropbox
```
And in Python:
```python
import dropbox
def list_unorganized_files(dbx, path="/Inbox"):
res = dbx.files_list_folder(path)
return [entry.name for entry in res.entries]
```
## Results and Workflow
Once configured, Gemini can inspect file headers, categorize documents into appropriate directories, and even rename messy receipts into normalized ISO dates (`YYYY-MM-DD-merchant-amount.pdf`).
@@ -0,0 +1,27 @@
---
title: "使用 Gemini-CLI 与 MCP 服务整理 Dropbox"
date: 2025-08-31
author: "haopengzhan"
category: "Tech"
tags: ["AI Era", "Dropbox", "Gemini", "LLM", "Productivity", "k8s"]
excerpt: "面对堆积如山的 Dropbox 文件,如何使用大语言模型与 Model Context Protocol 构建自动化归档助手?"
cover: images/cover.svg
lang: zh
---
我想很多人都会遇到和我一样的困境:多年累积的单据、学术论文、照片杂乱无章地存放在 Dropbox 中。
在日常管理集群基础设施时,我们通过标准化的 **container image** 保持每个节点的干净与可复现。但我们的个人存储往往像一个臃肿的单体遗留系统。
## 云存储的管理困境
传统的文件夹分类需要大量人工维护,而且往往随着时间推移逐渐失控。借助 MCP 协议,我们可以让 AI 拥有读写文件的上下文能力。
```bash
# 启动 MCP 服务
npx -y @modelcontextprotocol/server-dropbox
```
## 总结与成效
现在每天晚上脚本自动运行,未分类文件自动按年月和类别归档,大幅提升了个人数字资产的管理效率。