2026-05-11 19:30:00
前几天写了篇新文章,照常往 Vercel 部署,结果 pipeline 直接报错了。一开始以为是构建问题,折腾了一会儿才发现不对——是 Vercel 账号出了状况。用 163 邮箱注册的账号怎么都登不上去了。后来才搞清楚,Vercel 把整个 163.com 邮箱根域给封禁了。
如果你是搞独立博客的,大概率听过或者正在用 Vercel。简单说,它是一个前端部署平台,支持自动从 GitHub 仓库拉代码、构建、部署,给你的网站分配一个域名,还自带 CDN 加速。很多人的博客就是 GitHub Pages + Vercel 的组合——代码放在 GitHub,用 Vercel 来部署和托管。
我自己的博客也是这么搞的。GitHub Pages 本来也能直接部署静态页面,但 Vercel 在自定义域名、HTTPS、CDN 这些方面用起来更方便,所以就选了它。
大概在 2026 年 5 月 4 号左右,大量使用 163 邮箱注册 Vercel 的用户发现,自己的账号突然无法登录了。我在网上搜了一下,发现不只是我一个人遇到这个问题。NodeSeek、V2EX 这些论坛上都有人在讨论。
根据网上的信息,Vercel 在 2026 年 4 月中旬遭遇了一次比较严重的安全事件。黑客团伙 ShinyHunters 通过一个第三方 AI 工具入侵了 Vercel 的内部系统,窃取了员工数据、企业后台权限和一些部署凭证,还索要了 200 万美元的赎金。
这次封禁 163 邮箱,大概率是安全事件后的应对措施之一。可能是因为 163 邮箱在此次事件中被大量用于恶意注册或攻击行为,也可能只是 Vercel 对某些邮箱域名做了更严格的风控。具体原因 Vercel 没有公开说明,但结果就是:整个 163.com 邮箱域名被一刀切了。
影响范围不小。很多国内开发者和独立博客作者都是用 163 邮箱注册的 Vercel,这次封禁直接导致这些人的博客全部无法访问。
不过如果你现在 Vercel 账号还处于登录状态(比如工作电脑的浏览器还保持着登录),其实是可以抢救一下的:进账号设置,添加一个新邮箱(Outlook、Gmail 都行),然后把 163 邮箱去掉。这样账号就能继续正常使用,不需要重新部署。网上有人分享了这个方法,能省不少事。但如果你跟我一样,发现的时候已经完全登不上去了,那就只能走下面的重建流程了。
账号被封了,博客打不开了,第一件事就是得搞清楚自己的博客到底是怎么部署的。说实话,这个博客配置的时间已经比较久了,当时配完之后就没怎么动过,具体流程已经记不太清了。
花了不少功夫才回忆起来,大概的链路是这样的:
为什么要搞这么复杂?主要是为了用 Cloudflare 的 CDN 和安全防护能力。阿里云做域名注册商,Cloudflare 做中间层的 DNS 管理和 CDN,Vercel 做最终的静态页面托管。当年配的时候也是参考了网上的一些教程,虽然链路长了点,但稳定运行了很久,也就没再动过。
搞清楚部署架构之后,恢复操作其实并不复杂。
第一步:注册新的 Vercel 账号
这次学聪明了,不用 163 邮箱了,用 Outlook 重新注册了一个 Vercel 账号。其实 Gmail 也行,但考虑到这次是 Vercel 封禁了特定邮箱域名,用国外主流的邮箱服务会更稳妥一些。
第二步:重新部署 GitHub 项目
登录新账号后,连接 GitHub,把博客仓库导入 Vercel。Vercel 自动识别了项目类型,构建和部署都是自动完成的,跟之前的操作基本一样。
第三步:重新配置域名
这一步是关键。因为之前域名解析是阿里云 → Cloudflare → Vercel 的链路,重新部署后需要把域名绑定到新的 Vercel 项目上。
让我惊喜的是,现在 Vercel 在添加自定义域名的时候,已经支持自动修改 Cloudflare 的 DNS 配置了。以前我记得需要手动去 Cloudflare 后台添加 CNAME 记录,现在 Vercel 直接通过 API 帮你搞定,点几下就完事了。
整个恢复过程,从注册新账号到博客重新可以访问,大概花了不到半小时。
不要用国内邮箱注册海外服务。163、QQ 邮箱这类国内邮箱,在海外服务的风控体系里天然就是高风险对象。这次是 Vercel,下次可能是别的服务。建议主力账号用 Gmail 或 Outlook。
记录好自己的部署架构。这次最浪费时间的环节就是回忆部署链路。当时配置完觉得都记住了,结果过了这么久早就忘得差不多了。建议把部署架构和关键配置写在笔记里,哪怕只是几句话。
Vercel 现在对 Cloudflare 的集成做得不错。以前手动配 DNS 的时候容易出错,现在自动化程度高了很多,重新部署的成本降低了不少。
有条件的话,做好备份和多平台准备。这次运气好,恢复起来比较快。但如果 GitHub 仓库也出了问题,或者 Vercel 彻底封了项目而不只是账号,恢复就会麻烦很多。重要的博客内容,建议在本地或者别的平台也有一份。
这次 Vercel 封禁 163 邮箱的事,算是给所有用国内邮箱注册海外服务的人提了个醒。平台侧的安全措施我们控制不了,但自己的账号安全和部署架构,还是可以提前做好准备的。
如果你也遇到了 Vercel 163 邮箱被封、Vercel 账号无法登录的问题,希望这篇文章能帮到你。恢复的核心思路就是:换一个邮箱重新注册,把项目重新部署一遍,域名重新绑定一下。操作不复杂,但前提是你得记得自己的部署链路。
2026-05-11 15:38:16
最近在整理团队的安全开发规范时,遇到了一个老问题:安全规范文档越积越多,但真正用的时候却找不到。每次新人入职,都要翻遍各种文档才能拼凑出完整的安全规范;每次出安全故障,事后总结的经验也散落在各处,下次遇到类似问题还得重新排查。
我试过用Confluence、Notion、甚至Git仓库的README来管理,但效果都不理想。直到看到了Karpathy提的llm-wiki思路,才觉得这可能是个突破口。
Karpathy在Gist里提了个想法:让LLM维护一个Wiki中间层。原话是”the wiki is a persistent, compounding artifact”——这个Wiki层就像个编译器,把原始文档编译成结构化数据,这样LLM每次回答问题时就不用从头在原始文档里”找”信息,直接查Wiki就行。
我觉得这个思路特别妙。传统文档管理是”存”和”找”,而llm-wiki是”编译”和”查询”。原始文档可能很乱、有重复、甚至有矛盾,但Wiki层是干净、结构化、有交叉引用的。
举个例子:我们团队有十几份安全开发规范文档,从输入验证到渗透测试都有,还有近十份安全故障复盘报告。这些文档格式不一,有的在Word里,有的在Markdown里,有的甚至只是Slack聊天记录。如果让LLM直接处理这些原始材料,效率会很低。但如果先”编译”成Wiki,效果就完全不一样了。
我首先把Karpathy的Gist链接丢给Claude,让它基于这个思路生成一份CLAUDE.md文件。这个文件相当于给LLM的”操作手册”,告诉它如何维护Wiki。
生成的CLAUDE.md包含了几个关键部分:
实际用下来,我发现模型生成的结果基本可用,没有做太多微调。只是另外让AI生成了output层,用于存放最终的规范文档。这样三层架构就完整了:raw放原始素材,wiki放结构化Wiki,output放最终输出。
这是最耗时的一步。我把散落在各处的安全开发规范文档和故障案例都收集起来,统一放到raw/sources/目录下。为了保持原貌,我没有修改这些文件的内容,也没有统一格式——PDF、Word、PPT、Markdown等各种格式直接放到raw层就行,模型都可以处理。
具体来说,我收集了:
这些素材质量参差不齐。有的写得很规范,有清晰的标题和列表;有的就是随手记的笔记,甚至还有截图。但没关系,llm-wiki的妙处就在于它能处理这种”脏数据”。
有了CLAUDE.md和原始素材,就可以让LLM开始工作了。我这里用的是Claude,主要是因为它的上下文窗口足够大,能一次性处理较多内容。
具体操作是这样的:
这个过程不是一次就能完成的。LLM生成的初版Wiki会有一些问题:
所以需要多轮迭代。我会检查生成的Wiki,指出问题,让LLM修正。比如:
“这个故障案例里提到了’连接池配置不当’,但为什么没有对应的concept页面?请创建一个[[connection-pool-best-practices]]页面,并在故障案例里引用它。”
经过5-6轮迭代,Wiki层才逐渐成型。
Wiki层准备好后,就可以生成最终的规范文档了。我让LLM基于Wiki层,生成一份面向开发者的、可直接阅读的规范文档。
这里有个设计决策:最终文档是放在output/目录下,而不是直接修改原始素材。这样保持了三层架构的清晰性:
生成的规范文档有几个特点:
最终的规范文档可以直接用Obsidian打开阅读。因为是Markdown格式,支持:
更重要的是,每条安全规范都关联着真实的故障案例。比如在”SQL注入防护”这条规范旁边,就链接着[[sql-injection-vulnerability-incident]]这个故障案例,详细描述了漏洞是怎么产生的、造成了什么影响、怎么修复的。这样开发者不仅能知道”要怎么做”,还能看到”不这么做会出什么问题”。
比起以前散落在各处的文档,阅读体验好了不止一个量级。
更强大的是LLM问答能力。因为有了Wiki层,LLM回答问题时特别准。比如我问:
“数据库批量更新时要注意什么?”
LLM会:
再比如问:”用户上传文件怎么处理才安全?”
LLM会:
这比让LLM在十几份原始文档里”找”答案要靠谱得多。
最让我满意的是更新机制。当有新的规范文档或故障报告时,我只需要:
比如上周我们出了个新的安全故障:因为用户输入未过滤导致XSS漏洞。我把故障报告放到raw目录后,LLM自动:
刚开始时,LLM生成的Wiki质量波动很大。有时能很好地提取关键信息,有时会漏掉重要内容。
解决方案:我细化了CLAUDE.md里的Wiki页面规范。比如要求每个页面必须有:
对于安全故障案例,还要求包含:漏洞类型、攻击向量、影响范围、修复方案、预防措施。这样LLM就有更清晰的指导,生成质量稳定了很多。
Wiki的核心价值在于交叉引用,但LLM经常创建单向链接,或者链接到不存在的页面。
解决方案:在CLAUDE.md里强化了交叉引用规则:
我还加了Lint检查,定期检查断裂链接和孤儿页面。
有些原始素材质量很差,比如截图、手写笔记、甚至是Slack聊天记录的导出。LLM处理这些素材时效率很低。
解决方案:对于这类素材,我会先手动整理成结构化的Markdown,再交给LLM处理。虽然增加了前期工作量,但保证了最终质量。
不要追求一步到位。llm-wiki是个迭代过程。第一版Wiki肯定不完美,但可以通过持续迭代改进。
CLAUDE.md要不断优化。随着使用,你会发现CLAUDE.md里缺失的规则。比如我后来加了”安全故障案例必须包含:漏洞类型、攻击向量、影响范围、修复方案、预防措施”这样的结构化要求。
保持三层架构的纯净。raw/就是raw/,不要修改;wiki/由LLM维护;output/面向人类。混在一起会越来越乱。
定期做健康检查。我会每周跑一次Lint,检查断裂链接、孤儿页面、单向链接等问题。发现问题及时修复。
接受不完美。LLM生成的Wiki不会100%准确,会有遗漏和错误。但相比完全靠人工维护,已经好太多了。
用llm-wiki管理安全开发规范,本质上是把”文档管理”变成了”知识工程”。原始文档是”原材料”,Wiki是”半成品”,规范文档是”成品”。LLM在这个过程中充当了”编译器”和”维护者”的角色。
这个方法最大的价值是可持续性。传统的文档管理,时间一长就会荒废,因为维护成本太高。而llm-wiki把维护成本转移给了LLM,人类只需要:
如果你也在为安全开发规范的管理头疼,不妨试试这个思路。不用完全照搬我的实现,关键是理解”Wiki中间层”这个核心理念。至于具体用什么工具、怎么组织目录,都可以根据实际情况调整。
毕竟,工具是为人服务的,不是吗?
2024-03-06 19:36:22
后端工程师开发前端的痛点,通常来说莫过于太过繁琐,经常要为了一些很小的事查半天。Vaadin很好的解决了这个痛点,为后端工程师提供易上手、方便使用的前端代码编写解决方案,今天我们就来了解一下。
大家好,今天跟大家介绍一个对后端工程师特别有价值的工具——Vaadin。
说起来,上手前端基本的html, css开发,确实并不难,但是如果只会这些基本的东西,开发起来会很繁琐。如果想要使用前端生态中的各种轮子,虽说便利度提升了,但学习成本也会同步上升。所以,如果不是职业的全栈工程师,只是作为一个后端,想临时写点前端代码,比如自己想做点小项目,通常来说都会有个很痛苦的过程。
Vaadin很好的解决了这个痛点。通过vaadin包装好的常用前端组件,我们几乎可以零学习成本的编写出功能完备、不太难看的页面。对于后端背景的程序员来说,无疑会大幅度降低自己做些小项目的成本。
Vaadin提供的功能,就是可以直接用java代码来写页面。Vaadin提供了多种输入框、表单等等封装好的前端样式,而且与springboot做了深度的融合,使用起来非常方便。
Vaadin的实际原理并不复杂,主要是基于服务端渲染,即在后端生成最终的html代码,交给浏览器。服务端渲染,这个并不罕见,与客户端渲染的优势和劣势,我们在这里不多讲。当然,对于vaadin来说,使用服务端渲染,似乎也没什么好说的,毕竟是写的后端代码,直接在后端做渲染,是个再正常不过的实现路径。Vaadin的引擎对前后端之间的交互做了封装,所以对使用者来说,前后端之间的交互是无感的,在页面层,我们也可以正常的调用后端service.
下面是我写的一段代码示例,可以更直观的感受Vaadin的作用:
1 |
@Route(value = "path", layout = MainView.class) |
这段代码中,我们完全使用java代码对页面中的各个组件进行了编排,包括button的click函数,也是使用java开发者习惯的方式定义,并且能够直接调用其他后端service, 可以说是几乎零学习成本了。
详细代码和运行效果,可以到 项目地址中查看。
如果你对Vaadin感兴趣,或者有任何问题或想法,欢迎在评论区交流。一起探索如何更好地利用Vaadin,提升我们的开发效率吧!
2024-01-22 18:12:31
对于hexo的多语言方案,官方并没有提供很完备的支持,但是网络上有很多人尝试过不同的解决方案。今天,我们对这些方案简单做个总结,看看我们需要的多语言方案是什么样子的。
其实,hexo的多语言方案,说白了就是两个思路,一个是在文章维度切换语言,借助hexo原生的能力和hexo-generator-i18n等插件,用一套框架维护两个语言的内容;另一个则是独立维护不同语言的样式、内容,在站点维度切换语言,只是在域名层面合并到同一个域名。
具体要选择什么方案,取决于大家想要一个什么样的多语言网站。一开始我想象中的多语言网站的是第一种,即统一的首页,在首页和每篇文章上都支持切换语言,对每篇文章,可以通过切换语言,直达对应的其他语言译文上。
这个思路,借助于hexo-generator-i18n插件,可以实现,但要做比较多的定制开发。所以,我又重新思量了一下思路,我要的多语言网站应该是什么样。重新考虑之后,逐渐觉得第二种思路才是更合理的。我们要做一个多语言网站的目的是什么?对我来说,其实就是要获取一些英文流量。作为一个未备案网站,已经很久无法获取百度的搜索流量了,google在中国占的份额又太低,所以单凭中文内容,很难从搜索中获取较高流量。所以我希望通过提供一些英文内容,获取英文流量。这个目标下,完全不需要对所有的文章都维护多语言版本。何况,不同文章的语言受众也的确有很大区别,所以,语言的切换可以直接在站点层面进行。
具体操作方式上,一种是直接维护多个站点,两个站点内容、样式完全独立,只是部署在同一个域名下。比如中文站点根路径是lichuanyang.top,英文站点根路径是lichuanyang.top/en . 两个站点上各做一个跳转链接,向对方跳转。
我采用的是另一种方式,相比上述方式,成本小一些,不需要实际维护两个站点。原理大致如下:
在本地,同样维护两个站点。但是英文站点不会去做实际的部署,只是用作生成静态网页的工具。英文版的内容生成之后,直接复制到中文网站的对应目录下,只对中文网站做部署即可。
具体操作步骤如下:
将博客目录整体复制一份,作为英文博客目录. 例如,我的博客根目录叫blog.source, 复制出一个blog.source.en. 这步完成后,如果博客源码也是以git维护的,可以直接在原目录的外层直接新建一个git项目,在外层做管理就行了。 示例如下:
1 |
blog(git根目录)/blog.source |
将en目录下的文章全部删除;站点描述等文本调整为英文内容;英文语言设置为en;英文站点根 (_config.yml中的root配置)设置成/en
在两个站点下增加跳转链接。我是借助菜单功能,直接在菜单里加一个其他语言项。 next主题中配置如下:
1 |
中文站 : English: /en || fa fa-language |
之后配置基本就完成了,可以在en目录下写英文文章,流程与之前写中文文章时完全一致
最后就是生成和发布环节了,这一步要注意,每次生成时,我们需要先生成中文站点,再生成英文站点并将英文内容复制到中文目录下,避免生成中文内容时将英文内容覆盖掉。具体操作示例如下:
1 |
hexo clean && hexo g &&cd ../blog.source.en && hexo clean && hexo g && cd ../blog.source &&cp -r ../blog.source.en/public/. public/en/ && hexo s |
命令最后一段,使用hexo s就是本地启动,使用hexo d就是发布出去,和正常使用时一样。
这样,我们就有了一个好用的多语言站点了。之后,写中文内容就在中文目录下进行,写英文内容就在英文目录下进行, 最后执行一下上边的命令就可以了。
2023-12-22 17:24:18
一个优化知乎显示的油猴工具
之前在 小众软件 上看到一个知乎评论区时间展示混乱的吐槽,有大佬立刻就给了一个油猴脚本。
用了一段时间很舒服,然后前几天知乎做了个更新,导致展示开始混乱。研究了一下,做了更新,想着就发布到GreasyFork上吧,后边知乎再有调整,可以直接从线上更新。
脚本地址: 安装地址
大家感兴趣的话,欢迎留言,后边考虑做个油猴脚本的开发教程。
2023-01-28 19:06:17
这篇教程不会关注 kubernetes 的部署、架构、实现方式等内部原理,而是完全从一个使用 kubernetes 的开发人员的视角去介绍kubernetes是什么,从而帮助了解如何更好的去利用 kubernetes 的特性。
对于kubernetes是什么,一个比较官方的定义,Kubernetes是一个开源容器编排平台,管理大规模分布式容器化软件应用,简称为k8s.
通俗一些来讲,k8s 的核心理念是以应用为中心,向下屏蔽基础设施的差异,向上通过容器镜像实现应用的标准化,帮助开发人员在仅需关注应用本身,无需考虑部署、容灾、伸缩等运维细节的情况下,开发出大规模、可靠的分布式应用。
如上所述,k8s的核心优势就是降低运维成本,让应用开发人员能够专注于应用本身。具体来讲,包括如下几点:
这篇kubernetes教程的剩余部分会从应用开发人员视角,介绍需要了解的k8s概念,主要包括以下几类:
这是k8s内部大部分逻辑的实现方式,了解这个逻辑,可以帮助开发人员更好的理解某些特定情况下k8s的行为。
如果翻看k8s的yaml配置,就会发现k8s的大部分组件中都有一个或若干个spec字段,它的含义很好理解,就是对于某个属性的期望值,比如一个服务节点数量的期望值。
controller的作用就是持续监测期望值和实际指标是否一致,不一致时就进行相应的操作进行处理,比如发现节点数不够了,就会启动对应数量的新节点。
这样,无论是上线、扩缩容、还是故障恢复,我们会发现这些逻辑都变得非常清晰,都是触发一下对容器数期望值或实际值的调整,然后让controller去增删节点就可以了。
这套机制是k8s里广泛使用的一个底层设计原则,作用是解开监控和操作逻辑之间的耦合。举个例子,我们在很多情况下都需要触发启动新的节点,包括要做扩容时、部分节点挂了时、要上线时,等等。这些完全没关联的场景如何去触发相同的操作,代码要如何保持整洁呢?k8s的做法就是用一个期望值的概念做中间层,将操作的触发时机和操作本身拆分成独立的逻辑。
了解了这个逻辑,我们可以更容易的理解k8s是怎么工作的。 比如基于HPA做自动扩容,就是根据当前的CPU使用情况,和期望的CPU使用量,决定是增加还是减少节点。
Deployment 用于指示 k8s 如何创建和更新应用程序,我们通常理解的一个“服务”、“应用”大体上就对应在deployment这一层上。 像镜像、内存cpu使用限制、节点数、环境变量等各种配置,都是在deployment上。
pod比较好理解,就是我们通常理解的一个节点。pod是 k8s 中最小可管理单元。
Service是一个需要着重了解的概念,它是一个抽象层,它选择具备某些特征的 Pod(容器组)并为它们定义一个访问方式。 通常的应用场景下,deployment和service是一一对应的,但实际上deployment和service之间并没有必然的联系。
一个请求恰好可以打到某个deployment关联的所有pod上,看起来是一件非常正常的事情。而事实上,这件事情的发生不是由于这些pod通过一个deployment产生,而是因为deployment上配置了一个标签,通过这个deployment生成的pod都获得了这个标签; 同时,另外存在一个service, 设置通过这个标签选择pod, 请求进入的是通过service选择出的一批pod.
k8s提供了一些用于应用扩展的接口,其中就包括probe和hook, 可以帮助应用更好的利用k8s的能力。
k8s提供了 liveness和 readiness 探针,顾名思义,liveness probe的作用是判断应用是否存活,readiness probe的作用是判断应用是否已经启动完毕。
应用可以用http接口等不同形式提供探针,k8s则负责在探测到不同状态时做不同的操作。比如说探测到liveness probe返回状态异常,则可以认为此节点已经丢失,对此节点做删除处理。
hook则支持应用在一些特定阶段插入一些行为,比如preStop hook, 运行应用关闭之前先做一些操作,可以用来做graceful shutdown.
通过对本篇kubernetes教程上文列出的概念的了解,相信大家可以从应用开发人员的视角完全理解kubernetes是什么, 也可以大致想象出应用方使用k8s的基本思路: 构建标准镜像,暴露出必要的监控数据,剩下的事情都交给k8s来做。
在准确的数据下,k8s可以帮我们做好系统的自愈、扩缩容等操作,而我们要做的是,结合应用场景,尽量给出更加准确的监控数据。
举一些例子,比如说应用提供的liveness探针,如何能尽量确保准确的展示系统的健康状态,某个接口可以访问,某个端口可以调通,能否等价的说明这个应用是否健康。
比如说k8s以cpu、内存等指标来判断系统负载时,这些机器指标能否真实的展现出应用的真实负载,是否有可能连接池、IO等其他瓶颈会在cpu、内存等瓶颈到来之前就达到。
这些都是需要应用方结合应用的实际情况精心设计的。
此外,k8s的设计理念决定了在k8s环境下,“重启”会是一件非常稀松平常的事,像硬件故障、小概率的死循环、不健康的gc, 等不同类型的问题,都可以在k8s的自主重启下实现自愈; 自动的扩缩容扩充中,也会经常性的有节点启停。 所以说,应用需要让自己的启动流程顺畅一些,避免大量耗时或大量资源消耗。