Skip to content

Commit ba2c5fe

Browse files
committed
feat: add mqttkit cover images and documentation
- Create cover images for mqttkit using HTML templates. - Add README.md for cover image generation instructions. - Implement rendering script using Puppeteer to generate PNGs. - Introduce minimal and standard cover HTML templates. - Add English and Chinese introduction markdown files for mqttkit.
1 parent b32c6e4 commit ba2c5fe

9 files changed

Lines changed: 1053 additions & 0 deletions

blog/PUBLISH-GUIDE.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# mqttkit 发布投放指南
2+
3+
两份正文:
4+
- 英文:`blog/en-introducing-mqttkit.md`
5+
- 中文:`blog/zh-introducing-mqttkit.md`
6+
7+
按下面的渠道顺序投,每个渠道都列了**标题候选 / tag / 字数 / 平台特有注意事项**
8+
9+
---
10+
11+
## A. 英文渠道
12+
13+
### 1. dev.to (强烈推荐先发这里)
14+
15+
- **URL**<https://dev.to/new>
16+
- **标题候选**
17+
- `mqttkit: Elysia-style application framework for MQTT`
18+
- `Why doesn't MQTT have its own Express? Introducing mqttkit`
19+
- `Building MQTT apps in TypeScript with ordered middleware and typed topic routes`
20+
- **Tags**(最多 4 个,按热度选):`typescript`, `mqtt`, `iot`, `bun`
21+
- **Cover image**:建议自己做一张 1000x420 的题图(一行代码 + logo 就够)。可以用 ray.so / carbon.now.sh 截那段 Quick Start 代码。
22+
- **Canonical URL**:如果你后面要在 Medium / 个人博客同步,建议先发 dev.to,再把 dev.to 的 URL 填到其他平台的 canonical 上。
23+
- **正文**:直接贴 `en-introducing-mqttkit.md` 的 Markdown。dev.to 完全支持。
24+
25+
### 2. Hacker News — Show HN
26+
27+
- **URL**<https://news.ycombinator.com/submit>
28+
- **标题**`Show HN: mqttkit – Elysia-style application framework for MQTT`
29+
- HN 标题不要 emoji、不要全大写、不要营销词。
30+
- **URL 填**:你的 GitHub 仓库地址 `https://github.com/keyp/mqttkit`**不要**填博客地址。
31+
- **首条评论**(自己作为作者发,开场就放,HN 默认会展开):
32+
33+
> Author here. I kept writing the same MQTT boilerplate — ad-hoc auth in `aedes.authorizePublish`, JSON.parse + zod by hand in `client.on('message')`, manual correlationData for RPC. After the third project I extracted the framework layer.
34+
>
35+
> Design choice: don't reimplement the protocol. Aedes / EMQX / Mosquitto already do CONNECT, QoS, retain, sessions, MQTT 5 properties. mqttkit is purely the application layer on top — ordered `use()` middleware, `router().topic()` with topic params and Standard Schema validation, MQTT 5 RPC with retries, per-route timeout/concurrency, AsyncAPI 3.0 generation from the same route declarations, structured metrics hooks for Prometheus/OTel.
36+
>
37+
> Bun + TypeScript first. In-memory TestBroker for unit tests. Currently with an Aedes adapter; broker interface is small (a couple hundred LOC) if anyone wants to wire it to EMQX or NanoMQ.
38+
>
39+
> Feedback welcome — particularly on the RPC retry semantics and the topic-policy shape.
40+
41+
- **发布时机**:周二/周三 UTC 早上 9 点(≈ 北京 17:00)或 UTC 下午 3 点上 HN front page 概率高一些。
42+
- **重要**:发完不要刷票、不要在群里求 upvote,HN 反作弊会直接埋。
43+
44+
### 3. Reddit
45+
46+
按 subreddit 分别发(**不要同一时间一次发完,会被识别为 spam**,每个 sub 隔几小时):
47+
48+
| Subreddit | 标题 | 注意 |
49+
| --- | --- | --- |
50+
| r/typescript | `mqttkit – Elysia-style application framework for MQTT, written in TypeScript` | 强调 TS 类型推断、Standard Schema 集成 |
51+
| r/node | `Express but for MQTT – ordered middleware, topic routes, MQTT 5 RPC` | 强调和 mqtt.js / aedes 直接 hook 的对比 |
52+
| r/IOT | `Built an application framework for MQTT backends (auth, validation, AsyncAPI docs)` | 强调 IoT 应用场景、AsyncAPI |
53+
| r/javascript | `Show /r/javascript: mqttkit – MQTT application framework with typed routes and RPC` | 偏 demo 友好 |
54+
| r/bun | `mqttkit – first-class Bun MQTT framework` | 强调 Bun 性能 / 一等公民 |
55+
56+
**正文**:把 `en-introducing-mqttkit.md` 里"代码长这样" + 3-5 个核心特性 + 链接,保留 800-1200 词左右,太长 Reddit 不爱看。
57+
58+
### 4. Medium
59+
60+
- 等 dev.to 发完 24 小时后再发 Medium,正文一样。
61+
- **必须**填 canonical URL 指向 dev.to(Settings → Customize → Canonical URL),否则 SEO 互相吃掉。
62+
- Publication 投递:`Better Programming` / `Level Up Coding` / `JavaScript in Plain English`,任选一个。
63+
64+
### 5. Awesome 列表 PR(长期 SEO,强烈建议都做)
65+
66+
| 仓库 | 操作 |
67+
| --- | --- |
68+
| [hobbyquaker/awesome-mqtt](https://github.com/hobbyquaker/awesome-mqtt) | 加到 "Libraries" / Node 区块 |
69+
| [apvarun/awesome-bun](https://github.com/apvarun/awesome-bun) | 加到 Networking / Framework 区块 |
70+
| [HQarroum/awesome-iot](https://github.com/HQarroum/awesome-iot) | 加到 Frameworks |
71+
| [sindresorhus/awesome-nodejs](https://github.com/sindresorhus/awesome-nodejs) | 门槛较高,先攒 ≥500 star 再试 |
72+
73+
PR 描述:一行项目说明 + 仓库链接 + 已有 npm published 的证明。
74+
75+
### 6. AsyncAPI 官方 tooling 目录
76+
77+
- 仓库:<https://github.com/asyncapi/website>(tools 数据在 `pages/tools/``config/tools.json`,PR 之前先 search 一下当前位置)
78+
- 目的:让 `@mqttkit/asyncapi` 出现在 <https://www.asyncapi.com/tools> 列表
79+
- 收录条件:开源 + 有文档 + 能跑
80+
81+
### 7. Discord 社群「Show & Tell」
82+
83+
按这些社区的 `#showcase` / `#show-and-tell` / `#i-made-this` 发:
84+
- Elysia Discord
85+
- Bun Discord
86+
- AsyncAPI Discord(很重要,因为你用了他们的标准)
87+
- TypeScript Community Discord
88+
89+
文案要短(1-2 段 + 链接),别复制粘贴博客全文。
90+
91+
---
92+
93+
## B. 中文渠道
94+
95+
### 1. 掘金(juejin.cn)
96+
97+
- **URL**<https://juejin.cn/editor/drafts/new?type=article>
98+
- **标题候选**
99+
- `给 MQTT 写应用,为什么不能像写 Elysia / Hono 一样?`
100+
- `mqttkit:在 Aedes 之上加一层 Elysia 风格的应用框架`
101+
- `用 TypeScript 写 MQTT 后端的正确姿势`
102+
- **分类**:后端 / Node.js
103+
- **标签**`TypeScript` `Node.js` `物联网` `MQTT` `Bun`
104+
- **封面**:用 carbon.now.sh 截 Quick Start 代码即可。
105+
- **正文**:直接贴 `zh-introducing-mqttkit.md`
106+
107+
### 2. V2EX
108+
109+
- **URL**<https://www.v2exhub.com/new>(或 v2ex.com)
110+
- **节点**`分享创造``程序员`
111+
- **标题**`[分享创造] mqttkit:给 MQTT 写应用,像写 Elysia / Hono 那样`
112+
- **正文**:V2EX 不爱长文,建议 300-500 字概述 + 仓库链接 + 文档链接。可以拷贝中文文章的「起因」「Quick Start 代码」「链接」三段。
113+
- **注意**:V2EX 用户对营销文很敏感,开头不要吹,直接讲技术动机。
114+
115+
### 3. 知乎
116+
117+
- **URL**<https://zhuanlan.zhihu.com/write>
118+
- **专栏**:发到自己的专栏,或者投稿 `前端开发` `Node.js` 相关专栏。
119+
- **标题**`给 MQTT 写应用,为什么不能像写 Elysia / Hono 一样?`
120+
- **正文**:知乎支持 Markdown 编辑器,但代码块体验较差。建议把代码段单独贴成 carbon.now.sh 图片或 Gist 嵌入会更好看。
121+
- **末尾**:可以加一个开放性问题引导评论:「你在 MQTT 后端踩过哪些重复造轮子的坑?」
122+
123+
### 4. 微信公众号 / 个人博客(可选)
124+
125+
如果你有公众号,同样标题 + 中文正文。重点:
126+
127+
- 公众号要把所有 GitHub 链接放在「阅读原文」里,正文里写文字版(微信检测外链会限流)。
128+
- 个人博客记得加 `<link rel="canonical" href="https://juejin.cn/...">`,把权重让给掘金。
129+
130+
### 5. SegmentFault
131+
132+
- 投到「Node.js」「TypeScript」标签下。
133+
- 受众和掘金重叠但更技术向,直接复用中文正文即可。
134+
135+
### 6. OSCHINA
136+
137+
- 适合发「项目发布」类。<https://www.oschina.net/news/post>
138+
- 选择 "开源项目",会被收录到 oschina 项目库里,对国内搜索引擎 SEO 有帮助。
139+
140+
---
141+
142+
## C. 长期持续动作(不止一次性)
143+
144+
1. **每次发版本写 changelog 推文**:X / 微博 / 掘金沸点都发一条。"v0.x: 新增 XX,修了 YY。"
145+
2. **录一段 60 秒 demo**:device 连上 → 看到 topic route 命中 → 浏览器打开 AsyncAPI 文档。发 X / B 站 / YouTube Shorts。
146+
3. **写延伸文章**:每个核心特性单独一篇深度文章,比 README 多讲设计取舍。例如:
147+
- "MQTT 5 RPC 在 Aedes 上的实现细节"
148+
- "为什么 mqttkit 用 Standard Schema 而不是自创校验 API"
149+
- "AsyncAPI 3.0 是怎么从路由声明里被推导出来的"
150+
4. **盯 GitHub issue**:第一周高频响应、互动,star 转化率最高的就是这一周。
151+
5. **每 3 个月去 awesome 列表里检查链接是否还在**,顺手 PR 更新描述。
152+
153+
---
154+
155+
## D. 投放节奏建议(一周内做完最有效)
156+
157+
|| 动作 |
158+
| --- | --- |
159+
| D1 周二 | dev.to 发英文版;中文版同步发掘金 |
160+
| D2 周三 | Hacker News Show HN(UTC 早 9 点);24h 内不要去刷票 |
161+
| D3 周四 | Reddit r/typescript + r/node(间隔几小时) |
162+
| D4 周五 | Reddit r/IOT + r/bun;Discord 社群 showcase |
163+
| D5 周末 | Medium 发英文版(canonical 指向 dev.to) |
164+
| D6 周一 | V2EX 分享创造 + SegmentFault + 知乎 |
165+
| D7 周二 | Awesome 列表 PR(一次性提交 3-4 个) |
166+
167+
---
168+
169+
## E. 题图 / 物料准备清单
170+
171+
发布之前先做好这些,避免临到发的时候卡住:
172+
173+
- [ ] 1 张代码题图(1000×420,carbon.now.sh 截 Quick Start)
174+
- [ ] 1 张架构图(broker adapter / middleware / router / asyncapi 四层关系)—— 可选
175+
- [ ] 1 段 60 秒 GIF 或 mp4 demo —— 可选但强烈推荐
176+
- [ ] GitHub repo 的 `About` 描述、Topics(`mqtt` `iot` `bun` `typescript` `elysia` `asyncapi`)补齐
177+
- [ ] GitHub repo 加 Social Preview 图(Settings → Social preview)
178+
- [ ] README 顶部加一行 badge:npm version / license / bundle size
179+
180+
题图直接做不动的话,最低成本:carbon.now.sh 选 `One Dark` 主题,贴 Quick Start,导出 PNG。
181+
182+
---
183+
184+
## F. 你不该做的事
185+
186+
- ❌ 同一天往 5 个 subreddit 一次性投——会被自动判 spam
187+
- ❌ HN 发完拉群求 upvote——会被埋
188+
- ❌ 标题用 emoji 或全大写——HN / Reddit 都不喜欢
189+
- ❌ 中文文章里说 "革命性"、"颠覆"、"最强"——技术社区会被反感
190+
- ❌ 评论区遇到批评直接辩论——感谢 + 记录到 issue 是更好的姿势

blog/cover/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# dev.to Cover Image
2+
3+
dev.to 推荐封面:**1000×420**(实际显示 1000×420,建议 2x = 2000×840 输出)。
4+
5+
## 两个方案
6+
7+
| 文件 | 风格 | 推荐场景 |
8+
| --- | --- | --- |
9+
| `cover.html` | **代码款**——左边标题右边一段 Quick Start 代码 | 偏 dev 受众(dev.to / HN) |
10+
| `cover-minimal.html` | **极简款**——大字号品牌名 + tagline | 偏品牌识别 / 重复使用 |
11+
12+
两份都自带渐变背景 + 网格 + 配色,无需额外素材。
13+
14+
## 怎么导出 PNG(三选一)
15+
16+
### 方式 A:用脚本自动导出(推荐)
17+
18+
```bash
19+
# 在仓库根目录
20+
bun add -D puppeteer # 或 npm i -D puppeteer
21+
node blog/cover/render.mjs # 产出 cover.png + cover-minimal.png(2x 高清)
22+
```
23+
24+
输出在 `blog/cover/cover.png``blog/cover/cover-minimal.png`
25+
26+
### 方式 B:浏览器手动截图(最快,无依赖)
27+
28+
1. 用 Chrome 打开 `blog/cover/cover.html`(或 `cover-minimal.html`
29+
2. 打开 DevTools → 调成 1000×420 的 device emulator,或直接给 body 设 `zoom: 1`
30+
3. 右键 `.cover` 元素 → "Capture node screenshot"
31+
4. 得到 PNG
32+
33+
### 方式 C:用在线工具
34+
35+
把 HTML 内容贴进 <https://htmlcsstoimage.com/><https://www.bannerbear.com/> 一类的服务,设置 1000×420,导出 PNG。
36+
37+
## 上传到 dev.to
38+
39+
dev.to 编辑器右上角 → "Add a cover image" → 上传刚才导出的 PNG。
40+
41+
如果觉得效果不够,可以:
42+
- 调整 `cover.html``:root` 的颜色变量
43+
-`<h1>` 的字号 / tagline 文案
44+
- 直接拿 carbon.now.sh 截一张代码图(最朴素)
45+
46+
## 备选:carbon.now.sh 一键模板
47+
48+
如果上面两个都嫌麻烦,直接打开:
49+
<https://carbon.now.sh/?bg=rgba%280%2C0%2C0%2C0%29&t=one-dark&wt=none&l=typescript&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=152%25&si=false&es=2x&wm=false>
50+
51+
`examples/aedes-basic/src/index.ts` 的 Quick Start 段粘进去,导出 2x PNG 即可。

blog/cover/cover-minimal.html

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>mqttkit cover — minimal</title>
6+
<link rel="preconnect" href="https://fonts.googleapis.com" />
7+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8+
<link
9+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800;900&family=JetBrains+Mono:wght@500&display=swap"
10+
rel="stylesheet"
11+
/>
12+
<style>
13+
html, body { margin: 0; padding: 0; background: #050714; }
14+
body {
15+
font-family: 'Inter', system-ui, sans-serif;
16+
display: flex;
17+
align-items: center;
18+
justify-content: center;
19+
}
20+
.cover {
21+
width: 1000px;
22+
height: 420px;
23+
position: relative;
24+
overflow: hidden;
25+
background:
26+
radial-gradient(900px 500px at 80% 100%, rgba(167, 139, 250, 0.22), transparent 60%),
27+
radial-gradient(700px 400px at 0% 0%, rgba(45, 212, 191, 0.18), transparent 55%),
28+
#0a0f24;
29+
display: flex;
30+
flex-direction: column;
31+
justify-content: center;
32+
align-items: center;
33+
text-align: center;
34+
padding: 0 80px;
35+
box-sizing: border-box;
36+
color: #e6eaf2;
37+
}
38+
.topo {
39+
position: absolute;
40+
inset: 0;
41+
background-image:
42+
linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
43+
linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
44+
background-size: 56px 56px;
45+
mask-image: radial-gradient(700px 380px at 50% 50%, black 30%, transparent 80%);
46+
}
47+
.kicker {
48+
font-family: 'JetBrains Mono', ui-monospace, monospace;
49+
font-size: 14px;
50+
letter-spacing: 0.18em;
51+
text-transform: uppercase;
52+
color: #5eead4;
53+
margin-bottom: 18px;
54+
z-index: 1;
55+
}
56+
.kicker .sep { opacity: 0.5; margin: 0 10px; }
57+
h1 {
58+
margin: 0;
59+
font-size: 104px;
60+
font-weight: 900;
61+
letter-spacing: -0.045em;
62+
line-height: 1;
63+
background: linear-gradient(120deg, #5eead4 0%, #a78bfa 55%, #f472b6 100%);
64+
-webkit-background-clip: text;
65+
background-clip: text;
66+
color: transparent;
67+
z-index: 1;
68+
}
69+
.tag {
70+
margin-top: 22px;
71+
font-size: 24px;
72+
font-weight: 600;
73+
color: #cbd5e1;
74+
max-width: 760px;
75+
line-height: 1.35;
76+
z-index: 1;
77+
}
78+
.tag .hi { color: #fff; }
79+
.footer {
80+
position: absolute;
81+
bottom: 26px;
82+
left: 0; right: 0;
83+
text-align: center;
84+
font-family: 'JetBrains Mono', ui-monospace, monospace;
85+
font-size: 13px;
86+
color: #64748b;
87+
letter-spacing: 0.05em;
88+
}
89+
.footer b { color: #94a3b8; font-weight: 500; }
90+
</style>
91+
</head>
92+
<body>
93+
<div class="cover">
94+
<div class="topo"></div>
95+
<div class="kicker">MQTT <span class="sep">·</span> Bun <span class="sep">·</span> TypeScript</div>
96+
<h1>mqttkit</h1>
97+
<div class="tag">
98+
An <span class="hi">Elysia-style application framework</span> for MQTT.<br />
99+
Middleware, typed topic routes, MQTT 5 RPC, AsyncAPI docs.
100+
</div>
101+
<div class="footer"><b>mqttkit.keyp.dev</b> &nbsp;·&nbsp; github.com/keyp/mqttkit</div>
102+
</div>
103+
</body>
104+
</html>

blog/cover/cover-minimal.png

479 KB
Loading

0 commit comments

Comments
 (0)