feat(content): implement consultant review recommendations (executive summary, dynamic cta, homelab cleanup)
Deploy / build (push) Successful in 25s

This commit is contained in:
hoelee committed 2026-10-27 00:31:50 +08:00
1 parent 5ecf25d37c
commit 355fecd348
28 files changed
+1634 -49

No files matched your search

@@ -39,7 +39,7 @@ I've since found two other gates on the same host with the same latent problem,
## The setup: SSO for an app that has no SSO
The app was a self-hosted database UI on my NAS (Docker, published on a host port, sitting behind nginx and a Cloudflare Tunnel — the usual shape of my homelab). I wanted a login wall in front of it so that "reachable from the internet" wouldn't mean "anyone who guesses the hostname sees the login form and can start guessing passwords".
The app was a self-hosted database UI on my NAS (Docker, published on a host port, sitting behind nginx and a Cloudflare Tunnel — the usual shape of the infrastructure I manage). I wanted a login wall in front of it so that "reachable from the internet" wouldn't mean "anyone who guesses the hostname sees the login form and can start guessing passwords".
The app's own documentation advertises SSO. I'll keep this post focused on the ingress trap and put the licensing story in its own write-up, but the short version is that the advertised SSO is a licensed feature, the open-source build ships the code path but not the permission to use it, and the community fork that used to fill the gap hasn't been touched in two years. Twenty minutes of research, two abandoned approaches, one conclusion: **the app would never do SSO for me, so the gate had to live in front of it.**
@@ -184,7 +184,7 @@ grep -c 'proxy_pass http://localhost:10000;' vhost.conf
# 11
```
Eleven. My app was one of them. **Ten other applications are gated through that outpost**, and a blanket substitution would have silently un-gated every one of them at once — including the ones doing real authentication work. The "fix" would have looked successful and removed authentication from most of my homelab.
Eleven. My app was one of them. **Ten other applications are gated through that outpost**, and a blanket substitution would have silently un-gated every one of them at once — including the ones doing real authentication work. The "fix" would have looked successful and removed authentication from most of my infrastructure.
What I did instead:
@@ -8,7 +8,7 @@ ogImage: /og/automating-cyberpanel-without-the-ui.jpg
banner: /banners/automating-cyberpanel-without-the-ui.jpg
---
I run CyberPanel 2.4.4.1 inside an Ubuntu VM on my home lab to host a handful
I run CyberPanel 2.4.4.1 inside an Ubuntu VM on my self-managed infrastructure to host a handful
of small sites. It sits at `https://panel.hoelee.com`, reverse-proxied to an
internal box. Recently I wanted to script two things against it — list my
sites and delete one — without opening the browser. This is the story of
@@ -7,6 +7,22 @@ tags: ["docker", "oom", "traefik", "synology", "crawlers"]
ogImage: /og/docker-reported-exit-0-the-oom-killer-disagreed.jpg
banner: /banners/docker-reported-exit-0-the-oom-killer-disagreed.jpg
draft: false
summary:
audience: "SREs, DevOps Leads & Systems Engineers"
painPoint: "A public edge proxy (Traefik) crashed 7 times over 4 days while Docker daemon reported ExitCode=0 and OOMKilled=false, rendering standard monitoring completely blind."
impact:
- metric: "Exposed Root Cause"
detail: "Bypassed Docker's false status via kernel dmesg and cgroup high-water marks, pinning the crash to 25,976 crawler IPs hitting a 512MB limit."
- metric: "Zero-Downtime Hotfix"
detail: "Hot-scaled proxy container to 2GB with memory swap locked, dropping peak memory to 210MB in 18 minutes with 0 subsequent crashes."
- metric: "Anti-Lockout SOP"
detail: "Established edge crawler stress-testing and verified probe procedures to prevent diagnostic self-lockout from security filters."
stack: ["Docker", "Linux Kernel (dmesg/cgroups)", "Traefik", "Synology DSM", "Security Filtering"]
jumpLinks:
business: "#the-result"
architecture: "#the-kernel-keeps-its-own-record"
result: "#what-i-would-do-differently"
dynamicCta: true
---
## The notification I expected to be a config mistake
@@ -6,6 +6,22 @@ category: case-studies
tags: [telegram, docker, cloudflare, litellm, typescript, ai]
ogImage: /og/how-i-built-the-digikedai-telegram-bot.jpg
banner: /banners/how-i-built-the-digikedai-telegram-bot.jpg
summary:
audience: "Business Owners & Product Leads"
painPoint: "A solo digital goods business faces a non-stop stream of repetitive pre-sales questions (trial requests, account setup, package selection) — leading to missed midnight sales and high manual support overhead."
impact:
- metric: "24/7 Instant Reply"
detail: "Handles customer inquiries across Telegram and WhatsApp with zero human intervention for free-trial provisioning."
- metric: "Zero Price Hallucination"
detail: "3-tier deterministic SKU retrieval and state-machine purchase interception prevent hallucinated prices or leaked internal paths."
- metric: "~175 Unit Tests"
detail: "Runs reliably in a single container on a Synology NAS with LiteLLM gateway failover."
stack: ["TypeScript", "grammY", "LiteLLM", "NocoDB", "Cloudflare Tunnel", "PostgreSQL"]
jumpLinks:
business: "#why-a-business-needs-a-bot-like-this"
architecture: "#the-architecture"
result: "#the-result"
dynamicCta: true
---
Digi Kedai sells digital products — online courses, ebooks, and templates — and
@@ -61,7 +77,7 @@ Customer → Telegram/WhatsApp → Cloudflare tunnel → bot.digikedai.com
A few decisions worth explaining:
**A Cloudflare tunnel into the container, with no Traefik hop.** My homelab is
**A Cloudflare tunnel into the container, with no Traefik hop.** The self-hosted infrastructure is
behind CGNAT, so nothing is publicly reachable without a tunnel. The webhook URL
lives behind a **secret path segment** (`/<secret>/webhook`) so Telegram's
updates only land if the caller knows the secret — a cheap first line of defense
@@ -182,15 +198,3 @@ If you're building your own Telegram bot backed by an LLM, the lesson is the
boring one: the model is the easy part. The webhook lifecycle, the payload
contract, and the idempotency of your provisioning are where it actually breaks —
design those first.
---
## Want a bot like this for your business?
I build custom Telegram/WhatsApp AI bots, websites, and self-hosted
infrastructure for businesses. If a bot like this could save you time and
money — or you'd like to hire me — I'd love to talk:
- 📱 **WhatsApp:** [+60 12-797 2969](https://wa.me/60127972969)
- 📧 **Email:** [[email protected]](mailto:[email protected])
- 🌐 **Website:** [hoelee.com](https://hoelee.com)
+1 -1
View File
@@ -37,7 +37,7 @@ git push → Gitea webhook → act_runner picks up the job
→ npm ci → astro build → copy dist/* to docroot → nginx serves it
```
The whole thing runs on hardware in my homelab, which is exactly the point —
The whole thing runs on hardware on the infrastructure I manage, which is exactly the point —
this is a live demo of the DevOps work I describe elsewhere.
## The docker-compose setup
@@ -4,7 +4,7 @@ description: "Three hosts, one Prometheus, one Grafana. The coverage matrix, the
pubDate: 2026-07-29
updatedDate: 2026-09-29
category: case-studies
tags: [prometheus, grafana, unraid, synology, dsm, cadvisor, monitoring, homelab]
tags: [prometheus, grafana, unraid, synology, dsm, cadvisor, monitoring, self-hosting]
ogImage: /og/one-prometheus-for-unraid-synology-and-a-vps.jpg
banner: /banners/one-prometheus-for-unraid-synology-and-a-vps.jpg
draft: false
@@ -12,7 +12,7 @@ draft: false
## Why this matters
A homelab is not a toy when it's running other people's websites, mail and files. The
Self-hosted infrastructure is not a toy when it's running other people's websites, mail and files. The
difference between a bad week and a bad *month* is usually how early you found out: a
disk with a reallocated sector, a volume with 20 GB left, a VM being starved of CPU by
its host, a container that restarted four times overnight while you slept.
@@ -45,7 +45,7 @@ CosyVoice 2 (the 0.5B parameter variant from Alibaba's FunAudioLLM team) emerged
## 3. Infrastructure & Network Topology
Our home-lab runs on a tiered hardware split:
Our self-managed infrastructure runs on a tiered hardware split:
```
[ Internet / Clients ]
@@ -4,7 +4,7 @@ description: "A disk-health collector that treats any non-zero smartctl exit as
pubDate: 2026-04-14
updatedDate: 2026-09-29
category: notes
tags: [smart, smartctl, unraid, monitoring, bash, disks, homelab]
tags: [smart, smartctl, unraid, monitoring, bash, disks, self-hosting]
ogImage: /og/smartctl-exit-code-32-skips-the-disks-that-matter.jpg
banner: /banners/smartctl-exit-code-32-skips-the-disks-that-matter.jpg
draft: false
@@ -40,7 +40,7 @@ By driving an already-authenticated Chrome instance in Docker via CDP, we operat
When controlling a local Chrome instance, Playwright or Puppeteer attaches files using `page.setInputFiles("input[type=file]", local_path)`. The browser reads the file from your local disk and passes the handle directly to the OS file picker.
However, in a self-hosted homelab or production setup, you frequently run Chrome inside an isolated Docker container on a NAS or server to keep your authenticated session alive 24/7 without exposing your daily desktop.
However, in a self-hosted on-premise or production setup, you frequently run Chrome inside an isolated Docker container on a NAS or server to keep your authenticated session alive 24/7 without exposing your daily desktop.
When your automation script runs on a different workstation or runner and tries to push a video over a remote CDP connection:
1. Playwright attempts to base64-encode or stream the file buffer over the WebSocket protocol.
@@ -143,7 +143,7 @@ def stage_file_to_container(local_path: str, dest_filename: str) -> None:
### Operational Caveats & Security Checklist
1. **Client RAM Footprint**: While Chrome experiences zero memory pressure, `io.BytesIO()` buffers the 130MB tar archive in the Python process memory. For multi-gigabyte video dumps, write the `.tar` to a temporary file and stream it using `requests` or `urllib3` chunked uploads.
2. **Pre-existing Destination Directory**: The Docker Archive API extracts *into* the directory specified by `?path=`. If `/config/Downloads` does not exist in the container image, the API returns HTTP 404/500 rather than creating the parent folders.
3. **API Key & CDP Exposure**: Both the Portainer API key and the unauthenticated Chrome DevTools port (default `9222` or `9242`) grant root-level execution capability. Never bind these ports to public IPs (`0.0.0.0`). Keep them strictly on an isolated homelab bridge or behind mutual-TLS / Authentik SSO.
3. **API Key & CDP Exposure**: Both the Portainer API key and the unauthenticated Chrome DevTools port (default `9222` or `9242`) grant root-level execution capability. Never bind these ports to public IPs (`0.0.0.0`). Keep them strictly on an isolated internal bridge or behind mutual-TLS / Authentik SSO.
### Measured Performance
On a local 1 Gbps LAN:
@@ -9,7 +9,7 @@ banner: "/banners/why-is-my-tts-model-repeating-percent.jpg"
draft: false
---
We had just wired Alibaba's open-source CosyVoice 2 into our home-lab automation cluster. The direct Python test scripts worked cleanly, but when we fired our first test request through a Synology-hosted n8n Webhook, the audio response sounded like an alien countdown.
We had just wired Alibaba's open-source CosyVoice 2 into our self-hosted automation cluster. The direct Python test scripts worked cleanly, but when we fired our first test request through a Synology-hosted n8n Webhook, the audio response sounded like an alien countdown.
Instead of the Chinese phrase *"恭喜!本地模型通过 N8N 联动测试成功!"* (Congratulations! Local model test via n8n succeeded!), the speaker rattled off a robotic avalanche:
@@ -1,12 +1,28 @@
---
title: "Docker 说它干净退出,内核说它被 OOM 杀掉了"
description: "NAS 上的容器不停「意外停止」,而 Docker 报的是 exit 0 / OOMKilled=false。真正的证据在 dmesg 里:六次 cgroup 内存击杀。"
title: "Docker 报告退出码 0,内核却早已执行 OOM-Kill"
description: "NAS 上的一个容器反复异常退出,Docker 却上报 exit 0 与 OOMKilled=false。真实的证据在 dmesg 里:六次 cgroup OOM-kill。"
pubDate: 2026-10-16
category: devops
tags: ["docker", "oom", "traefik", "synology", "crawlers"]
ogImage: /og/zh-docker-reported-exit-0-the-oom-killer-disagreed.jpg
banner: /banners/docker-reported-exit-0-the-oom-killer-disagreed.jpg
draft: false
summary:
audience: "SREs, DevOps Leads & 系统工程师"
painPoint: "公网边缘反向代理容器(Traefik)4天内无预警重启7次,但 Docker daemon 谎报 ExitCode=0 且 OOMKilled=false,常规监控完全失明。"
impact:
- metric: "精准定位真凶"
detail: "穿透 Docker 状态假象,通过内核 dmesg 与 cgroup 内存水位线抓出 25,976 个代理爬虫 IP 引发的 512MB 触顶"
- metric: "0 停机热修复"
detail: "单容器精准扩容至 2GB 并锁死 Swap(防磁盘阵列雪崩),18分钟峰值回落至 210MB,连续运行 0 崩溃"
- metric: "防自锁诊断规范"
detail: "沉淀边缘网关高并发防爬虫压测与避免自身 IP 被 WAF 误封(403 假象)的排障 SOP"
stack: ["Docker", "Linux Kernel (dmesg/cgroups)", "Traefik", "Synology DSM", "Security Filtering"]
jumpLinks:
business: "#the-result"
architecture: "#the-kernel-keeps-its-own-record"
result: "#what-i-would-do-differently"
dynamicCta: true
---
## 一条我以为又是配置写错的告警
@@ -47,7 +47,7 @@ Tor 是一个匿名网络:流量经过三道中继,没有任何一道中继能
由此得到四个普通网站给不了的特性:
- **没有公网 IP、没有端口、没有 DNS。** 扫描器无从发现。你可以用 CGNAT 后面的家用网络来跑(现在大多数家庭宽带都是)。
- **没有公网 IP、没有端口、没有 DNS。** 扫描器无从发现。你可以用处于 CGNAT 后的私有网络来跑(现在大多数宽带都是)。
- **没有中间商。** 路径上没有任何 CDN、注册商或平台记录谁来过。
- **地址本身就是访问控制。** 拿到地址的人可以访问;其他人连这站存不存在都无法判断。要求更高时还能叠加客户端密钥。
- **clearnet 挂了它还在。** 域名被墙或被没收,洋葱站毫发无损,因为没有注册局参与其中。
+1 -1
View File
@@ -25,7 +25,7 @@ banner: /banners/hello-world.jpg
## 我写什么
- **全栈工程**——PHP/CodeIgniter、Java/Spring、TypeScript/React、WordPress。
- **DevOps 与自托管**——Docker、Traefik、Cloudflare 隧道、NAS/unRaid 家用实验室。
- **DevOps 与自托管**——Docker、Traefik、Cloudflare 隧道、NAS/unRaid 私有基础设施。
- **AI 与自动化**——Telegram 机器人、n8n 工作流、本地 LLM、记忆系统。
- **Web3**——Solidity、Foundry、ERC-20/721(仍在学习,如实呈现)。
@@ -1,11 +1,27 @@
---
title: "我如何打造 DigiKedai Telegram AI 客服机器人"
description: "一个全天候解答顾客问题、并在 Telegram 和 WhatsApp 上自动开通免费试用账号的 AI 客服机器人——TypeScript、grammY、Cloudflare 隧道 webhook,以及自托管的 LiteLLM 网关。"
title: "我是如何构建 DigiKedai Telegram AI 客服机器人的"
description: "一个跨 Telegram 与 WhatsApp 解答疑问并全自动开通免费试用账号的 AI 客服机器人——TypeScript、grammY、Cloudflare 隧道 webhook,以及自建 LiteLLM 网关。"
pubDate: 2026-09-06
category: case-studies
tags: [telegram, docker, cloudflare, litellm, typescript, ai]
ogImage: /og/zh-how-i-built-the-digikedai-telegram-bot.jpg
banner: /banners/how-i-built-the-digikedai-telegram-bot.jpg
summary:
audience: "企业主与产品/运营负责人"
painPoint: "一人数字商品电商面临大量重复售前咨询(试用申请、账号开通、选品),夜间与周末漏单严重,人工客服成本高。"
impact:
- metric: "24/7 秒级响应"
detail: "全天候接管 Telegram 与 WhatsApp 双端咨询,零人工介入自动发放免费试用账号"
- metric: "0 价格幻觉"
detail: "三层确定性 SKU 检索 + 状态机拦截购买意图,杜绝大模型胡报价或泄漏内部路径"
- metric: "~175 项单测护航"
detail: "单容器稳定运行于 NAS,通过自建 LiteLLM 网关实现模型高可用切换"
stack: ["TypeScript", "grammY", "LiteLLM", "NocoDB", "Cloudflare Tunnel", "PostgreSQL"]
jumpLinks:
business: "#why-a-business-needs-a-bot-like-this"
architecture: "#the-architecture"
result: "#the-result"
dynamicCta: true
---
Digi Kedai 销售数字产品——在线课程、电子书、模板——每一单都会带来一连串同样的顾客问题:*「这门课有免费试看吗?」「我怎么拿到账号?」「我该买哪个套餐?」*。对一个人的生意来说,人工回复根本忙不过来。于是我打造了一个 AI 客服机器人,全天候解答这些问题,甚至能在无人介入的情况下自动发放免费试用账号。
@@ -44,7 +60,7 @@ Digi Kedai 销售数字产品——在线课程、电子书、模板——每一
几个值得解释的决策:
**Cloudflare 隧道直连容器,不经过 Traefik。** 我的 homelab 在 CGNAT 后面,没有隧道就没有任何东西能被公网访问。webhook URL 藏在一个**秘密路径段**(`/<secret>/webhook`)后面,这样 Telegram 的更新只有知道这个 secret 的人才能送达——这是在 Telegram 自带 token 认证之外的第一道廉价防线。
**Cloudflare 隧道直连容器,不经过 Traefik。** 我管理的自托管生产节点在 CGNAT 后面,没有隧道就没有任何东西能被公网访问。webhook URL 藏在一个**秘密路径段**(`/<secret>/webhook`)后面,这样 Telegram 的更新只有知道这个 secret 的人才能送达——这是在 Telegram 自带 token 认证之外的第一道廉价防线。
**模型用 LiteLLM 的*别名*寻址,从不用原始模型名。** 机器人调用 `mem0-openai`;LiteLLM 把它映射到真实模型(背后还有 OpenRouter 故障转移)。机器人永远不需要知道实际是哪个供应商在服务请求。我设了 `temperature: 0.4` 和 `45s` 超时。
@@ -102,13 +118,3 @@ LLM 擅长开放式问题,却很不擅长*状态*。所以那些需要可靠
[git.hoelee.com/hoelee/digikedai-bot](https://git.hoelee.com/hoelee/digikedai-bot)。
如果你也在打造一个 LLM 驱动的 Telegram 机器人,教训其实很朴素:模型是最容易的部分。webhook 生命周期、payload 契约、以及开通流程的幂等性才是真正会出问题的地方——先设计好这些。
---
## 想为你的生意做一个这样的机器人吗?
我为企业定制 Telegram/WhatsApp AI 机器人、网站,以及自托管基础设施。如果一个这样的机器人能为你省时省钱——或者你想雇佣我——我非常乐意交流:
- 📱 **WhatsApp:** [+60 12-797 2969](https://wa.me/60127972969)
- 📧 **邮箱:** [[email protected]](mailto:[email protected])
- 🌐 **网站:** [hoelee.com](https://hoelee.com)
+1 -1
View File
@@ -36,7 +36,7 @@ git push → Gitea webhook → act_runner 领取任务
→ npm ci → astro build → 拷贝 dist/* 到站点根目录 → nginx 提供服务
```
整个过程都跑在我 homelab 自己的硬件上——这正是重点:它就是我别处所描述的
整个过程都跑在我自己管理的基础设施硬件上——这正是重点:它就是我别处所描述的
DevOps 工作的一个活生生的演示。
## docker-compose 配置
@@ -4,7 +4,7 @@ description: "三台主机、一个 Prometheus、一个 Grafana:覆盖矩阵
pubDate: 2026-07-29
updatedDate: 2026-09-29
category: case-studies
tags: [prometheus, grafana, unraid, synology, dsm, cadvisor, monitoring, homelab]
tags: [prometheus, grafana, unraid, synology, dsm, cadvisor, monitoring, self-hosting]
ogImage: /og/zh-one-prometheus-for-unraid-synology-and-a-vps.jpg
banner: /banners/one-prometheus-for-unraid-synology-and-a-vps.jpg
draft: false
@@ -12,7 +12,7 @@ draft: false
## 为什么这件事重要
当一台家用服务器在跑别人的网站、邮件和文件时,它就不是玩具了。糟糕的一周和糟糕的**一个月**之间,差别通常只是你多早发现:一块出现重分配扇区的盘、一个只剩 20 GB 的卷、一台被宿主机饿死的虚拟机、一个夜里重启四次的容器。
当一套自托管基础设施在跑别人的网站、邮件和文件时,它就不是玩具了。糟糕的一周和糟糕的**一个月**之间,差别通常只是你多早发现:一块出现重分配扇区的盘、一个只剩 20 GB 的卷、一台被宿主机饿死的虚拟机、一个夜里重启四次的容器。
我有三台机器——unRaid(日常主力)、群晖 NAS(存储与服务)和 VPS(对外)——以及三套**各自**都不知道它们在干什么的方式。这篇文章讲它们如何变成一块屏,以及我路上做错的四件事——其中三个你也会踩。
@@ -125,4 +125,4 @@ echo ONLINE - CosyVoice 2 已加载至显卡,服务就绪。
- **首包响应**:n8n 收到请求至音频开始回传约 **320 毫秒**,完全满足交互式即时语音应答的要求。
- **自主掌控**:不再受制于第三方厂商的封号、限频与网络审查,所有音频资产与 Prompt 完全留存在本地内网。
自托管高品质 AI 语音不再是大型服务器集群的专利。借助新一代自回归+扩散语音架构与消费级显卡,一套完全受控、零月租且音质超越商业云服务的全天候语音栈,已经完全可以在普通 Home-lab 环境中稳定运转。
自托管高品质 AI 语音不再是大型服务器集群的专利。借助新一代自回归+扩散语音架构与消费级显卡,一套完全受控、零月租且音质超越商业云服务的全天候语音栈,已经完全可以在自托管生产环境中稳定运转。
@@ -4,7 +4,7 @@ description: "把 smartctl 的任何非零退出码当成读取失败的监控
pubDate: 2026-04-14
updatedDate: 2026-09-29
category: notes
tags: [smart, smartctl, unraid, monitoring, bash, disks, homelab]
tags: [smart, smartctl, unraid, monitoring, bash, disks, self-hosting]
ogImage: /og/zh-smartctl-exit-code-32-skips-the-disks-that-matter.jpg
banner: /banners/smartctl-exit-code-32-skips-the-disks-that-matter.jpg
draft: false
@@ -79,4 +79,4 @@ fio --name=verify --filename=/dev/sdd \
这颗几分钟前就要被我送去 RMA 的盘,现在已经是一颗格式化好、挂载上、verify 全过的缓存 SSD,`btrfs device stats` 全部清零。修好它的成本:一根线。相信原有诊断的成本:一趟没必要的保修,还有一台迟早会在那根坏线上损坏数据的机器。
如果你在 NAS 或家用服务器上遇到一颗盘报 FPDMA 超时、但 SMART 看起来没问题——换线,别急着换盘。
如果你在 NAS 或自管服务器上遇到一颗盘报 FPDMA 超时、但 SMART 看起来没问题——换线,别急着换盘。
@@ -38,7 +38,7 @@ YouTube Data API 默认每天只有 10,000 单位配额,上传一个视频扣
在控制本地 Chrome 时,Playwright 或 Puppeteer 使用 `page.setInputFiles("input[type=file]", local_path)`。浏览器直接从当前机器的物理磁盘中读取文件句柄,操作极其平稳。
但在自托管 Homelab 或生产部署中,为了让 YouTube、电商等带有登录态的账号 7x24 小时保持在线且不污染日常桌面,我们通常将 Chrome 运行在 Synology NAS 或 Linux 服务器的 Docker 容器中。
但在自托管私有基础设施或生产部署中,为了让 YouTube、电商等带有登录态的账号 7x24 小时保持在线且不污染日常桌面,我们通常将 Chrome 运行在 Synology NAS 或 Linux 服务器的 Docker 容器中。
当我们在外部工作站或 CI 流水线上运行脚本,试图跨网络通过远程 CDP 将视频推给容器里的浏览器时,问题接踵而至:
1. Playwright 会尝试通过 WebSocket 协议将巨大的二进制文件转为 Base64 或分片推流;
@@ -62,7 +62,7 @@ draft: false
不用云端按分钟计费的语音转写 API,流水线直接运行在宿主机的 NVIDIA RTX 3060 显卡上。通过 `faster-whisper` 与 `Silero-VAD` 结合,自动切除无声段,并按标点符号聚合成 3 到 8 秒的自然语义分句。
### 严禁家庭 NAS 直出公网视频流
用家里的 NAS 直出公网 Range/206 视频流,一旦遇到爬虫或多人同时访问,家庭宽带的上行带宽会被瞬间打满,甚至触发运营商对于家用宽带 PCDN 的封禁。公网访问采用双轨策略:长青视频优先使用 B站/YouTube 官方嵌入播放器;自存短视频经 NVENC 压缩为 720p H.264 后托管在免流量费的 Cloudflare R2。
用自建 NAS 直出公网 Range/206 视频流,一旦遇到爬虫或多人同时访问,宽带的上行带宽会被瞬间打满,甚至触发运营商对于 PCDN 的封禁。公网访问采用双轨策略:长青视频优先使用 B站/YouTube 官方嵌入播放器;自存短视频经 NVENC 压缩为 720p H.264 后托管在免流量费的 Cloudflare R2。
### 基于 Pagefind 的毫秒级离线搜索
前台不维护重型的 Meilisearch 或 Elasticsearch 数据库,直接编译为 Astro 静态站点并利用 Pagefind 构建分片索引。读者在浏览器端搜索时,通过 WebAssembly 按需拉取几十 KB 的分片,零服务器内存常驻,中文关键词检索延迟在 20ms 以内。