普通视图

Received today — 2025年6月4日技术

MinIO最新社区版砍掉 Web 管理功能

作者ysicing
2025年6月3日 22:14

近日,MinIO 社区版迎来重大更新,删除了 11 万行代码,彻底移除 Web 管理控制台功能,官方称此举为“精简代码,专注核心存储功能”。这一决定引发社区热议,对用户体验和项目生态造成显著影响。本文将深入剖析这一变化的细节、影响及社区反应。

Web 管理功能移除的影响

MinIO 作为一款高性能分布式对象存储系统,因其 S3 协议兼容性和易用性深受开发者喜爱。其 Web 管理控制台是社区版的核心功能,支持存储桶管理、文件浏览和权限配置,极大降低了操作门槛,尤其适合:

  • 非技术用户:通过直观界面快速上手,减少对命令行的依赖
  • 团队协作:便捷管理存储资源,提升效率
  • 实时监控:查看文件列表、使用量等存储状态

然而,新版本将 Web 控制台的核心管理功能(包括用户账户管理、访问策略配置、存储桶管理和系统配置)全部移除,官方推荐使用 mc 命令行工具替代。相比可视化界面,mc 命令行工具对新手不够友好,且在快速调试和监控场景下效率较低。

新老控制台对比

2025.4.22

2025.5.24

被移除的具体功能

此次更新几乎清空了 Web 管理控制台的功能,仅保留基本的对象浏览能力。以下是主要受影响的模块:

  • 账户与策略管理
    • 移除用户账户创建、访问密钥管理和策略设置功能
    • OIDC 单点登录等高级认证功能被砍,用户只能使用管理员初始账户登录,无法通过界面创建新访问密钥
  • 配置与集群管理
    • 无法通过界面查看或修改服务器配置、后端存储设置及集群状态
    • 所有实例调整和服务监控需依赖命令行或 API
  • 存储桶管理
    • 删除新建存储桶、设置策略、版本控制和生命周期规则的界面功能
    • Web 界面退化为纯对象浏览器,仅支持列出存储桶及文件
  • 其他功能
    • 服务器信息仪表盘、多节点集群视图、日志和通知配置等管理功能全部移除

简言之,新版 Web 界面已不再是管理控制台,而是一个功能极简的文件浏览器。用户被迫转向 mc 命令行工具完成所有管理任务,操作门槛显著提高

受影响的版本

  • 2025.4.22 版本是最后一个保留完整控制台功能的版本, 可用镜像 ccr.ccs.tencentyun.com/k7scn/minio:2025.4.22
  • 2025.5.24 版本则包含了删除控制台功能的改动(MinIO 官方在发布说明的 “重要事项” 中明确提到嵌入式 Web 控制台(Console)已被弃用,并移至独立的 object-browser 项目)

社区反应与讨论

MinIO 官方称,删除 11 万行代码旨在优化性能、聚焦核心存储功能。然而,Web 控制台资源占用较低,维护成本有限,社区对这一理由普遍质疑。相比之下,MinIO 商用版保留了完整的 Web 管理功能,功能更强大,引发了“社区版精简是为了推销商用版”的猜测

X 平台和 Reddit 等社区反馈显示,开发者认为这是开源项目商业化的常见策略:通过削减免费版功能,引导用户转向付费版本。部分用户甚至称此次更新为特洛伊木马式更新,批评官方未提供过渡方案,给中小团队带来额外适配成本

作为回应,社区迅速行动:

  • 启动 OpenMaxIO 分支项目,试图恢复被移除的功能
  • SeaweedFS 和 Garage 等替代方案获得更多关注

结语

MinIO 社区版移除 Web 管理功能的决定,削弱了其易用性和吸引力。开源项目的核心在于平衡社区需求与商业利益,MinIO 的后续决策将直接影响其在对象存储领域的地位。对于依赖 Web 控制台的团队,建议暂时停留在 2025.4.22 版本,或探索其他替代方案。


欢迎关注,可以看看我郑再打工每天都在折腾什么。

Received yesterday — 2025年6月3日技术

《漫威银河护卫队》,一段美妙的太空冒险

2025年6月3日 21:27

多年以前在epic领取的《漫威银河护卫队》,碍于对漫威没啥兴趣因此一直没玩。最近有些闲余时间,因此下下来仔细的品一品。

想不到游戏优化异常很棒,手感和画面和以前玩的《星球大战™:前线™ II》很像,但是是剧情类的游戏,因此决定慢慢玩玩看。一开始吸引我的其实是主角团的对话内容,配音很出色仿佛像是在看电影一样。剧情也很不错,很少吸引人。唯一美中不足的地方是放技能按键太繁琐了,也不方便选中敌人。不过也还好全程普通难度通关。不过游戏玩法上确实也是有些单一,只能控制星爵,其他角色也不能控制,另外也就是那些敌人,花样不多。但是能打两把空战还是挺不错的。

{cat_gallery}









{/cat_gallery}

玩了一半的时候感觉还不错啊,打算把电影补一补,哇,除了主角其他角色都选的好丑啊qaq,这时候才发现游戏的建模确实不错。比如像《战神》里面奎托斯的德拉克斯和像《命令与征服-红色警戒2》里的尤里的瑞克,以前其他角色比如曼蒂丝、怪物女王等都是颜值在线。另外电影剧情有些迷,并没有游戏好,有些失望。

游戏流程不到20小时,还是很值得体验的!

镜头与代码的交响:打造个人线上画廊与假期随笔

作者Innei
2025年6月3日 22:14
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/notes/193

这些天,日子很短。却过得很有意义。

Vibe Coding: 线上画廊

自从前段日子买了一台相机之后,出门的欲望也多了不少,到哪都要拍几张。日积月累想要做一个展示一些成片的地方了。不少大佬都会有一个属于自己的网站展示,因此萌生了一个想法,也做了一个。

本次开发基本全程使用 claude-4-sonnet。花了一天时间就把模子刻好了。

:::gallery :::

这是一个雏形。

后来,我开始研究图片预览的高性能方案,又引导 AI 写了一个基于 LOD 方案的 WebGL 图片预览器。即便是预览一张使用哈苏 X2D 拍摄的一亿像素的图片在缩放和加载也不会卡顿。

接入了 Live Photo 的播放, 也是通过这个了解到 WebCodecs API,使用它对 mov 视频转码然后播放。

接入了 HEIC/HIF 文件的解析,调用了 libheic 的 wasm 进行的转换。

而我上面做的全部一是因为偷懒,我可以直接把拍摄的 Live Photo 或者 HIF 文件放到 S3 上不需要自己的格式转换;二是通过一个项目学到新的东西。

今天我又对这个项目进行了构建优化,使用 Cluster 模式,同时处理多张图片,节省了约 3 倍的时间。而这一切都是基于引导话术 AI 进行的重构和优化。

项目地址:

https://github.com/Innei/photo-gallery

如有需要的摄影爱好者也可以试试它。

我的线上画廊:https://gallery.innei.in

后续,对项目还有更多的展望,比如动态化等等。

好友来访

端午假期,南京的朋友来这边玩了几天。一起帮拍了很多照片。去了周边的景点。

组团 Coding 和做了一顿饭。

@MaxtuneLee 共建 Gallery,以及未来推出的摄影工具链。

看完了?说点什么呢

端午再游康定+看高山海子

2025年6月3日 11:08

null

没想到这个月又来了一次康定,不过这次比上次还要玩的高兴。

不过中途发生了个意外,我们有个新手队员不听劝告在上山时走了野线,差点迷路在山里。

我们在山上等了一两个小时,眼看着好像要下雨了,时间已经下午三点了,时间越晚山上的天气就会越恶劣。所以我们打算先下撤到下面的草甸去她分线的地方等待,同时在下山途中和所有能见到的人询问,并帮忙散播这个信息,好在下去后很快找到有信号的地方,而且运气很好的是她也有信号,所以第一时间联系上了她。

听她说她马上登顶了,但是她说没看到其他人,我们就知道她估计完全走错了方向,因为我们在山顶上的海子边上碰到了十几号重装徒步的人,上面闹哄哄的,不可能没人,所以立刻让他马上原路下撤,而我们则是找了两块岩石中间,拿出急救毯挡住风口生活等待,最终4点左右看到她的身影时才松了一口气。

这里告诫所有出去户外徒步的朋友,一定要跟随大部队行动,上山前掌握好基础徒步知识,如:如何看户外轨迹地图、基础求生避险知识等等。

因为端午都三天假期,所已这次的规划是两天一夜的行程,留一天假期在家里修养恢复。

因为最近四川的天气都不太好,所以我提前一个多星期就开始关注想去的景点天气,但是通过手机上自带天气软件给的天气数据,这类软件都是通过当地的气象站来给出天气数据,而我们去的山上基本上都没有气象站,而且高山地区天气变化无常,所以需要通过专业的天气软件观测云层、风向等数据进行评估。

这次用的Moteoblue和Windy这两个软件进行观测。

💡
Windy中的预测模型可以选择多个模型推测的数据进行对,其中也有Moteoblue的数据,但是无法展示Moteoblue的云层信息。
Windy上发文当天康定老榆林村的天气情况

不过可能是他们的推测模型不太一样,Windy的ECMW模型刚开始的时候推测这几个地方没雨,但是临近出发的日子后却又提示有雨。而Moteoblue则相对稳定,一直显示都是31号和1号白天至少是阴天,没有下雨,而且圈子里驴友们都更认可MoteoBlue的数据推断。

所以我们这次的目标就定在了新榆林村的白海子和前面的野马海子。

最后出发时确定的人员有8人,4男4女,其中两位车主,一辆奥迪轿车,一辆小排量suv。其中有老朋友林老师、小罗,本来上次泸州的刘姐也想去的,但是他有事耽搁了。

行程安排如下。

  1. 31号早上7点出发康定,在天全休息吃饭
  2. 31号下午2点继续出发到第一站野马海子,这个只有200左右的爬升,权当作适应拉练。
  3. 31号下午5点左右下山,驱车康定新城吃饭,因为上次老城吃过找不到停车位的亏,所以打车去老城逛街。
  4. 逛完去民宿,休息。
  5. 1号早上6点起床,7点出发白海子,民宿出门几百米就是出发点,今天强度比较高7公里左右有1300的爬升。
  6. 1号下午2点左右登顶,然后下撤,5点左右到山脚
  7. 在康定新城吃个饭,然后回程,预计10点左右到家

费用明细:

  • 车费:我们坐的奥迪,最终均摊下来得车费170元左右一人,那个suv应该会更低。
  • 住宿费:119标间
  • 两餐正餐:(351+279)/8=78.75
  • 我和霜的总费用:170+170+119+78.75=537.75

行程开始。

出发

历时5个小时,终于到康定了

马不停蹄的赶往野马海子

去往野马海子的哑口有个小海子

刚到起点便下起了毛毛细雨

1. 和林老师 2. 大家伙合照

河流和边上的乱石滩上长了很多像荷叶一样的植物

能看到雪山了,翻过去就是海子

下着小雨的野马海子别有一种特殊的韵味。

拍下了人生照片,可惜当时嫌雨大,我自己没拍单人照片。(后悔!)

营地里和其他徒步客一起休息,我老婆看到这张会杀了我吧??

海子和海子上的雪山

拍点团体照

快到出发点了

1. 林老师 2. 小罗

到出发点时发现天气晴了一点,雾气稍散

到康定城,准备找吃的。

牦牛火锅(忘记拍了,回头找其他人要了再补一张图片)

溜溜河的水流比上次来的时候急了很多。

当了一回卖唱歌手😄,倾听地址:

3.07 复制打开抖音,看看【跨过远山而来的风的作品】在康定当了一回卖唱歌手 😄 # 山丘 https://v.douyin.com/CmP946UHSjI/ OXM:/ 05/11 j@c.an

秀个恩爱

林老师有点EMO,所以陪他在康定城门口放肆高歌。

0:00
/0:13

之后便去了民宿,因为玩的太嗨了,忘记拍照了。

第二天7点出发白海子

开始进入树林

杜鹃花开的很漂亮

拐过一个路口霍然开朗,云层也渐渐散去,几座不知名雪山出现在视野里,出太阳拉!!

峡谷里的雾被风渐渐吹走

爬过一个绝望坡,合个影。

山谷全景

太阳通过树枝,让这里像是时空隧道​

喝个水

到达河谷路段

又一个绝望坡

能看近距离看到雪山了

这里上去就是一个大草甸

高山草甸,这里是有虫草的窝。

远处的雪山

爬爬爬,没完没了的坡,爬升比勒多曼因可多的多。

路边的小黄花

让花和雪山和个影

翻过这个坡就到了

最后时刻

0:00
/0:15

白海子近景

运气真的很好,一直都有阳光。

人生照片,我怎么这么帅!

哈哈,出发前在抖音上看到的玩法,我也试试。

对了,我们还碰到了一个十几个人的重装队伍,带了好多好吃的,很热情的叫上我们蹭吃蹭喝。

体验了一次在山上乞讨,人家还给你备了碗筷,哈哈哈。

0:00
/0:13

虽然一直在吃喝拍照,但是一直没忘记迷路的那个队友,我们等到三点半时山风把大雾吹了过来,天气也越来越差,我们只能先下撤到下面草甸在联系她。

运气不错的联系上了她,我让她立即原路下撤,我们在草甸这里等她。山上降温,起大雾,还若有若无的有些雨珠,也不知道她需要多久过来,所以只能先找个避风的地方生火取暖,保存体温。

等了一个多小时,终于来了,这才算是松了一口气。

这个姐姐第一徒步,冲锋衣不透气,里面的衣服也速干的,在山上要迷路了,真的是非常危险的,之后我们下山了,才发现四川的户外群里她算是出名了,各个群都在问有没有看到一个红衣服小姐姐....。

最后急速下撤,终于在下午五六点左右下撤到民宿。

抛去队友迷路这个事,这个旅程还是非常开心的,最后在起点拍个合照。

Received before yesterday技术

值得入手的Steam模拟类神作:缺氧(Oxygen Not Included)

作者ysicing
2025年5月31日 23:07

各位小伙伴!今天给大家安利一款超硬核、超上头的模拟经营类游戏——缺氧(Oxygen Not Included)! 如果你喜欢挑战脑力、沉浸式体验和无限可能的建造乐趣,这款游戏绝对不容错过!

缺氧是由 Klei Entertainment(饥荒开发商)打造的一款生存模拟类游戏。玩家需要在一个地下星球中管理一群可爱的小人(复制人),通过建造基地、分配资源、应对环境危机,让他们在缺氧、缺食、缺电的恶劣环境中存活下来!

特点

  • 超高自由度的建造体验
    在游戏中,你可以自由规划地下基地,设计复杂的管道、电力和氧气系统。想建一个自给自足的生态圈?还是一个高科技的自动化基地?全看你的脑洞!从简单的茅厕到复杂的核反应堆,建造过程既烧脑又满足
  • 硬核的资源管理
    氧气、食物、水、能源、温度……每一项资源都需要你精打细算。稍有不慎,小人可能会因为缺氧窒息、食物中毒,甚至被高温烫伤
  • 随机生成的挑战
    每次开局的地图和资源分布都不相同,随机事件(如陨石雨、火山喷发)让每一局都充满新鲜感。你需要根据环境灵活调整策略,永远不会觉得无聊
  • 丰富的 MOD 支持
    社区 MOD 丰富多样,从增加新建筑到优化游戏体验,应有尽有!无论你是想降低难度还是挑战极限,都能找到适合自己的玩法

我的玩法开局看水和铜多不多,不多重开

适合人群

  • 喜欢模拟经营
  • 热衷挑战的硬核玩家:喜欢烧脑、追求极限生存
  • 创意玩家:想打造独一无二的地下乌托邦

当然耐心很重要, 同时要有自我控制能力,因为很容易肝上头

入手性价比

推荐直接购买缺氧完整包, 我上个月入手大概不到 90CNY。

缺氧完整包

推荐购买正版,请勿相信廉价购买,谨防虚假入库,导致游戏和钱两失。

新手建议

  • 前期多关注氧气和食物,善用“暂停”功能规划基地
  • 游戏上手稍有难度,但官方有中文支持,社区教程也超多,入门后根本停不下来
  • 多在 B 站搜搜相关教程
  • 早期多重开几次就会玩了。

官方介绍节选

  • 建立广阔的基地以及探索生存所需的资源:
    从挖掘、资源分配到管道、电力系统,太空殖民地的一切都在你的掌控之下。然而,资源会从你第一次呼吸开始被消耗 ,所以如果你想生存下来的话,就一定要确保你探索得够快。
  • 伴随着压力模拟的精神状态问题
    给你的殖民地提供娱乐休闲活动、优越的住宿条件和更好的食物,来保证生存不会受到精神状态的影响。每个复制人之间都有差异,会对压力做出不同的潜在破坏性反应。 所以不管付出什么代价,一定要确保他们拥有愉悦的心情
  • 用复杂的气体和液体模拟系统来提高效率
    建立一个连锁的管道系统,可以迅速的将燃料和液体输送到基地的关键区域。优质的规划以及获得的加成可以让你的殖民地转变成一个运转良好的不朽机器
  • 通过电网模拟系统来获得电力:
    你可以通过众多不同的能源来获得电力,包括煤,氢,天然气或者仅仅是朴实老旧的油脂。修复电力流失,电路过载和崩溃问题以保持你殖民地的顺利运行

欢迎关注,可以看看我郑再打工每天都在折腾什么。

Lucky-canvas 抽奖插件折腾记

作者林木木
2025年5月31日 20:02

基于 「Lucky-canvas」 抽奖插件,借助 字节跳动 trae 做了个大转盘,全屏显示、替换背景、概率设置、一键导入、边框修改等能想到的和需要的功能都已实现,效果远超预期。

体验地址:https://immmmm.com/dzp/

使用手册 📚

1.打开网址, 右下角“全屏按钮”,右上角是“设置入口”。点击中间开始转动,结束时会弹出中奖内容,并伴有撒花。

2.右上角点击进入“设置”。首次打开,点击安装字体“Aa 年度最可爱”。

3.设置分层两列,左侧显示的奖项名称、中奖概率,可以直接修改或删除。说明:某一奖项的数值是这一项中奖率的“分子”,所有数值总和是“分母”。如“棒棒糖”的中奖率设置的 15,则中奖率是

15/(10+10+……+15+10+10+5)

4.左侧奖项列表下方是 4 个按钮。顾名思义,第 1、2、4 项试试就知。说明一下第 3 项“上传配置”,采用了“上传 txt 文件,一行一条内容”的形式,一键导入。如:本地新建了以下内容「姓氏+空格+中奖数值」

 1
 1
 1
 1
 1
 1

点击“上传配置”,选择 txt 文件,导入即可。所以,如果要换电脑快速导入,只要编辑保存好自己的“奖项.txt”到别的电脑,导入即可。

5.配置右侧,点击可更改转盘配色、指针、内外层转盘背景。

随心搭配,换着玩儿~

致谢

抽奖组件 lucky-canvas 素材大全

🎉

轻松管理K3s集群服务:System Upgrade Controller 的超实用指南

作者ysicing
2025年5月30日 21:20

K3s 作为轻量级 Kubernetes 发行版,以其高效、简洁的特性深受开发者与运维人员喜爱。但手动升级 K3s 集群可能是个繁琐的任务,幸好有 System Upgrade Controller!这个工具能让你的 K3s 集群实现自动化、无宕机升级,省时又省心。本文将带你了解 System Upgrade Controller 的魅力,并提供简洁的部署步骤,让你的集群管理更轻松!

主要用于升级 k3s 集群节点上的服务,不仅仅局限于 k3s 服务本身。

简介

System Upgrade Controller 是 Rancher 开发的一个自动化升级工具。它通过 Kubernetes 原生资源(如 Plan)管理节点和 K3s 版本的升级,核心优势包括:

  • 自动化:一键配置,自动完成 K3s 版本升级
  • 零宕机:逐节点升级,确保服务不中断
  • 灵活性:支持自定义升级策略,适配各种集群规模
  • 轻量高效:与 K3s 的低资源占用理念完美契合

如果你想让 K3s 集群保持最新或者减少运维负担,绝对值得一试!

项目地址:

在 K3s 上部署

以下是快速部署 System Upgrade Controller 的步骤,简单易上手

kubectl apply -f https://raw.githubusercontent.com/rancher/system-upgrade-controller/master/manifests/system-upgrade-controller.yaml

或者

kubectl apply -k github.com/rancher/system-upgrade-controller

服务控制器默认会部署到 system-upgrade 命名空间下

kubectl get deploy -n system-upgrade
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
system-upgrade-controller   1/1     1            1           335d

使用场景

常见使用如下,由于权限极高,操作时需要确保重复执行没影响。

  • 升级 k3s 本身
  • 升级 k3s 集群节点服务

升级 k3s 服务

由于我现在的环境特殊,只有一个 master 节点,每次跨版本升级 master 节点都是先手动升级到最新版本,然后在使用下面的命令升级计算节点。(保障至少 1 个控制节点版本是最新的)

---
apiVersion: v1
kind: Secret
metadata:
  name: k3s1306
  namespace: system-upgrade
type: Opaque
stringData:
  upgrade.sh: |
    #!/bin/bash

    set -x

    binfile=$(command -v k3s)

    $binfile -v | grep "v1.30.6" && (
      echo "done"
      exit 0
    ) || (
      wget https://c.ysicing.net/oss/tiga/linux/amd64/k3s
      chmod +x k3s
      mv k3s $binfile && systemctl restart k3s
    )
---
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: k3s1306
  namespace: system-upgrade
spec:
  concurrency: 3
  nodeSelector:
    matchExpressions:
      - {key: kubernetes.io/os, operator: Exists}
  tolerations:
  - {operator: Exists}
  serviceAccountName: system-upgrade
  secrets:
    - name: k3s1306
      path: /host/run/system-upgrade/secrets/k3s1306
  cordon: false
  version: latest
  upgrade:
    image: hub.ysicing.net/ysicing/debian-upgrade:20230909
    command: ["chroot", "/host"]
    args: ["sh", "/run/system-upgrade/secrets/k3s1306/upgrade.sh"]

想了解更多官方的姿势,可以参考

升级集群服务

  • 升级 tailscale 服务
---
apiVersion: v1
kind: Secret
metadata:
  name: ts-script
  namespace: system-upgrade
type: Opaque
stringData:
  upgrade.sh: |
    #!/bin/bash

    set -x
    if tailscale version 2>/dev/null | grep -q "1.82.5"; then
        echo "Tailscale 1.82.5 already installed"
        exit 0
    fi
    export DEBIAN_FRONTEND=noninteractive
    apt-get update -qq
    apt-get install -y --no-install-recommends tailscale
---
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: ts1825
  namespace: system-upgrade
spec:
  concurrency: 1
  nodeSelector:
    matchExpressions:
      - {key: kubernetes.io/os, operator: Exists}
  tolerations:
  - {operator: Exists}
  serviceAccountName: system-upgrade
  secrets:
    - name: ts-script
      path: /host/run/system-upgrade/secrets/ts-script
  cordon: false
  version: latest
  upgrade:
    image: hub.ysicing.net/ysicing/debian-upgrade:20230909
    command: ["chroot", "/host"]
    args: ["sh", "/run/system-upgrade/secrets/ts-script/upgrade.sh"]
  • 升级 easytier
---
apiVersion: v1
kind: Secret
metadata:
  name: debian
  namespace: system-upgrade
type: Opaque
stringData:
  upgrade.sh: |
    #!/bin/sh
    set -e
    if easytier-core -V 2>/dev/null | grep -q "2.2.4"; then
        echo "easytier 2.2.4 already installed"
        exit 0
    fi
    apt-get --assume-yes update
    DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade --assume-yes
    curl https://c.ysicing.net/oss/scripts/easytier.sh | bash
---
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
  name: debian-25021514
  namespace: system-upgrade
spec:
  concurrency: 3
  nodeSelector:
    matchExpressions:
      - {key: kubernetes.io/os, operator: Exists}
  tolerations:
  - {operator: Exists}
  serviceAccountName: system-upgrade
  secrets:
    - name: debian
      path: /host/run/system-upgrade/secrets/debian
  cordon: false
  version: latest
  upgrade:
    image: ysicing/debian
    command: ["chroot", "/host"]
    args: ["sh", "/run/system-upgrade/secrets/debian/upgrade.sh"]

通过上面 3 个例子,其实就是帮你去每个节点执行相关脚本,如果你有大量类似的重复的工作,System Upgrade Controller 就是个绝佳的运维工具,它让版本管理变得简单、高效、无忧,显著提升你的运维体验。


欢迎关注,可以看看我郑再打工每天都在折腾什么。

枇杷熟了的五月月报

2025年5月30日 20:32

今年五月挺精彩,中美关税战暂时告一段落了,伯克希尔开年会了,比特币又上新高了,Google IO又来了。为了赶进度,五月真的挺忙的,这些东西也都没有怎么关注。月底也到了,博客还是要来更新一下。

杂谈

Google IO

今年的Google IO,已经过去了几天才想起来看看网上的新闻和官方的视频,因为懒和忙,也没有及时的写篇文章,想要关注信息看官网,或者中文世界已经都有很多文章了,因此我这里简单说说吧。由于Android新版本的发布节奏改变,Android方面的更新很早之前其实就已经发布了。在这次发布会上,新版本的介绍主要大的可能就是系统UI的更新,这个可以说跟小米的HyperOS更像了,而新发布的Live Notification也与国产厂商已经上线一年的新的实时通知很像,爸爸抄儿子,也是倒反天罡了。

除此之外Android上面的介绍,首先还是Compse,Compose功能和性能都有极大的提高,同时对于KMP的支持也更好,使用Kotlin做全平台开发指日可待。

整个IO,AI仍然是全场的热点,Google的模型更棒了,集成的工具更多了,google的智能眼睛又重启了。但问题是,身在中国的我们还是很难用到,各种墙以及谷歌的限制,只有尊贵的美国人才能体验到完整功能。

关于Google IO的详细可以去官网查看。

假药

因为之前在B站看到一个UP主的推荐,于是想要去买一个口腔清新喷剂,这个喷剂看许可属于一类医疗器械,因此我这里标题的是假药。这个喷剂在淘宝上是又官方旗舰店售卖的,但是本着货比三家的原则,在京东也搜了搜,结果京东的不少非自营商家的价格还挺便宜的,于是就选择了一家购买了一瓶。

无奖竞猜,上图中哪个是假货。

收到货后,发现包装的印刷质量比较差,怀疑是假货,于是问商家,告诉我请放心使用。随后又去淘宝旗舰店看了看,发现两个包装是有一些差异的,淘宝买家分享的评价里面的图明显比我这个印刷质量好多了。我购买的这个上面有个正品标识的二维码,扫码查询之后告诉我是正品,然而这个网址是一个不知名的网址,并且下面还有个给商家注册的入口。同时我又去问了淘宝店的客服,确认了他们没有这个正品验证的网址,他们的包装开口方向也与我购买的不同,因此坐实了这是假货。

而商家仍然不认同这是假货,给了我两个回复,一,商品名称和正品是一样的,二,他这个是新包装。只好选择京东投诉,上传了淘宝正品图和假货图,然后经过跟京东反复掰扯了两个星期,京东仍然回复卖家不承认是假货,只答应京东方面赔偿我商品价格,看起来是对于商家无任何处理。

同时我也在12315提交了投诉和举报,因为选择了绿色通道,结果是截止目前为止,没有任何回应。而我也没有精力和时间去找检测机构进行检测,这件事情也只能到此作罢。

在这里建议大家,京东平台而第三方商家购物需要特别慎重。特别是我这种,商家是药店,还销售假货,简直是害人。

月初是五一,回了趟老家,家里大旱,还在种地的很多人在抽水浇地。正值杨树飘絮,漫天飞舞的杨絮,配上干燥的天气让人相当不舒服。

以上为村子里颓败的房屋。

老家坐落在皖北平原,没山没水,也没啥历史名胜,为了发展旅游。县城搞了个遗址公园,种了许多的樱花树,清明节期间樱花盛放,此时已经全部凋谢了。因为小长假的原因,遗址公园还是围起来收费,搞了些表演,也算是吸引一些周边的人民。

附近还搞了一个县博物馆,主要展示了一些本地的历史文化和文物,虽然说不是什么珍贵文物,但是我觉得对于当地的中小学生来说还是挺好的,想我大学之前没出过本县,没见过啥博物馆,现在的小孩有这些东西可以看看。只可惜农村的父母可能也没有这个意识带小孩看这些。

本月的徒步,先是去了一次无锡惠山,早上早起出发,路上不堵,10点多就开到了惠山脚下。跟着两步路的路线走了个爱心线,这条路线比较简单小朋友跟着她的同学一起在前面走的很快,下午三点半就走完了全程。

就在上周去了苏州大阳山,出发前几天都在下雨,本以为山上会很泥泞,但是实际上没有。因为前几天下雨的原因,这天过来徒步登山的人并不多。我们开车过来,把车停到了苏州乐园的停车场,这里每小时4元,20元封顶,然而往前走的时候发现路边停车只要7块钱每次,因为懒还是没有挪车。之后遇到第二个问题,因为是按照别人路线的反穿走的,在一个门那里被保安拦住不让我们进去,说要去正门买票进去。我们只好往回走,从正门附近的一个小豁口钻进去,并且爬了一段比较难走的路才拐到我们原本要走的路线上。而实际上,大多数人走的金蛇线入口,也就是我们终点的地方这里,上山也是不要门票的。大阳山比惠山难度稍高,但是因为我们先走了2公里的公路,总里程10公里,大家走下来感觉也都不算太累。

五月份还是枇杷成熟的季节,家里人也喜欢吃,相比于苏州东山80一斤的白玉枇杷,公园里免费的枇杷虽然酸,但是也不错了。于是我们选择在周末开着车到广富林郊野公园露营摘枇杷,枇杷树有不少棵,也有不少人带着工具过来摘,我们还是收获满满。

这个月主要看了两本书,首先是《巴菲特之道》这本书,想到要看这本是因为伯克希尔开了年会,巴菲特宣布退休,这本书介绍了巴菲特的部分经历,他的投资原则,我也专门写了文章,感兴趣可以看看。

另外还看了一本《寻路中国》,微信读书推荐的,作者是之前在中国工作过十几年的一位美国记者何伟,书中讲述他开车在中国旅行和与人交流的故事,时间大概是在2002年到2007年之间的事情,他见到了中国的工业化进程,农村的变化等等,书的内容比较吸引人,只花了不到两个星期就看完了。

电视剧也看了两部,首先是美剧《最后生还者》第二季上线了,也就花时间先把第一季看了看,目前在看第二季了,个人感觉第二季没有第一季好看,第一季男女主一起找实验室相当于是主线,每集又有独立的剧情。而到了第二季,感觉就是你找我复仇,我找你复仇,然后有出来一大堆新的组织,越往后越有点看不动。 另外还看了去年大火的剧集《我的阿勒泰》,整部剧只有八集,第一季作家告诉女主要“去生活,去爱,去受伤”,之后就是女主在草原生活的故事,最后结尾也算是happy end,不过我感觉可能留一点遗憾或许会更好。另外就是这部剧的画面很好看,草原加雪山真的很美,想要去新疆看看,可惜是很难请个长假。

尾声

五月工作上挺忙的,但是并没有啥产出,并且被谷歌爸爸卡着公司的产品也没法发布,希望六月能够顺利一点。至于我上面的胡言乱语,你就当耳旁风😄。

看完评论一下吧

优化Typecho的思路

作者老孙
2025年5月30日 19:08
AI摘要:文章介绍了优化Typecho博客性能的多种方法:1)服务器端使用Nginx开启gzip压缩和浏览器缓存;2)图片优化采用原生JS实现懒加载;3)通过PHP压缩HTML输出;4)设置浏览器缓存和页面缓存策略,包括简单的PHP缓存实现。这些技术可显著提升网站加载速度和性能。

服务器端

使用Nginx作为web服务端可以使用以下开启

# 启用 gzip 压缩
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_types
  application/javascript
  application/json
  application/xml
  text/css
  text/plain
  text/xml;

# 浏览器缓存控制
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, no-transform";
}

图片优化

使用原生js实现懒加载

document.addEventListener('DOMContentLoaded', function() {
    // 获取所有图片(如果主题默认输出 src,可以动态替换为 data-src)
    const images = document.querySelectorAll('img[src]:not([data-src])');
    
    // 防止重复处理
    images.forEach(img => {
        if (!img.getAttribute('data-src')) {
            img.setAttribute('data-src', img.src); // 把 src 存到 data-src
            img.removeAttribute('src'); // 移除 src,避免立即加载
        }
    });

    // 懒加载逻辑
    const lazyLoad = (targets) => {
        if ('IntersectionObserver' in window) {
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const img = entry.target;
                        img.src = img.dataset.src;
                        observer.unobserve(img); // 加载后停止观察
                    }
                });
            });
            targets.forEach(img => observer.observe(img));
        } else {
            // 兼容旧浏览器(滚动监听)
            const checkImages = () => {
                targets.forEach(img => {
                    const rect = img.getBoundingClientRect();
                    if (rect.top < window.innerHeight + 100) { // 提前 100px 加载
                        img.src = img.dataset.src;
                    }
                });
            };
            window.addEventListener('scroll', checkImages);
            checkImages(); // 初始检查
        }
    };

    // 对所有 data-src 图片应用懒加载
    lazyLoad(document.querySelectorAll('img[data-src]'));
});

资源合并与压缩

压缩HTML输出

// 在主题的 functions.php 中
function compress_html($html) {
    $placeholders = [];
    $i = 0;

    // 匹配 <pre> 和 <code> 区块,替换为唯一占位符
    $html = preg_replace_callback(
        '/<(pre|code)[^>]*>.*?<\/\1>/is',
        function ($matches) use (&$placeholders, &$i) {
            $key = "###HTML_COMPRESS_IGNORE_" . $i . "###";
            $placeholders[$key] = $matches[0];
            $i++;
            return $key;
        },
        $html
    );

    // 正常压缩
    $search = array(
        '/\>[^\S ]+/s',
        '/[^\S ]+\</s',
        '/(\s)+/s'
    );
    $replace = array(
        '>',
        '<',
        '\\1'
    );
    $html = preg_replace($search, $replace, $html);

    // 恢复占位符
    if (!empty($placeholders)) {
        $html = str_replace(array_keys($placeholders), array_values($placeholders), $html);
    }

    return $html;
}
//在head.php中开启
<?php ob_start("compress_html"); ?>
//在footer.php中结束
<?php ob_end_flush(); ?>

缓存策略

浏览器缓存

<meta http-equiv="Cache-Control" content="max-age=86400" />

header.php中加入

页面缓存

在主题的 functions.php 中添加简单的页面缓存

function page_cache() {
    // 不缓存后台、登录页和提交操作
    if (defined('__TYPECHO_ADMIN__') || $_SERVER['REQUEST_METHOD'] != 'GET') {
        return;
    }
    
    $cache_dir = __TYPECHO_ROOT_DIR__ . '/cache';
    if (!is_dir($cache_dir)) {
        mkdir($cache_dir, 0755, true);
    }
    
    $url_hash = md5($_SERVER['REQUEST_URI']);
    $cache_file = $cache_dir . '/' . $url_hash . '.html';
    
    // 缓存过期时间(秒)
    $cache_time = 3600; // 1小时
    
    // 如果缓存文件存在且未过期,则直接输出
    if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) {
        echo file_get_contents($cache_file);
        exit;
    }
    
    // 否则开始输出缓冲
    ob_start();
}

function save_cache() {
    // 同样跳过后台等页面
    if (defined('__TYPECHO_ADMIN__') || $_SERVER['REQUEST_METHOD'] != 'GET') {
        return;
    }
    
    $cache_dir = __TYPECHO_ROOT_DIR__ . '/cache';
    $url_hash = md5($_SERVER['REQUEST_URI']);
    $cache_file = $cache_dir . '/' . $url_hash . '.html';
    
    // 保存缓冲内容到文件
    $content = ob_get_contents();
    file_put_contents($cache_file, $content);
}
// 在页面开始处调用
page_cache();
// 在页面结束前调用
register_shutdown_function('save_cache');

端午插柳

2025年5月30日 13:31

这么多年了每到端午还记得小时候满村子的爬树去折柳枝,小小的身子拖着比身子大一倍的柳枝往家里拖,之后就会插在家里的大大小小的门上。想想最近一次也是折柳枝大概也是在11年前了,自从离开家就再也没有在节假日回过家,回家也是只有在每年过年的时候。

今年本来是不想插柳枝的,早上出门看到了别人家门口的艾草。就想到了家里,想着是不是也可以挂一点,但是挂什么又不知道了。因为在南方是“清明插柳,端午挂艾”。
这一南一北的习俗真实有意思,但是查了一下好像就只有甘肃某些地方是有这样的习俗的。没想到我们也有“少数民族”特色。
总体来说有两个版本:
1.插柳习俗可能与寒食节纪念介子推的传说有关(寒食节插柳)。由于甘肃历史上多民族交融,寒食与端午的习俗可能有所混合。
2.清代左宗棠在西北戍边时曾命军队广植柳树(“左公柳”),改善了当地环境。后人可能将柳树视为“守护之树”,并融入节日习俗。

入乡随俗,今天回去看看那可不可以采到艾草,柳枝学校有。这样就可以两种都有啦!

夸克网盘免第三方工具下载提速方案

作者刘郎
2025年5月30日 10:36

之前被夸克网盘的限速困扰了许久(非会员白嫖党),和目前大多数主流的网盘一样,非会员用户下载体验极差。以前用油猴脚本还要逐个文件下载,操作太繁琐。

前段时间偶然发现了一个不使用第三方工具就能提速方法,实测平均速度4-5M/s,虽没能达到满速的效果,但对于日常下载也够用了。现在我就把这个亲测有效的方法分享给大家,也给自己留个备份,有需要的博友赶紧去试试吧!

效果展示

刘郎阁

未提速时的效果

刘郎阁

提速后的效果,快了10倍左右

具体操作

1.在PC桌面端,点击快传,点击发送网盘文件:

刘郎阁

2.然后选中要下载的文件或者文件夹,确认添加:

刘郎阁

3.最后点击下载到本地

刘郎阁

这时候你会发现下载速度比之前的百十k,已上升到了4~5M/s左右

该方法从2024年11月15日使用到现在,任然有效,且行且珍惜,收藏起来,快去试试吧!

HomeAssistant如何自定义侧边栏标题,修改标题文本教程

作者张洪Heo
2025年5月29日 15:35
这篇文章介绍了如何在HomeAssistant中自定义侧边栏标题。作者指出该操作虽简单但缺乏系统教程,主要通过HACS社区商店安装Custom Sidebar集成,搜索并安装相关模块(如RainXML),编辑配置文件并添加指定模块路径,随后在/config/www/目录创建sidebar-config.yaml文件设置标题参数。完成配置后重启HomeAssistant服务即可生效。文中还提供了详细的配置参数示例,帮助用户顺利操作。

五岳独尊——泰安&amp;泰山二日游

2025年5月28日 10:10

紧接着之前的北京行,从北京坐车到泰安,准备第二天爬泰山。但是北京走的确实很累,于是打算多休整一天,隔天再去爬泰山,因此是个二日游。

下了火车,就能看到泰安新建的地标建筑——财源门,也是这次计划住的地方。本来想住在如家,但是一犹豫如家一日涨了50,只能忍痛了。

修整体力,体验泰安

因为修整的缘故,上午基本没干啥,等到中午的时候才想着出门去。因为计划直接爬泰山,没怎么做攻略。查了一下到处都是炒鸡,于是想着尝一尝特色吧。中午在点评上找了一家不错的店,79.9要了四个菜:泰山干炒鸡、小酥肉、泰山熏豆干、泰山小豆腐,泰山干炒鸡就是油炸辣子鸡,小酥肉就是炸鸡柳,熏豆干就是拌豆干,小豆腐就是小白菜鸡蛋豆腐汤qaq。菜品都很一般,但是老板人很好,店面也不错,尤其是好评送的山楂汁真的好喝。

吃完了午饭就去逛了逛菜市场,听说泰山是山东煎饼的发扬地,但是菜市场的煎饼六块钱一斤,太贵了。然后就又回去休息了。本打算下午去岱庙,想了想不如第二天下山之后直接去岱庙,然后看看老县衙大院,正好都是顺路。休息到了晚上,准备出门逛逛小吃街买买吃的,也准备一下第二天爬泰山的补给。因此出发去了北新夜市。

到的时候天还没黑,小吃车的摊位都还没出来全,就和我妈在路口坐了一会。夜市很热闹,不过大部分都是小吃。也有不少套圈、打气球等游戏。记得气枪是10块钱50发,要是体验的话很划算,但是想了想算了哈哈哈哈哈。买了不少小吃,鸭货啊,炸场啊,土豆泥拌饭啊好多哈哈哈。

逛完小吃街之后回去才想起来忘记买明天的午饭了qaq,好在有外卖,买了一些汉堡,足够了ω

爬上泰山,五岳独尊

第二天一大早,就坐公交到了天外村,坐大巴进山也很顺利,很快就到了中天门。来到泰山,一定要爬从中天门到南天门这段路,才会有会当凌绝顶,一览众山小的感觉。但是真的应了那句话,十八盘会教训每一个嘴硬的人,看似近在咫尺,却爬了一段路以为登顶了,结果上去一个拐角又是很长的楼梯。即使最后终于到了南天门了,那也是泰山的大门,距离山顶还有好远呢。

不过既然到了南天门,登上玉皇顶那就是板上钉钉的事了,但是这泰山上的人也太多了qaq,尤其是五岳独尊石,想拍一张真的全是人。

本来计划坐缆车下到中天门,然后下山走到红门,再去岱庙和老县衙大院的,但是我妈说她腿疼,也只好做大巴又从天外村出来,回到旅店休息了。其实时间也不早了,要是走下山估计也是天黑了,这样也好。岱庙想想也就是个庙,应该大差不差了。晚上自己出来,吃一些好吃的,比如这个驴杂汤+两个驴肉火烧的15元套餐,还有这个24.5元30串羊肉串外加茶水不限量的套餐。真的是太美了。吃两个驴肉火烧吃饱之后,来到烧烤摊,坐在室外的折叠桌旁,杯中倒了一杯茶水,吹着夏季的暖风,舒缓了一天的疲惫。恰到好处的火候让肉串香气四溢,身边的音响放着抒情的音乐,结束了在泰安的最后一个晚上。

ps后续:吃完烤串之后,想着前一晚夜市买的卤鸭翅还没吃,于是回去路上买了蜜雪冰城,回去躺在床上开着大屏幕边看电影边啃鸭翅哈哈哈哈哈

Typecho文章置顶(非插件)

作者老孙
2025年5月26日 10:12
AI摘要:文章介绍了Typecho博客系统实现文章置顶功能的完整方案,包括在index.php中插入PHP代码处理置顶逻辑,在functions.php添加主题设置项,以及在文章列表模板中添加置顶标识显示代码。该方法解决了传统置顶方式导致文章总数减少的问题,通过单独查询置顶文章并调整分页逻辑来实现正确显示。

置顶

最近才发现原来用的文章置顶还是有些问题的,置顶N篇文章,翻页时文章列表中文章还是维持原有的翻页逻辑,那么列表中文章的总数中就会减少N篇文章.

实现

正常逻辑应该是从文章列表中查找需要置顶的文章展示在首页列表,把原有的文章列表向后压.翻页时会筛选已经置顶的文章.不再重复显示.

步骤

  1. index.php中插入代码

    <?php  
    $sticky = $this->options->sticky;
    $db = Typecho_Db::get();
    $pageSize = $this->options->pageSize;
    if ($sticky && !empty(trim($sticky))) {
     $sticky_cids = array_filter(explode('|', $sticky));
     if (!empty($sticky_cids)) {
         $sticky_html = " <span class='sticky--post'><svg xmlns='http://www.w3.org/2000/svg' width='16px' height='16px' fill='none' viewBox='0 0 24 24' class='bk'>
    <path fill='#242424' fill-rule='evenodd' d='M12.333 16.993a7.4 7.4 0 0 1-1.686-.12 7.25 7.25 0 1 1 8.047-4.334v.001a7.2 7.2 0 0 1-.632 1.188 7.26 7.26 0 0 1-4.708 3.146l-.07.013q-.466.083-.951.105m.356.979a8.4 8.4 0 0 1-1.377 0l-2.075 5.7a.375.375 0 0 1-.625.13l-2.465-2.604-3.563.41a.375.375 0 0 1-.395-.501l2.645-7.267a8.25 8.25 0 1 1 14.333 0l2.645 7.267a.375.375 0 0 1-.396.5l-3.562-.41-2.465 2.604a.375.375 0 0 1-.625-.13zm5.786-3.109a8.25 8.25 0 0 1-4.775 2.962l1.658 4.554 1.77-1.87.344-.362.496.057 2.558.294zm-12.95 0L3.476 20.5l2.557-.295.497-.057.344.363 1.77 1.87 1.658-4.555a8.25 8.25 0 0 1-4.775-2.961' clip-rule='evenodd'></path></svg></span> ";
         
         // 保存原始对象状态
         $originalRows = $this->row;
         $originalStack = $this->stack;
         $originalLength = $this->length;
         $totalOriginal = $this->getTotal();
         
         // 重置当前对象状态
         $this->row = [];
         $this->stack = [];
         $this->length = 0;
         
         if (isset($this->currentPage) && $this->currentPage == 1) {
             // 查询置顶文章
             $selectSticky = $this->select()->where('type = ?', 'post');
             foreach ($sticky_cids as $i => $cid) {
                 if ($i == 0) 
                     $selectSticky->where('cid = ?', $cid);
                 else 
                     $selectSticky->orWhere('cid = ?', $cid);
             }
             $stickyPosts = $db->fetchAll($selectSticky);
             
             // 添加置顶文章到结果集
             foreach ($stickyPosts as &$stickyPost) {
                 $stickyPost['isSticky'] = true;
                 $stickyPost['stickyHtml'] = $sticky_html;
                 $this->push($stickyPost);
             }
             
             // 计算当前页应显示的普通文章数量
             $standardPageSize = $pageSize - count($stickyPosts);
             
             // 确保第一页不会显示太多文章
             if ($standardPageSize <= 0) {
                 $standardPageSize = 0; // 如果置顶文章已经填满或超过一页,则不显示普通文章
             }
         } else {
             // 非第一页显示正常数量的文章
             $standardPageSize = $pageSize;
         }
         
         // 查询普通文章
         if ($this->currentPage == 1) {
             // 第一页需要排除置顶文章并限制数量
             $selectNormal = $this->select()
                 ->where('type = ?', 'post')
                 ->where('status = ?', 'publish')
                 ->where('created < ?', time());
                 
             // 排除所有置顶文章
             foreach ($sticky_cids as $cid) {
                 $selectNormal->where('table.contents.cid != ?', $cid);
             }
             
             $selectNormal->order('created', Typecho_Db::SORT_DESC)
                 ->limit($standardPageSize)
                 ->offset(0);
         } else {
             // 非第一页的查询
             // 计算正确的偏移量:(当前页码-1) * 每页数量 - 置顶文章数
             // 这样可以确保不会漏掉文章
             $offset = ($this->currentPage - 1) * $pageSize - count($sticky_cids);
             $offset = max($offset, 0); // 确保偏移量不为负
             
             $selectNormal = $this->select()
                 ->where('type = ?', 'post')
                 ->where('status = ?', 'publish')
                 ->where('created < ?', time());
                 
             // 排除所有置顶文章
             foreach ($sticky_cids as $cid) {
                 $selectNormal->where('table.contents.cid != ?', $cid);
             }
             
             $selectNormal->order('created', Typecho_Db::SORT_DESC)
                 ->limit($pageSize)
                 ->offset($offset);
         }
     } else {
         // 没有有效的置顶文章ID,正常查询
         $selectNormal = $this->select()
             ->where('type = ?', 'post')
             ->where('status = ?', 'publish')
             ->where('created < ?', time())
             ->order('created', Typecho_Db::SORT_DESC)
             ->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
     }
    } else {
     // 没有设置置顶文章,正常查询
     $selectNormal = $this->select()
         ->where('type = ?', 'post')
         ->where('status = ?', 'publish')
         ->where('created < ?', time())
         ->order('created', Typecho_Db::SORT_DESC)
         ->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
    }
    
    // 添加私有文章查询条件
    if ($this->user->hasLogin()) {
     $uid = $this->user->uid;
     if ($uid) {
         $selectNormal->orWhere('authorId = ? AND status = ?', $uid, 'private');
     }
    }
    
    // 获取普通文章
    $normalPosts = $db->fetchAll($selectNormal);
    
    // 如果没有置顶文章或在前面的代码中没有重置对象状态,则在这里重置
    if (empty($sticky) || empty(trim($sticky)) || empty($sticky_cids)) {
     $this->row = [];
     $this->stack = [];
     $this->length = 0;
    }
    
    // 将普通文章添加到结果集
    foreach ($normalPosts as $normalPost) {
     $this->push($normalPost);
    }
    ?>
  2. 在主题的functions.php文件中查找

    function themeConfig($form) {

    之后插入代码,会增加一个主题设置项

     $sticky = new Typecho_Widget_Helper_Form_Element_Text('sticky', NULL, NULL, _t('置顶文章cid'), _t('多篇文章以`|`符号隔开'), _t('展示需要置顶的文章。'));
     $form->addInput($sticky);
  3. 在文章列表中合适的地方插入

    <?php if (isset($this->isSticky) && $this->isSticky): ?>
    <?php echo $this->stickyHtml; ?>
    <?php endif; ?>

红墙深几许,一梦到明清——北京&amp;故宫一日游

2025年5月26日 10:15

早上九点的火车到达北京丰台站,晚上十点半的火车驶离北京站,计划了一个白天的北京入夏一日游。

到达北京丰台站,因为没吃早饭,正好在这附近吃一吃。感谢地道的老北京“小羊”前辈,为我这次北京行省了不少攻略的时间,还吃到了许多当地物美价廉的特色,真的超级超级好。这次下火车吃的《尹记门钉肉饼》就是推荐的连锁店。不过当时问了一下门钉肉饼只有在十点半之后才开始开卖,没办法因为时间紧迫吃不到了。不过也没关系,要了一份12元的羊杂汤+小饼,也要到了传说中的豆汁,3元一大碗,可惜就喝了五口。羊杂汤很咸,豆汁很酸,就是字面意思上的酸臭,喝起来也还行吧,确实不怎么好喝。要了一个牛肉大懒龙,就剩最后一个了哈哈哈哈哈。

吃过早饭,计划去看一眼天安门之后进故宫博物院,因此先去北京站存个背包。老板人很好,一眼就看出来我是大连的hhhhh,也给我优惠了几块钱。远远的能看到北京站的去看天安门,因为没有预约,被告知好像没法看到天安门???想不到看个天安门还预约了,可能是人真的多吧。折腾了半天去到了侧门进故宫了。

故宫确实大气,人也是真多,但是现在故宫正在修缮,连宫殿的大门都不能靠近,远处的门口挤了一堆人在那拍照。走着走着就很累,因为都忘记去看清明上河图了。网上看的攻略什么盖章集邮全都直接错过了,匆匆忙忙的就逛完御花园从北门上了景山公园。

景山公园的自动贩卖机很方便也很便宜,里面还有怡人的鲜花,安静的小路,而且还有能俯视故宫的美景,真是个好地方。

因为逛完很累了而且已经下午了,想着去北海公园逛逛但是貌似走不动了,于是直接下一个计划——去鼓楼的《姚记炒肝店》吃炒肝和卤煮!

卤煮确实好吃,炒肝也好,还要了炸灌肠,炸灌肠沾上蒜水还真的是灌肠的香味,好吃!

然后去哪呢?“小羊”前辈说去看看后海吧。我看地图附近就是烟袋斜街和南锣鼓巷,烟袋斜街确实不错,就是开始下起了雨。走过银锭桥发现越走越远,就计划回逛南锣鼓巷。可悲的是到了南锣鼓巷雨下大了,而且里面的东西是真的贵,也不好逛,只能草草结束了行程。

天色渐暗,雨还很大,不知道去哪里好了。想起之前看到过有个不错的小景点,在王府井商场的地下有个《和平菓局》,有许多不错的场景可以打卡还在室内,于是就最后选择去那边逛逛结束这一天的行程了。王府井大街很宽,因为并没有做攻略,就知道有个古老的天主教堂,冶可以去打打卡。

休息片刻,之后取回了包,去北京站候车厅休息休息慢慢等车了。

zblog列表页面标题及文章数量

2025年5月25日 22:31

主要用户各种列表页面显示 xx分类/标签下有xx篇文章

{if $type == 'index'}
<h2>最新文章</h2>
{else}
<h2>
{if $type == 'category'}
找到<span>{$category.Count}</span>篇与
{elseif  $type == 'tag'}
找到<span>{$tag.Count}</span>篇与
{elseif  $type == 'author'}
找到<span>{$author.Articles}</span>篇与
{/if}
<span>{$title}</span> 相关的结果</h2>
{/if}

如有更好的方式欢迎评论区补充哈!

zblog判断用户是否登录

2025年5月25日 20:56

Z-blogPHP1.7.4判断用户是否登录

{if $user.ID > 0}
    <!-- 判断用户ID是否大于0 -->
    欢迎你:<a href="{$host}zb_system/cmd.php?act=login" target="_blank">{$zbp->user->StaticName}</a>
    <!-- 用户登录后显示的内容 -->
{else}
    <a href="{$host}zb_system/cmd.php?act=login" target="_blank">登录</a>
    <!-- 用户未登录显示的内容 -->
{/if}

给文章引入联邦宇宙嘟文互动记录

2025年5月24日 17:42

Photo by Pankaj Patel / Unsplash

去年还是前年就在思考如何在 Ghost 中集成 Activitypub 的互动,还找了一个国内博客大佬的实现 ActivityPub 协议的简单实现 - Lawrence Li ,不过这位大佬的方案是完全自己实现协议部分,对我来说有点太复杂了。

后来Ghost官方也发布了一个Ghost和Activitypub的继承方案,不过我看了下项目的Docker-compose文件,感觉太臃肿了(毕竟是商业化产品,性能各方面都要考虑到)。

而且开发一年多了,现在也只在官方付费服务里Beta,所以现在兴致缺缺。

因为最近用Cloudflare Worker实现了很多有意思的玩意,

所已,今天忽然灵光一闪,想到了一个非常有意思的点子,通过和AI的几轮互动,感觉应该能完整实现大佬博客里的那种效果。

其实原理无非就是利用Cloudflare Worker和KV功能,对文章ID和嘟文ID进行储存,在页面展示时再去请求数据进行展示,整个逻辑大概如下:

文章和嘟文同步

  1. Worker定时请求Ghost博客中最新一篇的数据(我这边是用Ghost的唯一文章ID做Key,你的博客系统没有API的可以请求RSS,但是ID必须是唯一的,可以自己截取slug出来应该也是可行的)。
  2. 拿到Key后在KV中进行查找,如果录入过就跳过。
  3. 没录入就拿文章的数据根据长毛象或GTS的API要求组装嘟文进行发布。
  4. 获取到嘟文唯一ID后和文章ID一起存入KV。

嘟文数据获取

  1. 博客文章详情页面加载完后通过文章ID请求Worker。
  2. Worker拿到ID去KV中查找嘟文ID。
  3. 找到话通过嘟文ID去长毛象或者GTS获取嘟文互动数据。
  4. 进行展示。
💡
经蜗牛大佬提醒,根据嘟文的原始可访问性做了控制。
1. 只显示public评论。
2. 不显示敏感(sensitive )内容。
3. 不显示locked用户点赞。

实践

进入Cloudflare Worker直接新建一个Worker,模板选Hello World,然后下面代码覆盖原有代码,我这边Ghost获取文章的部分你们用AI改成获取RSS,并截取文章slug作为文章ID。

// 配置常量
const GTS_INSTANCE = "https://social.gts.com";
const GTS_TOKEN = "ZTU5YTZLZMQTNWRJFSAFAXG3NDQ3MWQZOWRK";
const CACHE_TTL = 600; // 互动数据缓存时间(秒)
const BLOG_URL = "https://blog.com"; // Ghost博客地址
const BLOG_API_KEY = "78eb22fbf6260dcc3a1de7cf82"; // Ghost Admin API Key

// 在 Worker 代码开头添加 CORS 处理函数
const handleCORS = (response, origin) => {
  const headers = new Headers(response.headers);
  headers.set('Access-Control-Allow-Origin', origin || '*');
  headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  return new Response(response.body, {
    status: response.status,
    headers
  });
};

export default {
  async fetch(request, env) {
    // 处理预检请求 (OPTIONS)
    if (request.method === 'OPTIONS') {
      return handleCORS(new Response(null), request.headers.get('Origin'));
    }

    const url = new URL(request.url);
    const path = url.pathname;

    // 处理定时触发的自动发布
    if (path === '/api/sync') {
      return handleAutoPublish(env);
    }

    // 提供互动数据API
    if (path === '/api/interactions' && request.method === 'GET') {
      return getInteractions(url.searchParams, env);
    }

    return new Response('Not Found', { status: 404 });
  },

  // 添加定时触发器配置
  async scheduled(event, env, ctx) {
    ctx.waitUntil(handleAutoPublish(env));
  }
};

// 自动发布最新文章
async function handleAutoPublish(env) {
  try {
    // 从Ghost获取最新文章
    const postsResp = await fetch(`${BLOG_URL}/ghost/api/content/posts/?limit=1&order=published_at%20desc&key=${BLOG_API_KEY}`, {
      headers: {
        'Accept-Version': 'v5.0',
        'Content-Type': 'application/json'
      }
    });

    if (!postsResp.ok) {
      throw new Error('Failed to fetch posts from Ghost');
    }

    const postsData = await postsResp.json();
    const latestPost = postsData.posts[0];

    if (!latestPost) {
      return new Response('No posts found', { status: 200 });
    }

    // 检查是否已经发布过
    const existingMapping = await env.BLOG_TOOT_MAPPING.get(`post:${latestPost.id}`);
    if (existingMapping) {
      return new Response('Post already published', { status: 200 });
    }

    // 发布到GoToSocial
    const tootContent = `${latestPost.title}\n${BLOG_URL+'/'+latestPost.slug}\n\nfrom 1900's Blog.(auto sync)\n\n#博客`;
    
    const tootResp = await fetch(`${GTS_INSTANCE}/api/v1/statuses`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${GTS_TOKEN}`,
        'Content-Type': 'application/json',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
      },
      body: JSON.stringify({
        status: tootContent,
        visibility: "public"
      })
    });

    const tootData = await tootResp.json();

    // 存储映射关系到KV
    await env.BLOG_TOOT_MAPPING.put(
      `post:${latestPost.id}`,
      JSON.stringify({
        toot_id: tootData.id,
        toot_uri: tootData.uri,
        created_at: Date.now()
      })
    );

    return new Response('Auto publish success', { status: 200 });
  } catch (err) {
    return new Response(err.message, { status: 500 });
  }
}

async function getInteractions(params, env) {
  const postId = params.get('post_id');
  if (!postId) return new Response('Missing post_id', { status: 400 });

  // 从KV获取Toot信息
  const tootData = await env.BLOG_TOOT_MAPPING.get(`post:${postId}`);
  if (!tootData) return new Response('Mapping not found', { status: 404 });

  const { toot_id } = JSON.parse(tootData);

  // 并发获取回复和点赞数据
  const [contextResp, favouritesResp] = await Promise.all([
    fetch(`${GTS_INSTANCE}/api/v1/statuses/${toot_id}/context`, {
      headers: { 
        'Authorization': `Bearer ${GTS_TOKEN}`,
        'CF-Cache-Tag': `context_${toot_id}`,
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
      },
      cf: { cacheTtl: CACHE_TTL }
    }),
    fetch(`${GTS_INSTANCE}/api/v1/statuses/${toot_id}/favourited_by`, {
      headers: { 
        'Authorization': `Bearer ${GTS_TOKEN}`,
        'CF-Cache-Tag': `favs_${toot_id}`,
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'
      },
      cf: { cacheTtl: CACHE_TTL }
    })
  ]);

  if (!contextResp.ok || !favouritesResp.ok) {
    return new Response('Failed to fetch interactions', { status: 502 });
  }

  // 处理数据
  const [contextData, favouritesData] = await Promise.all([
    contextResp.json(),
    favouritesResp.json()
  ]);

  // 格式化响应
  const formatted = {
    post_id: postId,
    toot_id: toot_id,
    replies: contextData.descendants.map(item => ({
      id: item.id,
      author: {
        name: item.account.display_name,
        avatar: item.account.avatar
      },
      content: item.content,
      created_at: item.created_at
    })),
    favourites: favouritesData.map(user => ({
      id: user.id,
      name: user.display_name,
      avatar: user.avatar,
      username: user.acct
    })),
    stats: {
      replies_count: contextData.descendants.length,
      favourites_count: favouritesData.length
    }
  };

  return new Response(JSON.stringify(formatted), {
    headers: { 
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*' 
    }
  });
}

Worker定时执行

进入Worker的设置页面,绑定KV命名空间和设置Cron执行间隔。

这里KV空间需要提前建好,路径为 储存和数据库 > KV > 创建 > 录入名称 BLOG_TOOT_MAPPING ,然后再去设置页面绑定。

前端渲染

有了API提供数据,前端只需要在页面加载时获取数据进行渲染即可,我这里做了简单的展示,带红心的头像是点赞用户,没带红心的是用户评论,鼠标悬浮在头像上即可展示。

目前暂时还没想好如何更好的实现,之后有想法了再进行完善。

我这边相关代码剥离到了一个单独的js文件里,原理是一样的,你也可以直接写在页面上。具体代码可以用AI帮你生成一个就行。

import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
import 'tippy.js/themes/light.css';

// 配置常量
const API_ENDPOINT = 'https://your.workers.dev/api/interactions';

// 主入口函数
export default async function initActivityPubInteractions() {
    try {
        const container = document.querySelector('#activitypub');
        if (!container) {
            console.error('未找到#activitypub元素');
            return;
        }

        const postId = container.dataset.postid;
        if (!postId) {
            console.error('缺少data-postid属性');
            return;
        }

        const data = await fetchInteractions(postId);
        renderAllInteractions(data, container);

        // 如果有互动数据则显示容器
        if (data.stats.replies_count > 0 || data.stats.favourites_count > 0) {
            container.style.display = 'block';
        }
    } catch (error) {
        console.error('加载互动数据失败:', error);
    }
}

// 获取互动数据
async function fetchInteractions(postId) {
    const response = await fetch(`${API_ENDPOINT}?post_id=${postId}`);
    if (!response.ok) throw new Error('API请求失败');
    return await response.json();
}
// 渲染所有互动(混合点赞和评论)
function renderAllInteractions(data, container) {
    const avatarList = container.querySelector('.discussion-avatar-list');
    if (!avatarList) return;

    avatarList.innerHTML = '';

    // 合并点赞和评论数据
    const allInteractions = [...data.favourites.map((user) => ({ ...user, type: 'like' })), ...data.replies.map((user) => ({ ...user, type: 'reply' }))];

    // 按时间排序(最新的在前)
    allInteractions.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

    allInteractions.forEach((user) => {
        const li = document.createElement('li');
        li.innerHTML = `
      <div class="comment-user-avatar ${user.type}">
        <img src="${user.avatar || user.author.avatar}" 
             alt="${user.name || user.username}" 
             class="avatar avatar-60 photo" 
             loading="lazy"
             data-user-id="${user.id}"
             data-type="${user.type}">
      </div>
    `;
        avatarList.appendChild(li);

        // 直接在这里初始化 Tippy
        const img = li.querySelector('img');
        if (user.type === 'reply') {
            // 评论工具提示
            tippy(img, {
                theme: 'light',
                allowHTML: true,
                interactive: true,
                maxWidth: 350,
                delay: [100, 0],
                content: '加载中...',
                onShow(instance) {
                    instance.setContent(user.content);
                }
            });
        } else {
            // 点赞工具提示
            tippy(img, {
                content: '💖',
                delay: [100, 0]
            });
        }
    });

    // 更新统计信息
}

HTML 代码部分

<!--- 其他代码 --->
<div class="social-interactions">
    <ol class="discussion-avatar-list"></ol>
</div>

<script>
    // 引入上面的函数文件
    import loadInteractions from '../utils/acitivitypub';
    // 适配Astro的PWA加载
    document.addEventListener('astro:page-load', () => {
        loadInteractions();
    });
</script>
<!--- 其他代码 --->

zblogPHP开启伪静态图文教程

2025年5月24日 12:52

在zblog后台插件管理里面找到【静态管理中心】插件,注意,这个插件是安装程序后自带的,默认是没有开启的,我们启动一下,然后点击扳手图标进入插件设置。

Snipaste_2025-05-24_12-47-07.webp

然后点击伪静态(高手也可以修改下默认伪静态下的网址规则,新手直接略过即可),然后滚动页面到最下面点击保存

Snipaste_2025-05-24_12-49-14.webp

此时页面会跳转伪静态规则页面

Snipaste_2025-05-24_12-49-28.webp

不同服务器环境规则写法不同,根据自己环境选择下,然后复制伪静态规则,填到自己网站控制面板伪静态规则里面保存即可

工作周年与AI焦虑:成长与挑战并存

作者Innei
2025年5月23日 23:06
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/notes/192

Folo 与我的一年

转眼间,这份工作又快到一年了。说起来,我毕业以来,经历了两次换工作,第一次是因为工作内容和压力的问题,第二次是因为裁员。不幸的是,这两次实际都没有满一年。

又是这里节骨眼上,恰好是一年,又开始有点焦虑了。焦虑的是,做的事是否达到预期,是否有成长,产品时候有前途等等。一年的时间说长不长说短也不短。

回顾过去的一年,我自认为还是能达到预期的吧。从我刚入职,从一个毛坯的产品开始做,重新设计组件 UI,重构底层,赋予一些有意思的交互设计以及功能,到 Web 端开放公测。不过只是三个月的时间。又是一个月完成了 Web 对 Mobile 的响应式改造。现如今 Mobile App 也已经上线很久。而这些在一年的时间中全部完成了。对于一个只有 4 个开发成员的项目来说,感觉已经很不错了。

希望越来越好吧。

AI 焦虑

回看今年,各路神仙打架。AI 发展越来越快。该说是好事还是坏事呢。如今写代码变得越来越不动脑子了,只是无脑敲着 Tab,出了问题也不知道。非常害怕这样依赖 AI 之后连很简单的逻辑都不会写了。AI 在进步而我在退步。

前段日子,也是借助 AI 完成了大部分的代码。虽然 AI 现在对 Swift 还是不太熟悉。但是循序渐进的引导最终还是能达到一个预期的结果。现在非常流行 Vibe Coding,即便是完全不会编程,只需要一个好点子,以及良好的文字表达,就能引导 AI 一步步做出你想要的结果。慢慢的代码越来越不值钱,最值钱的是好的点子。像我这种只会切图的低级程序员真的那一天就突然被淘汰了。现在的 AI 可比我会写多了。这种焦虑感越来越强。

确实会有一种很矛盾的感觉,一方面对自己所能开发的领域、边界有了更多信心,可以开发前端、iOS 甚至是各种之前并不了解的技术栈;而另一方面,对于自己离开 AI 后独立写代码的信心在显著下降,连带着自己独立思考的能力。 -- 周报 #95 - All AI 与 No AI

看完了?说点什么呢

Raphael AI图片生成器:释放创意,零门槛打造惊艳视觉盛宴

作者ysicing
2025年5月23日 19:53

在 AI 技术席卷全球的今天,图像生成工具正成为内容创作者、设计师和营销人员的得力助手。Raphael AI 作为全球首款完全免费无限制无需注册登录的 AI 图像生成器,以其强大的功能和便捷的操作,迅速在创意圈掀起热潮。

今天偶然看到的,有免费额度的 AI 图片生成器。适合生成配图或者插图,用来生成封面感觉还差点意思,和即梦效果差不多。

Raphael AI 是什么?

Raphael AI 是一款基于先进的 FLUX.1-Dev 模型打造的 AI 图像生成工具,用户只需输入文字描述(提示词),即可在几秒钟内生成高质量、多风格的图像。它的核心优势在于:

  • 完全免费, 零成本创作:无使用次数限制,无需注册或登录,打开网页即可创作
  • 多样化风格:支持写实摄影、动漫、油画、数字艺术等多种艺术风格
  • 快速生成:优化的推理管道确保几秒内出图,效率惊人
  • 隐私保护:零数据保留政策,提示词和生成图像不存储于服务器,保障用户隐私
  • 商业用途:生成图像无水印,可自由用于个人或商业项目
  • 一定质量保障:由 FLUX.1-Dev 模型驱动,提供具有卓越细节和艺术风格控制的照片级逼真图像;高级文本理解

对比国内大模型,我觉得是优势功能的都标注了。为啥说一定质量保障,因为高质量需要订阅才行 😄

测试

英文提示词

Humorous and funny style, preferably with a purple-haired Japanese anime girl holding a sign that says, "I need 66 more followers to reach 1000, please subscribe."

选择一个,其他都略微有些瑕疵

中文提示词

幽默搞笑风, 最好有个紫色头发日漫妹子举牌子,牌子上写着「我还有 66 个粉丝才满 1000,请求订阅」》

总结

官方建议: 请用英文输入提示词以获得最佳效果

经过实操,确实英文提示词要比中文好多了,可能我问的方式仅在 ChatGPT 上实验良好。

官方也提供了一些灵感示例提示词,点击相关图片就可以查看其他人的提示词

再次实操一下,发现生成 SB 比缘生更简单哈哈哈

生成: Cute chubby Pikachu on the grass, surrounded by small white flowers, with the two Chinese characters "SB" written on Pikachu's belly, soft pastel anime style

官网


Mac如何将喜欢的视频作为屏幕保护程序?自定义Mac视频屏保,最新系统教程

作者张洪Heo
2025年5月23日 14:26
这篇文章介绍了如何通过SaveHollywood工具在Mac系统上轻松设置自定义视频屏幕保护。文章提示用户,许多人可能没有意识到可以使用视频作为屏幕保护。通过简单的步骤,你可以从静态图片切换到动态视频,提升桌面的视觉效果。具体来说,读者需要安装SaveHollywood,将高清视频文件拖入工具目录中,并根据提示启用播放功能。文章还提示,操作必须严格遵循步骤,以免遇到“视频库为空”的错误。此外,工具的兼容性较高,且操作界面友好,适合大多数用户。文章鼓励读者按照指导操作,体验一次视觉效果的升级。

iPhone如何查看自己电话卡的上网优先级QCI?SIM卡的QCI查看教程

作者张洪Heo
2025年5月23日 12:32
这篇文章介绍了QCI( QUALITY CLASS IDENTIFIER)在移动网络通信中的作用及等级划分标准,明确了不同QCI等级对网络延迟、传输优先级和业务场景的具体影响。文章还详细讲解了如何通过iOS设备的反馈日志获取SIM卡的QCI等级信息,并强调了对QCI等级的理解对优化网络使用体验的重要性。此外,文章指出QCI等级越高,网络资源的优先级越高,但同时也要求预留更多的传输时延以确保高质量的应用体验。具体而言,QCI等级越高通常适用于对网络延迟较为敏感的业务,如实时视频通话和在线游戏,而QCI等级较低的网络资源可能更适合非实时性要求较高的任务。文中还通过个人使用体验进一步阐述了QCI等级在实际应用中的表现,并总结了不同QCI等级在移动互联网中的典型应用场景。

微软开源的更贴近Windows习惯的Linux命令行文本编辑器:edit

2025年5月23日 11:15

Photo by Christopher Gower / Unsplash

不知道是不是微软知道天下小白苦 Linux 的命令行编辑器久矣,最近开源了一款 windows 操作习惯的 shell 文本编辑器 edit。

GitHub - microsoft/edit: We all edit.
We all edit. Contribute to microsoft/edit development by creating an account on GitHub.
GitHubmicrosoft

支持多行选择、复制粘贴、鼠标控制等操作,操作习惯更偏向 widnows。

所以第一时间安装体验,感觉确实不错,写一篇安装教程。

下载使用你自己平台的二进制文件,Releases · microsoft/edit

curl -LO https://github.com/microsoft/edit/releases/download/v1.0.0/edit-1.0.0-x86_64-linux-gnu.xz

解压,重命名

unxz edit-1.0.0-x86_64-linux-gnu.xz
mv edit-1.0.0-x86_64-linux-gnu edit

给执行权限,并移动到 bin 目录

chmod +x edit
sudo mv edit /usr/local/bin/

测试是否安装成功

edit --version

常用快捷键

  • New File:Ctrl+N
  • Open File:Ctrl+0
  • Save:Ctrl+s
  • Close Editor:Ctrl+W
  • Exit:Ctrl+Q
  • Undo:Ctrl+Z
  • Redo:Ctrl+Y
  • Cut:Ctrl+x
  • Copy:Ctrl+c
  • Paste:Ctrl+V
  • Find:Ctrl+F
  • Replace:Ctrl+R

Google新AI产品无法使用解决指南

作者ysicing
2025年5月22日 18:21

今天看到这个消息推送,之前也遇到过这种情况,这个简单写下如何解决,当个精神美国佬

背景

受限于某些特殊环境或者边缘因素影响,国内是没法正常使用 Google AI 相关的产品的。(默认你已经可以访问 Google AI 等产品了)

接下来给的几种方式,都可以尝试

修改浏览器默认语言

这个跟之前 Github 默认禁止中文用户访问类似,检查你的请求头 accept-language 信息

示例修改 Edge 默认首选语言为英语即可,生效后随便访问一个网站开发者工具查看请求头或者支持切换语言的网站打开是否为英文

如果你的账号没问题了,通常打开那些网站就可以正常使用了。如果还不行,可能就是账号问题了

当然也会带来一些问题,你打开所有支持多语言的网站都会默认使用英文,影响也不大

申请谷歌账号地区修改

有可能,你很早之前就注册了谷歌账号,地区选的中国

查看账号绑定的地区 https://policies.google.com/terms

换区操作

找对入口,其实也很简单,通常我们都选择美区,精神美国佬,社会大学生, 部分 AI 功能暂时只对美区开发。

换区入口 https://policies.google.com/country-association-form

理由选择其他原因,听劝

好了,其他就是基本要求了,自行准备就行。

对了,这两个我港区的小号也是可以正常访问的。

PS: 大佬们都买了 Google One Ultra 没?


用Docker部署Navidrome音乐服务器

作者刘郎
2025年5月22日 14:25

Navidrome是一款基于Web的开源音乐服务器应用程序,可在Linux、Windows、macOS等多种操作系统上运行。它界面简洁直观,音乐管理功能强大,能自动扫描音乐库,获取歌曲信息和专辑封面,支持按艺术家、专辑、流派等分类浏览,还支持MP3、FLAC、AAC等多种音频格式。

它具有流媒体播放功能,可在不同设备上流畅播放音乐,支持Chromecast投屏,也有用户管理功能,可创建多个账户,便于多用户共享音乐库。其安装方式多样,可通过官网下载安装包、使用包管理工具或从源代码编译安装,配置简单,指定音乐库目录等参数后即可使用。

Navidrome适用于家庭音乐服务器、个人Nas音乐收藏管理以及小型办公环境等场景。

效果展示

PC端效果:

刘郎阁

移动端效果:

刘郎阁

刘郎阁

注意:移动端需搭配音流App或其他支持Navidrome的播放器使用。

搭建该项目的初衷

搭建Navidrome个人音乐服务器,是因为市面上的音乐服务器价格高昂,且需要持续开通会员才能享受高质量服务,一旦会员到期,音乐服务便无法继续使用。更令人无奈的是,即便开通会员,部分喜爱的音乐仍需额外付费购买,且这些音乐还存在平台使用限制,换平台播放又得重新掏钱。

与之相比,自行搭建Navidrome个人音乐服务器,一次搭建就能终身免费使用,音乐资源完全由自己掌控,无论是音乐源还是音乐质量都能随心决定。无需开通繁多的音乐会员,也不必额外花钱购买音乐,只要能连接上自建的音乐服务器,无论身处何地都能尽情享受专属的音乐盛宴。

准备工作

搭建Navidrome个人音乐服务器,我们需要3个关键组件:

1.Navidrome:用于管理自定义的音乐资源

2.Music-Tag-Web:用于刮削音乐资源

何为刮削?就是根据音乐资源的名称自动获取该音乐资源的一切信息:歌曲名称、作者、专辑、歌词、相关图片、发行时间、歌曲类型、歌曲大小……

3.音流App:音乐客户端

注意:只要是支持Navidrome服务的客户端都可以使用,不限于音流App

具体操作

搭建Navidrome

1.复制以下命令,粘贴到服务器终端直接运行即可:

docker run -d \
   --name navidrome \
   --restart=unless-stopped \
   --user $(id -u):$(id -g) \
   -v /自定义music文件存储路径/music:/music \
   -v /自定义data文件存储路径/data:/data \
   -p 4533:4533 \
   -e ND_LOGLEVEL=info \
   deluan/navidrome:latest

事项说明:

① "自定义music文件存储路径"和"自定义data文件存储路径"需授权Root权限

② music文件用于存放显示的音乐文件

③ data文件用于存放Navidrome音乐服务器上的数据

④ 在执行该命令前,最好先去服务器后台的"防火墙"开放4533端口

⑤ 在执行该命令前,最好先去服务器的服务商后台开放"防火墙"的4533端口

2.等待上面的命令执行完成后,用以下方式进行第一次登录访问:

服务器ip地址:4533

首次登录,需设置用户名、密码。设置好之后,就可以登陆Navidrome音乐服务器了:

刘郎阁

3.将语言设置为中文

登录Navidrome音乐服务器后,点击右上角头像,然后点击"Personal个性化设置"中的将语言,设置为中文即可。

4.反向代理

如果觉得使用 "服务器ip+端口" 的方式登陆Navidrome音乐服务器不方便,可以进行反向代理设置,将登陆方式改为域名登录。至于怎么设置,这里我就不多说了,可以参考之前的文章,或自行百度。

搭建Music-Tag-Web

1.复制以下命令,粘贴到服务器终端直接运行即可:

docker run -d -p 8001:8001 -v /自定义music文件存储路径/music:/app/media -v /自定义data文件存储路径:/app/data --restart=always xhongc/music_tag_web:latest

事项说明:

① "自定义music文件存储路径"和"自定义data文件存储路径"需授权Root权限

② music文件用于临时存放需要刮削音乐资源的文件

③ data文件用于存放Music-Tag-Web服务器上的数据

③ 在执行该命令前,最好先去服务器后台的"防火墙"开放8001端口

④ 在执行该命令前,最好先去服务器的服务商后台开放"防火墙"的8001端口

2.等待上面的命令执行完成后,用以下方式进行第一次登录访问:

服务器ip地址:8001

首次登录,默认的账号和密码都是admin:

刘郎阁

刘郎阁

该图左边为音乐资源选择区域,右面为选中的音乐资源刮削之后展示的效果区域。

3.Music-Tag-Web的使用

如何使用Music-Tag-Web音乐资源刮削工具,我这里就以BEYOND的"真的爱你"为例,来作为演示:

① 将歌曲源文件上传至Music-Tag-Web服务器的"/music"目录下,即前面我们自定义的路径:"自定义music文件存储路径"

② 在浏览器中登录"服务器ip地址:8001",我们点击音乐资源选择区域中的刷新按钮,即可看到我们上传的音乐名称:

刘郎阁

③ 在左边的音乐资源选择区域选中我们需要刮削的音乐资源,点击中间的第二个或者第三个按钮:手动刮削和自动刮削

④ 此时会弹出来一个"自动修改标签",根据我们自己的需求进行适当的调整,然后点击"保存"系统即可开始进行刮削处理

⑤ 等待一会儿,就可以看到刮削后的效果了,如果不满意刮削后的效果,我们还可以接着继续进行刮削,或者手动刮削处理也行:

刘郎阁

4.反向代理

如果觉得使用 "服务器ip+端口" 的方式登陆Music-Tag-Web音乐资源刮削工具不方便,可以进行反向代理设置,将登陆方式改为域名登录。

资源合并

如何将Music-Tag-Web音乐资源刮削处理后的音乐资源上传到Navidrome音乐服务器上呢?答案很简单,直接复制粘贴即可:

直接将Music-Tag-Web中music目录下的音乐文件,直接剪切或复制粘贴至Navidrome中的music目录下即可。

到这里,我们的Navidrome个人音乐服务器就算弄好了。但现在有个问题,使用网页端来听歌,总感觉有点别扭,而且还不太方便,那咋办?不慌,音流App就能解决这个问题。

音流

音流App是一款免费音乐源连接播放器,可兼容Navidrome、Subsonic等多种音乐服务,能连接自建服务器访问个人音乐库。它资源丰富,以高性能音频引擎实现高品质播放,自动同步歌词。支持离线下载,具备智能搜索、播放列表管理等功能,支持多格式音频,界面简约且有丰富主题 ,还能基于算法进行智能推荐。

1.音流App下载:iOS其他平台

2.登录Navidrome

刘郎阁

主机地址填写你部署Navidrome的地址即可。

Navidrome个人音乐服务器到这里就算搭好啦!这下可以随时随地任性的听歌了!快打开播放器,找一首喜欢的歌,闭上眼睛,舒舒服服地“躺平”享受吧。

无损音乐资源下载站点推荐

1.MyFreeMP3
2.歌曲宝
3.Hifini
4.5song
5.天天无损音乐

微信公众号搜索 [刘郎阁] 关注并回复"1623"获取

260、小满

2025年5月21日 13:14

null

当夜卧稍迟,早起迎露,以应阳气渐满之势。晨起可循田埂而行,观麦浪轻摇,听蛙声初沸,导引气血,使志得舒。饮食宜增苦减辛,多食苦瓜、莴笋、新蚕豆以清心火,佐以薏仁、赤豆利湿健脾。

此时阳气将盛,湿气始滋,当调息静心,戒满戒溢,常怀虚谷之志。衣宜选亚麻、棉纱,疏风透汗,尤需护住脘腹关元。常饮三花饮(金银花、菊花、茉莉),清热解郁,兼防湿暑。运动当选八段锦、易筋经等和缓功法,如云舒卷,似穗低垂,使气机流畅而不壅滞。午后小憩半刻,敛阳养阴,此谓「持满之道」。

0元开88vip??

2025年5月22日 11:53

Photo by Ryan Born / Unsplash

因为最近淘宝买了不少东西,有些商家不送退货险,退货运费都挺贵的。

而且今天想买个好价鞋,但是需要88vip的券,@jun 倒是说帮我抢,但是有些券需要等,还要冲购物金,弄来弄去感觉也挺麻烦的。

所以想来想去还是自己开一个。

之前在咸鱼上几毛钱买过一个夸克云的88vip,估摸着中间应该是有什么门道,所以去这些地方做了一下搜索,了解了一下中间的套路。果然不出所料,感觉中间的利润还挺高的,现在这些大公司的羊毛被这些羊毛党可谓是薅到极致。

羊毛党的思路大概是这样:

  1. 去咸鱼、小红书等地方发帖指导别人开88会员,并说可以回收附带的视频、音乐权益,一般两个包年服务的回收价格在80元左右。
  2. 因为咸鱼、小红书平台的封控问题,引导客户至微信沟通。
  3. 顾客咸鱼上架宝贝,他们拍下,然后让客户开通88vip。
  4. 然后他们通过内部共享渠道拿到其他需要充值这两个服务的用户的手机号
  5. 我们配合进行充值
  6. 他们咸鱼付款。

我核查了一下已经了解到的收益点

  1. 推广金,走他们的邀请链接开通好像会有14-16元左右的推广奖励。
  2. 包年会员转手卖掉赚差价,网易云、优酷或芒果的包年服务价格咸鱼在100元左右,利润20元。
推手收益

可能还会有其他我没了解的收益点,但是上述两项至少会有差不多40元的收益,这可以说是无本买卖,如果能把中间的流程优化好,应该还有提升空间。

唯一的问题就是可不可以持续发展,这个完全看淘宝这边的政策管的严不严了。

特stse

2025年5月21日 16:01

书接上文。

小舅子他们在特种兵式的几天游玩之后表示最后一天想在家里休息一下,陪着他们吃了一天清淡的食物后,按了个脚,第二天一号他们便各自返回工作地去了。

而我们两口新的征程才刚要开始——下一站勒多曼因。

其实勒多曼因这个行程是五一之前就开始关注了的,因为之前徒步峨眉山、笔架山后加了很多户外群,五一前的偶然一天看到有人发了一个雪山冰湖皮筏艇的照片,并说准备开团出发,景点是我喜欢的那种风格,强度也还能接受,所以进群了解了一下,总共三天行程,:

  • 第一天成都集合,乘坐包大巴车出发去康定
  • 下午大概四五点到康定,吃晚饭,吃完后坐中转车去老榆林
  • 老榆林早起坐转运车出发去山脚下的格因草原开始徒步。
  • 徒步20公里,爬升到4300米左右,在山上营地住下。
  • 第三天早起冲顶,然后下山。
  • 各回各家。
  • 我们只需要带两天的路餐,其他的他们包。
  • 总费用650元1人,40人成团出发。

当时想着650这价格是真心不贵了,而且恰好能和小舅子他们来的时间错开,所以毫不犹豫的报了两个名。

但是在后面几天接待下舅子的过程中,群里消息不断,组织力度似乎不太够了,总共个加群的人有50多个,接龙说要去的也满了40个人,但是最后交钱的时候却不够包车的40人。

因为人数不够没办法拼大车,群主就开始降级说租20人小车,但是小车价格和大车只便宜了四五百块钱,最后摊下来每个人要多交100元,然后重新组织了一波接龙,不懂贵这100块会产生了多大的化学反应,这次直接20人都没凑齐...。

眼看着要散团了,但是我的瘾又被钓上来了,不想眼看着散团,索性就说我出个车,看有没有其他愿意出车的一起,凑个十几二十个人也还不错,终于在散团之际组织了三个车,最终有17人一起参与了这趟行程。

我们这个7坐车除开我和S,最终分两拨坐了5人。

先是3位泸州的朋友,两男一女,从泸州过来自贡上车,分别是快60的二叔,很会照顾人的刘姐,以及帅气的空少三笑。之后开车去成都接上2泰拳老师和一个川大的大学生。

后面就正式开始发图了,所以这里先吐槽一下这个团。

这个穿山甲不能算是正式的商业团,只能说是有一个大家信的过的群主去联系的车、住宿、餐食,规划好行程后在群里组织的,不过那点组织力几乎是没有,我们因为自驾所以最后减了100的团费,最终收的550元,但是当时承诺的东西一个都没达成,这里罗列已下,下次大家参加这种群性质的团一定要提前问清楚,留好文字存档。

  1. 说好的只用带路餐,但是最后第一天的晚餐需要自费,第二天的早餐也让我们自费,之后在山上说有鸡汤喝也没有准备,山上的营地的晚餐也很差,更别说早餐馒头还是馊的。
  2. 说好会花一千多请一个solo过贡嘎的领导,上山后发现人家是带了商团,几乎没管过我们团,另外一个年轻的小伙子说是被叫来管我们,但是就下山看到了他们一下,拍照服务之类的就更别提了。
  3. 转运车期初以为是什么大巴之类的,上去才知道想多了,全是那种小货卡,上面拉一块篷布就行了,后来听其他上山的说有人坐的车是拉牛羊的,全是屎...。

好了,亏已经吃了,只能说下次注意了。

行程分享正式开始。


我们5月3号从成都出发康定完美错开了车流高峰,全程几乎没有堵车,一路狂奔,车上听二叔说天全服务站有「此生必驾」318的牌子可以打卡,大家商量一致就在天全休息一会儿。

因为是自驾,所以时间比较充裕,路上又没有堵车,我们四点左右就已经到了康定,所以先去刘姐一个朋友开的餐馆吃了一顿菌菇牦牛火锅,老板做的蘸料一绝。

菌汤牦牛肉蒸气火锅

吃完饭才不到5点,所以大家准备一起去逛逛溜溜城。

我们本来是开车进城的,但是穿了一圈都没找到车位,最后在城区里堵了一个小时重新回到康定进城口的边上的一个集中停车场,20元一天,建议大家来这里玩不要想在城区找车位了。

卖青稞大饼,挺香的,下面的店铺15一个,走了一截发现全是从这个店拿的,人家只卖12元,这几步路就有3元的差价。

走到一半发现下雨了,还好穿的冲锋衣。并且凑巧这边有个广场在搞商业活动,似乎是请了什么藏族歌星在唱歌,虽然是藏文歌,但是唱的还挺好听的,不过摇一摇没识别出来。

大家累了坐在路边休息,因为雨大,二叔又没穿冲锋衣,所以暂时和他们分开,我们两口子单独逛去了。

康定旅游局局长同款抖音樱花大街。

溜溜城打卡,为什么叫溜溜城?

“康定溜溜城”这个充满诗意的别称,主要源于康定与《康定情歌》(又名《跑马溜溜的山上》)的深厚渊源。这首传唱世界的民歌让康定以“溜溜”之名广为人知,而“溜溜”在当地方言中更是承载了独特的文化意蕴,在康定及川西方言中,“溜溜”常用来形容事物“美好”“漂亮”“流畅”。

1. 溜溜城打卡 2. 小巷一景

藏族同胞们的广场舞是锅庄,那「锅庄」又是什么呢?。

锅庄(藏语称“果卓”或“歌庄”)是藏族民间广泛流传的一种传统集体舞蹈,尤其在四川、西藏、云南、青海等藏族聚居区盛行。它不仅是重要的娱乐活动,更是藏族文化、宗教和社会交往的载体。

康定的夜景还是非常漂亮的。

天色渐暗后和大家汇合,去往老榆林的民宿休息。

因为对这边的卫生环境不报什么期待,所以提前买了隔脏睡袋,用塑料袋+垃圾桶装了点热水泡脚,早早便躺下休息了。

1. 早起民宿外的风景 2.房间环境 3. 早起窗外的雪山 4. 隔脏睡袋

没有领队,更没有领队说让我们拍出发照,所以我索性叫上大家一起随便拍了一张。

17个人就这几个拍了照...。

坐上蓬卡就出发了,一路上看不到外面,但感觉早餐都要被颠出来了,估计外面到处都是导弹坑。

车上手机拍的远处雪山

1. 格因草原 2. 马帮的马

今天天气不错,依旧没看到领队...。

过木桥,水很凉,都是山上流下来的雪水。低山区

路过一个红石滩

低山区还有很多松树。

在徒步过程中海拔逐渐上升,慢慢树木就变成灌木和草甸,一路上会还经过各种路况:石头路,涉水路,草甸,沼泽等等。

一处绝佳的和雪山合影的机位

我也来一张,在这里不小心把登山杖掉落滚到峡谷下面去了。😅

一爬一个不吱声

有点累,生命力照片一张。

沿途的树木变成了灌木和草甸。

二叔年纪打了,自重也太重,大家都劝他坐马算了,他也听劝,800块直接座马去了营地。后来到营地听他说座马也听危险的,他上去的时候马没站稳前跪了,把他甩到沙地里了,我们在说幸好是沙地...。

终于走了快一半了,两岔河营地吃个午餐

一桌子的方便面碗和围了一桌子崩溃的驴友们。

继续出发

在一个绝望坡前碰到了其他队友,我不拍照大家是真不想拍合照....,碰上我这种队友是真挺好的。

中间那个是本次帮忙的「领队」

最不想走的就是这种河谷路段,全是碎石头。还碰上大风,风里还夹着小粒的冰雹。

最后一个绝望坡,进入雪线了,天上也飘起了小雪。

绝望坡后面的美景

终于到营地了!

营地景色全览

太累了,完全不想拍照用其他人发的照片把。

休息的棚子。营地的饭菜很不好吃,大家草草吃完便都上床准备休息。

因为营地在海拔4300米左右,加上棚子里氧气不太流通,所以帐篷里十几号人晚上过夜的时候都有不同程度的高反,症状基本上都是头疼。我后半夜睡了会,基本上是醒半小时睡半小时左右,我旁边的一个大学生几乎整夜没睡觉,两三点的时候还爬起来说想下山...,一整晚的噪音把他旁边的一个能睡着的老驴折腾到一晚上没怎么睡,感觉老驴也很难受哈哈。

S就是说觉得冷,因为棚子四处漏风,漏风最严重的就是我们躺下后头顶的地方,风大的时候我问不得不起来给她过上急救毯才好些。

后半夜风停了,我帮她把睡袋裹紧,把头罩住就留个鼻子嘴巴出来,才算是踏踏实实睡了好几个小时,比我有出息多了。

第二天四五点大家便陆续起床了,还能看到星空,但是我手机和充电宝都被冻没电了。

早餐的包子是馊的,我就喝了点稀饭,吃了自己带的面包。

因为出发的时候黑黢黢的,手机也没电了,就没拍什么照片,同行的刘姐借了他的充电宝给我用,让我得以续命。

天终于渐渐亮起来,发现我们被大雾追赶着,手机也有一些电了。

跟着前面的队伍的步伐,发现我们没有走常规的泥巴路线,而是条沿着河谷上行的碎石路段,然后踩着雪沿着山脊线的去往山顶,体验顶满,后来听前面的队友说起才知道,他们跟着一个商团的线路走的。

体验非常棒。

开始爬山脊线。几乎六七十度的大坡,踩着雪前进,非常爽!

就冲这一段爬坡这一趟也值了。

这里的雪一脚下去都能没过膝盖,躺雪上面来一张把。

旁边那条是常规上来的路,马上到了,看上面云层上去的时候应该还能看见雪山。

登顶。

说实话因为湖还没完全化开又被白雪覆盖,也不能去湖面拍照,导致山顶的风景和可玩性很一般,这块白色不知道的人还以为只是一片平坦的雪地。

别的不说,先打个卡把。

看好了,这一剑,会很帅!(换我来拍的时候后面忽然就起了大雾,没拍上😭。)

因为风雪,准备下山了。

后面下山的风景就不发了,下山的时候倒是碰上领队了,因为要做扫尾工作,我们又是垫底的,所以一直被催着下山,搞的我们下山速度飞快,后半段几乎都是跑着下山的。

回到起点。

下雨了,安排第一波人坐皮卡,这波人回到民宿衣服基本上都湿了,我依旧做的蓬卡下山,不过因为没休息好,有点晕车的感觉,差点吐了。

之后便是返程,回去的时候只有泸州的三位和我们一起,另外两个坐其他车了。

也好,我们这几个人更聊的来,我们边开边聊,倒也不觉得累,还拉个群准备下次有什么活动再一起出行。

直到晚上11点30分才安全到达自贡,他们另外拼车叫了个野猪儿回泸州。

这次行程结束我缓了三天才缓过来,每天下班回家几乎粘上枕头就睡,每天的睡眠时长破天荒的突破了8个小时,临近9小时!

下次出行真的不想再开车了。

  • 12  

CommentNotifier

2025年5月22日 10:11

CommentNotifier

Typecho博客评论邮件提醒,支持异步回调(异步回调优点就是不影响博客评论提交速度),支持编辑邮件模板,支持第三方开发邮件模板,发信方式支持SMTP与阿里云邮箱推送,支持表情回调(需要主题支持)

安装教程

下载后将压缩包解压到 /usr/plugins 目录
文件夹名改为CommentNotifier
登录管理后台,激活插件
配置插件 填写SMTP参数/阿里云邮箱推送参数
支持显示大部分主题的评论表情

软件架构

typecho版本为1.2.0及以上
php: >=7.2.0
如果启用SMTP加密模式PHP需要打开openssl扩展
邮件服务基于PHPMailer

下载插件

开源地址:https://github.com/jrotty/CommentNotifier

Farallon - 又一款Typecho Theme [置顶]

作者老孙
2024年5月20日 18:43
AI摘要:Farallon是一款移植自`hugo-theme-farallon`的Typecho主题,支持多种自定义功能,如观影页面、友情链接、说说页面等。主题通过Markdown语法实现好物页面的数据获取,并优化了复制链接方式。更新内容包括增加图片灯箱、删除QQ评论通知、显示评论者IP信息等。主题适用于多种插件和API,如豆瓣、Neodb、Memos和Mastodon。

说明

这是我移植的第一款主题

来自hugo-theme-farallon

预览

预览

更新

2025.3.26 0.7.1

两种翻页加载方式

主题设置 travel mid 展示 https://www.imsun.org/category/life/ 样式
设置 memos mid 展示 https://www.imsun.org/category/bb/ 样式

首页列表 说说和 图文 根据后台设置的分类mid 混合展示

删除了添加第三方评论的功能

增加了编辑页面的自定义字段的说明

2025.3.18

更改版本号为0.7.0

修改了好物页面的获取方式,使用Markdown语法表格数据获取

以下为示例

| 图片链接 | 商品名称 | 价格 | 商品链接 | 推荐理由 |
|---------|---------|------|----------|----------|
| https://example.com/ | 商品A | ¥99 | https://example.com/product1 | 这是一个很好的产品 |
| https://example.com/ | 商品B | ¥199 | https://example.com/product2 | 非常推荐购买 |

表格的内容不可缺少

优化了复制链接的方式

说说页面统一使用lightbox2

2025.3.15
增加图片灯箱
删除QQ评论通知
删除显示评论归属地

2024.12.25

使用API查询来获取IP归属地
根据等级设置名字颜色,鼠标悬停在昵称时显示等级,在头像显示评论数

2024.12.12

给评论区加上身份等级
显示留言者的IP信息

删除归档中的字数统计

把豆瓣的设置移到了自定义页面,使用自定义字段设置

使用指南

观影页面说明

by 豆瓣

参照Docker 自动同步豆瓣书影音记录部署

使用自定义字段设置douban 默认为https://db.imsun.org

https://www.imsun.org/movies

by Neodb 0.6.3新增

参照[article id="1688"]

获得获取neodb API
使用自定义字段设置neodb 默认为https://neodb.imsun.org

https://www.imsun.org/neodb

友情链接说明

使用此模板功能必须使用links 插件
https://www.imsun.org/links

可使用 寒泥 大佬制作的版本或者其他版本

说说页面说明

by memos

Memos请自行部署
https://www.imsun.org/memos/
利用memos实现动态获取说说,仅支持memos v0.20.0以下版本
使用自定义字段设置memos
在自定义字段中填入memos值为memos地址,不带/
在自定义字段中填入memosID默认值为1, 当您的ID 不为1时 需要设置
在自定义字段中填入memosnum默认值为20,默认获取20条最近的memo

by mastodon

https://www.imsun.org/talks/
支持mastodon gts pleroma
根据
[article id="1664"]
或者
[article id="1643"]
获得API地址
在自定义字段中填入tooot值为Mastodon API 地址 例如 https://www.imsun.org/toot.json

标签页面

https://www.imsun.org/tags/

分类页面

分类图片按照mid.jpg的格式存放在对应的目录中
然后填入相对的url
https://www.imsun.org/category/

归档页面

https://www.imsun.org/archives/

统计页面 0.6.2 移除

https://www.imsun.org/site/

首页摘要

优先获取自定义字段summary,其次显示默认字数摘要

好物页面

基于memos
https://www.imsun.org/goods
在自定义字段中填入memos值为memos地址
在自定义字段中填入memosID默认值为1,不为1时才需要设置
在自定义字段中填入memostag默认值为好物,不为好物时才需要设置

在0.7.0版本移除了以上方式,改用markdown语法实现.

下载地址

https://github.com/jkjoy/typecho-theme-farallon/releases

Drawnix:无限画布上的开源白板工具

作者ysicing
2025年5月21日 20:40

这个项目很早之前就关注过, 那时还没提供 Dockerfile😁

简介

Drawnix 是一款基于 Plait 框架打造的免费开源白板工具,提供思维导图、流程图和自由绘画等功能。依托插件化架构,用户能够随需扩展多种交互组件。每一次操作都能实时保存至浏览器缓存,确保内容不丢失

核心特性

  • 多样化绘图模式
    • 支持思维导图、流程图与自由画笔,满足不同场景需求
    • 可插入图片,通过简单拖拽丰富画面
  • 高效编辑体验
    • 撤销、重做、复制与粘贴操作流畅
    • 自动保存功能避免意外丢失(浏览器缓存)
  • 无限画布与导出能力
    • 通过缩放与滚动打造无边界创作空间
    • 可将画布导出为 PNG 或 JSON(.drawnix) 文件
  • 插件机制与兼容性
    • 插件架构支持 Angular、React 等多种 UI 框架
    • 自带 Slate 富文本扩展,后续可接入更多文本编辑插件
    • 未来可通过社区插件实现更多应用场景

项目

名称灵感

Drawnix ,源于绘画( Draw )与凤凰( Phoenix )的灵感交织。
凤凰象征着生生不息的创造力,而 Draw 代表着人类最原始的表达方式。在这里,每一次创作都是一次艺术的涅槃,每一笔绘画都是灵感的重生。
创意如同凤凰,浴火方能重生,而 Drawnix 要做技术与创意之火的守护者。
Draw Beyond, Rise Above.

与 Plait 框架的深度结合

Drawnix 底层依托作者公司开源的 Plait 画图框架,这是知识库产品的重要技术沉淀。两者紧密结合让开发者享受“一键开箱即用”的便捷,同时还能在业务分层中灵活装载自定义插件。

这款工具不仅是面向个人创作者,同样适用于团队协作与在线教学。无限画布中,每一次涂抹都让构思变得更生动、更立体,技术与想象力在此汇合,带来前所未有的白板体验。

私有化部署

作者已经提供了 Dockerfile,基于此 Dockerfile,我构建了一个镜像, 默认监听 80 端口,除此外无特殊配置

  • ccr.ccs.tencentyun.com/k7scn/drawnix 基于此97ab1d4构建完成

compose 部署

  • docker-compose.yaml
services:
  drawnix:
    image: ccr.ccs.tencentyun.com/k7scn/drawnix
    container_name: drawnix
    ports:
      - '100.90.80.15:8088:80'
    restart: always

然后配置 caddy 代理即可。

k3s 部署

我主要使用 k3s 部署,对外访问使用 cft

  • drawnix.yaml
---
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    app: drawnix
  name: drawnix
  namespace: kc-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: drawnix
  updateStrategy:
    type: InPlaceIfPossible
  template:
    metadata:
      labels:
        app: drawnix
    spec:
      tolerations:
      - operator: Exists
      nodeSelector:
        node-role.kubernetes.io/kc: "true"
      containers:
      - image: tcr.china.12306.work/github/drawnix
        imagePullPolicy: Always
        name: drawnix
        ports:
        - containerPort: 80
          protocol: TCP
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: drawnix
  name: drawnix
  namespace: kc-system
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: drawnix
  type: ClusterIP

apply 之后,在 cfd tunnel 管理页添加公共主机名

演示站

欢迎自建,不保证 SLA,暂时不知道开源版与官方演示有什么区别。


欢迎订阅我的微信公众号,同步更新 😁

说说:又开始出游计划了

2025年5月20日 22:35

前几天说要出去玩,结果突然就拍板定下了,这次计划去爬泰山。途中经过北京顺便也去一趟故宫参观一下!具体的日程可不像之前去西安玩做的那么充足,自带主打一个随性!故宫和泰山其实虽然看起来是核心内容,但是我的心思还是放在吃上了哈哈哈哈哈!

读《巴菲特之道》摘抄

2025年5月20日 09:50

刚刚的伯克希尔年会,巴菲特宣布了即将退休,这将又是一个时代终结。于是本月决定看看跟巴菲特相关的书,《巴菲特之道》这本书介绍了巴菲特的投资理念,内容也不长花了几天就看完了。

巴菲特投资哲学的成长

巴菲特从小就接触投资,做生意积累本金,通过股市赚钱。他人生有几个重要的人,格雷厄姆是他的导师,巴菲特从他这里学会了安全边际,也就是要买价格低于价值的股票。巴菲特在学校是格雷厄姆的学生,毕业后也到格雷厄姆的公司工作了几年,从他这里巴菲特还学会了独立思考。

巴菲特读了费雪的《普通股和不普通的利润》之后,在他的投资理念上更加转向费雪。费雪更加关注公司的成长潜力,以及公司是否有好的管理层,这与格雷厄姆的是两种筛选公司的理念。而巴菲特将两种理念融合,发展出自己的投资准则。

查理芒格,跟巴菲特一样是格雷厄姆的学生,他们是一生的事业伙伴,两人建立了密切的共生关系。

巴菲特的投资准则

巴菲特的投资准则有十二条,分为企业、管理、财务、市场共四个分类,他的很多投资都践行了这些准则的部分或者全部,具体书中单独一章进行了讲解。

具体的准则如下:

企业准则

企业应该简单易懂;

企业应该有持续稳定的运营历史;

企业应该有良好的长期远景;

管理准则:

管理层是否理性?

管理层对股东是否坦诚?

管理层能否抗拒惯性驱使?

财务准则:

  1. 重视资产净收益,而不是每股盈利

  2. 计算真正的”股东盈余“

  3. 寻找高利润率的企业

  4. 企业每留存一美元,至少产生一美元的市值

市场准则

  1. 公司是否有价值? 巴菲特通过现金流和合适的折现率确定企业价值,他使用美国政府长期国债利率作为折现率。价值是未来现金流折现后的现值;成长是确定价值的一个因素。

  2. 当前是否是买入的好时机,价格是否好? 合适的价格+公司表现符合预期才能保证成功,也就是安全边际。

心理学和数学在巴菲特投资中的体现

书中关于持股数量的数学分析,虽然说分散投资可以降低风险,但同时也会降低利润。而巴菲特正是集中投资的范本。书中这段话说的很好,“当世界给予你机会的时候,聪明的投资者会出重手。当他们具有极大赢面时,他们会下大注。其余的时间里,他们做的仅仅是等待。”

巴菲特还是典型的长期主义投资者,通过他的准则可以看到他在选择购买的股票时,也就已经相信这家公司在未来的十年能够创造相当的利润。

系统1与系统2

在丹尼尔·卡尼曼的《思考,快与慢》中首次了解了系统1与系统2,在这本书中再次被提及。系统1是我们的直接思维,一般不花时间,会快速做出判断。而系统2的思维方式是我们认知过程的反思,需要我们投入努力。无论是投资还是做决定,我们都有必要训练系统2,去认真思考,进行推敲。同时在作者看来,具有系统2思维方式的人更加有耐心。

总结

限于个人能力,内容写的比较乱。总结一下巴菲特的成功,理性和耐心是他成功的关键。对于普通人,如果不能够做到这些,并且不愿意花费时间去研究公司,那么巴菲特推荐我们去购买指数基金。

最后用书中的一段话作为结尾。一个人在一生中很难做出数以百计的正确决策,只要做出为数不多的智慧决策就已经足够了。

摘抄

理性的基石就是回望过去、总结现在,分析若干可能情况,最终做出抉择的能力。

投资是经过深入分析,可以承诺本金安全并提供满意回报的行为。不能满足这些要求的就是投机。

格雷厄姆的两项投资原则: 一是不要亏损;二是不要忘记第一条。

任何投资的价值都是公司未来现金流的折现。

巴菲特从格雷厄姆那里学到的最为重要的一课就是:成功的投资来源于,购买那些价格大大低于价值的股票。

从格雷厄姆那里,巴菲特学会了独立思考。如果你是在脚踏实地的基础上得出合乎逻辑的结论,就不要因为别人的反对而耽于行动。

从费雪那里,巴菲特学到了沟通的价值

他定义特许经营权企业的产品或服务:①被需要或渴望;②无可替代;③没有管制

巴菲特说:“市场就像上帝一样,帮助那些自助的人;但和上帝不同之处在于,市场不会原谅那些不知道自己在干什么的人。

在你占据优势的时候要加大筹码。

巴菲特说:“我们所要做的全部就是,将盈利概率乘上可能盈利的数量,减去亏损的概率乘上可能亏损的数量。

当世界给予你机会的时候,聪明的投资者会出重手。当他们具有极大赢面时,他们会下大注。其余的时间里,他们做的仅仅是等待。

巴菲特的风险观:风险与股价之波动无关,与那些个股未来产生利润的确定性有关。

短期而言,股市是台投票机;而长期而言,股市是台称重机。

”首先是将股票视为企业一样,“这将给你一个完全不同于股市中大多数人的视角”。其次是安全边际概念,“这将赋予你竞争优势”。再次是对待股市具有一个真正投资者的态度。

为何懂得人们的冲动是如此有价值:①你能从中学会如何避免多数人的错误;②你可以识别他人的错误,并从中捕捉到机会。

单单有智力不足以取得投资成功,与大脑的容量相比,将理性从情绪中分离出来的能力更为重要。

理性的基石就是回望过去、总结现在,分析若干可能情况,最终做出抉择的能力。

看完评论一下吧

初涉 ML Workflow 系统:Kubeflow Pipelines、Flyte 和 Metaflow

作者四火
2025年5月19日 07:18

入职 Coupang 两个月了,第一个月主要上手和开发 BOS(Business Operating System)系统,第二个月开始调研选型 ML Workflow 平台。前者目前来说相对比较简单,后者对我来说是一个新坑,也比较有意思,随便写写技术上的体会。

先扯点题外话,其实这次求职有几个比较符合我预期的机会,可在思考之后,我基本上毫不犹豫就选择了 Coupang 这一家。最主要的原因,并非因为雇主,而是因为要做的事情。一个相当规模的团队,在大干一场的早期阶段,要在搭建起属于自己相当规模的 AI infra 来。

我觉得软件行业的巨大的变革,新世纪以来就三次,第一次是互联网应用的崛起,我太小没能做啥;一次是十几年前的 cloud,看着它从爆发式增长到如同水和电一样进入我们的生活,可我算是错过了它比较早期的阶段,即便相当长的时间内我在 Amazon,但是我却并不在 AWS;而这一次,当 AI 的浪潮再来的时候,我就很想行动起来,真正投身其中。程序员的一生能有几个赶这样大潮的机会呢,我不想再错过了。虽说我没有 AI 的技术背景,但我知道 ML infra 到 AI infra 却是个我可以切入的角度——从我最初接触软件开始,尤其是学习全栈技术的时期开始,我就认定,技术是相通的,这十几年来我一直在如此实践。因此在调查和思考之后,我觉得这是一个我不想错过,并且更重要的是自认为能够抓住的机会。

当然,就此打住,我目前只是这个领域的初学者,因此理解并不深入。

Why ML Workflow?

接着说正题,在这一个月之前,虽然我经历过不少关于 workflow 的团队,虽然我参与过从零写完整的 workflow 引擎,但这些都是针对于通用 workflow 而言的,我对于机器学习的工作流,也就是 ML workflow 可以说一无所知。于是在问题和需求调查的过程中,第一个关于它的问题就自然而然出现了,我们是否真的需要 ML workflow,而不是通用的 workflow 系统?

其实,这主要还是由于 ML 的生态所决定的。通用 workflow 可以完成很多的事情,但是在机器学习到 AI 的领域内,这个过程中最主要的目的就是把 raw data 给转换成经过训练和验证的 model,其中有很多部分都是有固定模式,因而自成体系的。举例来说:

  • ML workflow 关注数据处理和 ML 或者 AI model 的生命周期,但是通用的 workflow 往往关注将业务流程自动化;
  • ML workflow 需要将 artifact 管理、model registry、model insights 和 experiment tracking 等工具集成起来,但是通用的 workflow 往往是业务 application 层面的集成;
  • ML workflow 执行的 task 往往需要高 GPU 使用和高内存,这和通常我们讨论的 workflow 的 task 对于 CPU 的使用完全不同。

总之,ML workflow 更像是一个 workflow 中的重要分支,它的特异性显著,因而从架构上它有很多在我们谈论通常 workflow 的时候不太涉及的特点,并且它们具有明显的共性。

ML Workflow 的固定套路

Workflow 这样的系统,和很多 infra 系统不同的地方在于,它具有全栈的特性,需要从端到端从用户完整的 use case 去思考。回想起通用的 workflow,我们会想,用户会去怎样定义一个 Workflow,怎样运行和测试它,并且怎样部署到线上跑起来。这其中的前半部分就是 development experience,而后半部分则是 deployment experience。

首先,对于 development experience 这个角度,ML workflow 有它独特的地方,其中最主要的就是 Python SDK。

通用 workflow 我们讲定义一个新的 workflow 的时候,我们通常都需要写一个 DSL,里面定义了一大堆 task 和依赖关系,而对于做得比较好的 workflow 系统来说,可能还需要一个可视化的 drag-and-drop 界面来方便地创建 workflow。

但是对于 ML workflow 来说,它最特殊之处是对于 Python code 的无缝集成。因为 Python 之于 ML 的地位就像是 Java 之于企业架构的地位,任何一个 ML workflow 客户端首先要考虑支持的编程语言就是 Python,用户通过往大了说是 SDK,而往小了说则是简单的 Python decorators,就可以定义 task 和 workflow。比方说,一个简单的 Flyte 的 hello world:

from flytekit import task, workflow

@task
def say_hello(name: str) -> str:
    return f"Hello, {name}!"


@workflow
def hello_workflow(name: str = "World") -> str:
    return say_hello(name=name)

在 ML workflow 的世界中,这是除了 DSL 和视图化之外的第三种定义 workflow 和 task 的方式,也是必须具备的方式。

第二个,对于 deployment experience 的角度,大致上是基于 Kubernetes 从 control plane 到 data plane 固定的交互机制。

我不知道这是不是一种关于 ML workflow 的约定俗成,但是通过调研 Kubeflow Pipelines、Flyte 和 Metaflow,我发现这三种对于 control plane 到 data plane 的交互模式是出乎意料地一致。

  • KubeFlow Pipelines: client [KFP SDK] -> control plane [API Server -> K8s APIs (CRD changes) -> Workflow Controller / K8s Operator] -> data plane [K8s API -> creating Task Pods -> blob storage]
  • Flyte: client [Flyte SDK] -> control plane [Flyte Admin -> K8s APIs (CRD changes) -> Flyte Propeller / K8s Operator] -> data plane [K8s API -> creating Task Pods -> blob storage]
  • Metaflow: client [Metaflow SDK] -> control plane [Metaflow Service -> K8s APIs (CRD changes) -> Metaflow Scheduler / K8s Operator] -> data plane [K8s API -> creating Task Pods -> blob storage]

注:也有把 Operator 那一层归为 data plane 的,我觉得都说得过去。

其中 Metaflow 说的是使用 Kubernetes 集成的情况,因为它并不是非得依赖于 Kubernetes。

但大多数使用都是基于 Kubernetes 的,而且基本上都是这个套路,control plane 的 service 收到请求以后,通过创建 K8s CRD objects 的方式告知 workflow controller(scheduler)来执行 workflow,对于 task 的执行通过调用 data plane 的 K8s API 来创建 task pods 执行。

对于特殊的 task,需要交由特殊的 K8s operator 来执行,那么这个 “交由” 的过程,也是通过 K8s 这一层的 CRD change 来实现——Propeller 负责创建 CRD,而对应的 operator 负责 monitor 相应的 CRD 改变并相应地执行任务。Propeller 和 operator 二者互相并不知道对方的存在。这种方式对于保证 operator 的重用性和跨 workflow 系统的统一性简直是太棒的设计了,我们在 try out 的时候,就让 Kubeflow Pipelines 系统中的 operator,去执行 Flyte 给创建的 PTJob 和 TFJob。

关于架构,我觉得 Flyte 的这张架构图对于 components 层次的划分说得非常清楚,下面的 control plane 和 data plane 是可以有属于自己的 cluster 的,不过值得说明的是,真正最终执行的 task pods,也就是图中的最下面的 K8s Pod,也是可以放在另外的 cluster 上,由远程的 K8s API 调用触发的,这样就可以带来更多一层的灵活性:

[Update on 5/31] 后来看到这篇非常好的分享 《Flyte School: Flyte Architecture Deep Dive》,对于初步了解的工程师来说,推荐观看。下图也来自于它。

ML Workflow 的特性比较

再来比较这三个 workflow 的优劣,我并不打算列全,而是简单说说自己印象最深的几点:

  • Kubeflow Pipelines 基本上有着最大的社区,因此它相对比较成熟,有自带的基于 CRD 的 K8s-native 的集成,因此可以直接跑 TensorFlow job 和 PyTorch job 之类的;UI 功能也比较强大,可以通过 drag-and-drop 来定制 workflow,也支持 yaml 文件创建 workflow。
  • Flyte 最吸引人的是它的 Strong Typing,很多错误能够在编译期本地就能够发现(Kubeflow pipelines 和 Metaflow 都只是 hints);开发过程中,本地直接就能跑,而不需要什么 container;对于 multi-tenancy 支持得最好(比如 RBAC 和 tenant 的 Quota 机制)。
  • Metaflow 的 setup 特别简单,而且本地可以直接调试;它对于 AWS 的一些 service 直接可以集成使用,特别方便(比如 Step Functions);Kubernetes 并不是一个依赖,也可以跑在 VM 上等等。

在我把这三者全部在 EKS 上搭了一遍并使用了一圈,也仔仔细细对别了特种特性和优劣之后,我对于 Flyte 的特性比较感兴趣,我觉得它们对我们团队也比较有用。

具体来说,很多区别但最重要的是两个:一个是 strong typing,其它两个都只支持 Python 类型的 hints,就这一点上,和一些 ML engineer 也讨论过,把问题发现在本地,是非常吸引人的;再一个是 multi-tenancy,对其 Flyte 有很多原生的特性支持,在平台完成之后,我们希望把平台上 ML 的能力开放出去,因此这是很重要的一个特性。此外,我也在考虑对于一个 control plane + 多个 data plane 这种 use case 的情况,这部分的需求还比较模糊,但是 Flyte 依然是这方面支持特性相对比较多的一个。

无论最后的结论为何,我希望我们能够比较灵活地部署选中的这个 ML workflow system,比方说,在 CLI 上,我们考虑在更高维度建立出一层,用户使用同样的命令,无论下面执行的 workflow 系统是什么,都不需要改变,这样一来,等到未来如果我们需要支持第二个,应该能够比较容易地整合进去。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

解锁高效开发利器——Gitness,助力你的DevOps之旅

作者ysicing
2025年5月18日 22:19

在快节奏的软件开发世界中,高效的代码托管和自动化部署是每个开发团队的追求。你是否曾为繁琐的 CI/CD 流程而头疼?是否希望有一个简单易用、功能强大的平台来管理你的代码和流水线?今天,我们为你介绍一款开源神器——Gitness,它将彻底改变你的开发体验。

作为 Drone 和 Gitness 深度定制用户,我还值得很推荐的。

什么是 Gitness

Gitness 是一个集代码托管与自动化 DevOps 流水线于一体的开源开发平台。作为 Drone CI 的下一代产品,Gitness 不仅保留了强大的持续集成(CI)功能,还新增了源代码托管能力,支持本地部署和 Docker 容器化运行。无论是个人开发者还是小型团队,Gitness 都能提供灵活、高效的解决方案。

核心亮点

  • 一体化平台:代码托管 + CI/CD 流水线 + 制品库
  • 用户友好:提供直观的 UI 界面、REST API,满足不同开发者的使用习惯
  • 灵活部署:支持私有化本地部署
  • 开源免费:完全开源,社区驱动,适合预算有限的团队(非常适合个人用户)

对比 Drone、Woodpecker

  • 目前支持 Runner 有限,仅支持 docker
  • 新增的制品库支持的种类相对比较少,常见的 Docker、Helm 是没问题的
  • 目前数据库仅支持 PG、Sqlite3
  • 流水线
    • 日志相比较 v2 仅支持存储到数据库,暂不支持存储到对象存储
    • 不支持重复执行(如定时、失败重试)
    • 流水线语法变更,相比较 Drone、Woodpecker 等 v2 版本的语法,功能缺少较多,但是满足基本使用
  • 代码仓库功能完善,日常使用是没问题

部署

环境变量配置

目前官方文档未提供,需要参考官方文档 harness/harness#config.go

部署

镜像:

  • harness/harness:unstable
  • 国内镜像 ccr.ccs.tencentyun.com/k7scn/harness:unstable
  • docker-compose.yaml
services:

  gitness:
    image: harness/harness:unstable
    container_name: gitness
    restart: always
    ports:
      - "3000:3000"
      - "3022:3022"
    volumes:
      - /data/gitness:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - GITNESS_DEBUG=true
      - GITNESS_TRACE=true
      - GITNESS_GIT_TRACE=true
      - GITNESS_GIT_DEFAULTBRANCH=master
      - GITNESS_WEBHOOK_ALLOW_LOOPBACK=true
      - GITNESS_WEBHOOK_ALLOW_PRIVATE_NETWORK=true
      - GITNESS_METRIC_ENABLED=false
      - GITNESS_PRINCIPAL_ADMIN_UID=ysicing
      - GITNESS_PRINCIPAL_ADMIN_DISPLAY_NAME=ysicing
      - GITNESS_PRINCIPAL_ADMIN_EMAIL=ysicing@12306.work
      - GITNESS_PRINCIPAL_ADMIN_PASSWORD=ysicing
#     - GITNESS_SMTP_HOST=
#     - GITNESS_SMTP_PORT
#     - GITNESS_SMTP_USERNAME
#     - GITNESS_SMTP_PASSWORD
#     - GITNESS_SMTP_FROM_MAIL
#     - GITNESS_SMTP_INSECURE
      - GITNESS_GITSPACE_DEFAULT_BASE_IMAGE=ccr.ccs.tencentyun.com/k7scn/base:dev-ubuntu-24.04
      - GITNESS_UI_SHOW_PLUGIN=true
      - GITNESS_URL_GIT=http://192.168.23.16:3000/git
      - GITNESS_URL_UI=http://192.168.23.16:3000
      - GITNESS_URL_API=http://192.168.23.16:3000/api
      - GITNESS_URL_GIT_SSH=ssh://192.168.23.16
      - GITNESS_SSH_ENABLE=true
#      - GITFOX_SSH_PORT=3022
#      - GITNESS_CI_PARALLEL_WORKERS=2
#      - GITNESS_GIT_ROOT
      - GITNESS_CI_PLUGINS_ZIP_URL=https://c.ysicing.net/oss/offline/master.zip

部署完成,使用 IP:3000 访问. 账号密码需要使用设置的 GITNESS_PRINCIPAL_ADMIN_UIDGITNESS_PRINCIPAL_ADMIN_PASSWORD

目前配置参数过多,稍微配置不对还可能有 bug。针对这个

  • 我司会开源 GitFox,基于 Gitness 定制的 fork 版本,一些额外特性功能的补充(企业、与禅道的联动)
  • 我个人也维护了一套 Gitness 的魔改版本 Gitless,在上游的版本增加了个人开发者常用的特性(仅从我个人使用)

使用说明

这里简单过一些,后面会专门写一些文章介绍一下。

创建组织

对应其他 Git 服务的组织(ORG/GROUP)

创建仓库

流水线

执行流水线,目前支持部分 drone 插件,基本可以拿来就用如果熟悉 drone 和 gitness 的流水线语法的话

制品仓库

目前支持如下,感觉是借鉴了 Nexus,基本已经覆盖了日常使用。

WebIDE

总结

Gitness 不仅是一款工具,更是开发者高效协作的伙伴。无论你是追求极致效率的个人开发者,还是需要稳定流水线的小型企业团队,Gitness 都能满足你的需求。不过有一说一,目前 Gitness 还是处于快速发展阶段,有 Bug 是正常不过的事,但是基本功能还是没啥问题的。其次他们开源版本支持力度不是那么紧急,更多的还是服务他们的 SAAS 产品。


用户外活动填满五一:勒多曼因篇

2025年5月18日 15:45

null

书接上文。

小舅子他们在特种兵式的几天游玩之后表示最后一天想在家里休息一下,陪着他们吃了一天清淡的食物后,按了个脚,第二天一号他们便各自返回工作地去了。

而我们两口新的征程才刚要开始——下一站勒多曼因。

其实勒多曼因这个行程是五一之前就开始关注了的,因为之前徒步峨眉山、笔架山后加了很多户外群,五一前的偶然一天看到有人发了一个雪山冰湖皮筏艇的照片,并说准备开团出发,景点是我喜欢的那种风格,强度也还能接受,所以进群了解了一下,总共三天行程,:

  • 第一天成都集合,乘坐包大巴车出发去康定
  • 下午大概四五点到康定,吃晚饭,吃完后坐中转车去老榆林
  • 老榆林早起坐转运车出发去山脚下的格因草原开始徒步。
  • 徒步20公里,爬升到4300米左右,在山上营地住下。
  • 第三天早起冲顶,然后下山。
  • 各回各家。
  • 我们只需要带两天的路餐,其他的他们包。
  • 总费用650元1人,40人成团出发。

当时想着650这价格是真心不贵了,而且恰好能和小舅子他们来的时间错开,所以毫不犹豫的报了两个名。

但是在后面几天接待下舅子的过程中,群里消息不断,组织力度似乎不太够了,总共个加群的人有50多个,接龙说要去的也满了40个人,但是最后交钱的时候却不够包车的40人。

因为人数不够没办法拼大车,群主就开始降级说租20人小车,但是小车价格和大车只便宜了四五百块钱,最后摊下来每个人要多交100元,然后重新组织了一波接龙,不懂贵这100块会产生了多大的化学反应,这次直接20人都没凑齐...。

眼看着要散团了,但是我的瘾又被钓上来了,不想眼看着散团,索性就说我出个车,看有没有其他愿意出车的一起,凑个十几二十个人也还不错,终于在散团之际组织了三个车,最终有17人一起参与了这趟行程。

我们这个7坐车除开我和S,最终分两拨坐了5人。

先是3位泸州的朋友,两男一女,从泸州过来自贡上车,分别是快60的二叔,很会照顾人的刘姐,以及帅气的空少三笑。之后开车去成都接上2泰拳老师和一个川大的大学生。

后面就正式开始发图了,所以这里先吐槽一下这个团。

这个穿山甲不能算是正式的商业团,只能说是有一个大家信的过的群主去联系的车、住宿、餐食,规划好行程后在群里组织的,不过那点组织力几乎是没有,我们因为自驾所以最后减了100的团费,最终收的550元,但是当时承诺的东西一个都没达成,这里罗列已下,下次大家参加这种群性质的团一定要提前问清楚,留好文字存档。

  1. 说好的只用带路餐,但是最后第一天的晚餐需要自费,第二天的早餐也让我们自费,之后在山上说有鸡汤喝也没有准备,山上的营地的晚餐也很差,更别说早餐馒头还是馊的。
  2. 说好会花一千多请一个solo过贡嘎的领导,上山后发现人家是带了商团,几乎没管过我们团,另外一个年轻的小伙子说是被叫来管我们,但是就下山看到了他们一下,拍照服务之类的就更别提了。
  3. 转运车期初以为是什么大巴之类的,上去才知道想多了,全是那种小货卡,上面拉一块篷布就行了,后来听其他上山的说有人坐的车是拉牛羊的,全是屎...。

好了,亏已经吃了,只能说下次注意了。

行程分享正式开始。


我们5月3号从成都出发康定完美错开了车流高峰,全程几乎没有堵车,一路狂奔,车上听二叔说天全服务站有「此生必驾」318的牌子可以打卡,大家商量一致就在天全休息一会儿。

因为是自驾,所以时间比较充裕,路上又没有堵车,我们四点左右就已经到了康定,所以先去刘姐一个朋友开的餐馆吃了一顿菌菇牦牛火锅,老板做的蘸料一绝。

菌汤牦牛肉蒸气火锅

吃完饭才不到5点,所以大家准备一起去逛逛溜溜城。

我们本来是开车进城的,但是穿了一圈都没找到车位,最后在城区里堵了一个小时重新回到康定进城口的边上的一个集中停车场,20元一天,建议大家来这里玩不要想在城区找车位了。

卖青稞大饼,挺香的,下面的店铺15一个,走了一截发现全是从这个店拿的,人家只卖12元,这几步路就有3元的差价。

走到一半发现下雨了,还好穿的冲锋衣。并且凑巧这边有个广场在搞商业活动,似乎是请了什么藏族歌星在唱歌,虽然是藏文歌,但是唱的还挺好听的,不过摇一摇没识别出来。

大家累了坐在路边休息,因为雨大,二叔又没穿冲锋衣,所以暂时和他们分开,我们两口子单独逛去了。

康定旅游局局长同款抖音樱花大街。

溜溜城打卡,为什么叫溜溜城?

“康定溜溜城”这个充满诗意的别称,主要源于康定与《康定情歌》(又名《跑马溜溜的山上》)的深厚渊源。这首传唱世界的民歌让康定以“溜溜”之名广为人知,而“溜溜”在当地方言中更是承载了独特的文化意蕴,在康定及川西方言中,“溜溜”常用来形容事物“美好”“漂亮”“流畅”。

1. 溜溜城打卡 2. 小巷一景

藏族同胞们的广场舞是锅庄,那「锅庄」又是什么呢?。

锅庄(藏语称“果卓”或“歌庄”)是藏族民间广泛流传的一种传统集体舞蹈,尤其在四川、西藏、云南、青海等藏族聚居区盛行。它不仅是重要的娱乐活动,更是藏族文化、宗教和社会交往的载体。

康定的夜景还是非常漂亮的。

天色渐暗后和大家汇合,去往老榆林的民宿休息。

因为对这边的卫生环境不报什么期待,所以提前买了隔脏睡袋,用塑料袋+垃圾桶装了点热水泡脚,早早便躺下休息了。

1. 早起民宿外的风景 2.房间环境 3. 早起窗外的雪山 4. 隔脏睡袋

没有领队,更没有领队说让我们拍出发照,所以我索性叫上大家一起随便拍了一张。

17个人就这几个拍了照...。

坐上蓬卡就出发了,一路上看不到外面,但感觉早餐都要被颠出来了,估计外面到处都是导弹坑。

车上手机拍的远处雪山

1. 格因草原 2. 马帮的马

今天天气不错,依旧没看到领队...。

过木桥,水很凉,都是山上流下来的雪水。低山区

路过一个红石滩

低山区还有很多松树。

在徒步过程中海拔逐渐上升,慢慢树木就变成灌木和草甸,一路上会还经过各种路况:石头路,涉水路,草甸,沼泽等等。

一处绝佳的和雪山合影的机位

我也来一张,在这里不小心把登山杖掉落滚到峡谷下面去了。😅

一爬一个不吱声

有点累,生命力照片一张。

沿途的树木变成了灌木和草甸。

二叔年纪打了,自重也太重,大家都劝他坐马算了,他也听劝,800块直接座马去了营地。后来到营地听他说座马也听危险的,他上去的时候马没站稳前跪了,把他甩到沙地里了,我们在说幸好是沙地...。

终于走了快一半了,两岔河营地吃个午餐

一桌子的方便面碗和围了一桌子崩溃的驴友们。

继续出发

在一个绝望坡前碰到了其他队友,我不拍照大家是真不想拍合照....,碰上我这种队友是真挺好的。

中间那个是本次帮忙的「领队」

最不想走的就是这种河谷路段,全是碎石头。还碰上大风,风里还夹着小粒的冰雹。

最后一个绝望坡,进入雪线了,天上也飘起了小雪。

绝望坡后面的美景

终于到营地了!

营地景色全览

太累了,完全不想拍照用其他人发的照片把。

休息的棚子。营地的饭菜很不好吃,大家草草吃完便都上床准备休息。

因为营地在海拔4300米左右,加上棚子里氧气不太流通,所以帐篷里十几号人晚上过夜的时候都有不同程度的高反,症状基本上都是头疼。我后半夜睡了会,基本上是醒半小时睡半小时左右,我旁边的一个大学生几乎整夜没睡觉,两三点的时候还爬起来说想下山...,一整晚的噪音把他旁边的一个能睡着的老驴折腾到一晚上没怎么睡,感觉老驴也很难受哈哈。

S就是说觉得冷,因为棚子四处漏风,漏风最严重的就是我们躺下后头顶的地方,风大的时候我问不得不起来给她过上急救毯才好些。

后半夜风停了,我帮她把睡袋裹紧,把头罩住就留个鼻子嘴巴出来,才算是踏踏实实睡了好几个小时,比我有出息多了。

第二天四五点大家便陆续起床了,还能看到星空,但是我手机和充电宝都被冻没电了。

早餐的包子是馊的,我就喝了点稀饭,吃了自己带的面包。

因为出发的时候黑黢黢的,手机也没电了,就没拍什么照片,同行的刘姐借了他的充电宝给我用,让我得以续命。

天终于渐渐亮起来,发现我们被大雾追赶着,手机也有一些电了。

跟着前面的队伍的步伐,发现我们没有走常规的泥巴路线,而是条沿着河谷上行的碎石路段,然后踩着雪沿着山脊线的去往山顶,体验顶满,后来听前面的队友说起才知道,他们跟着一个商团的线路走的。

体验非常棒。

开始爬山脊线。几乎六七十度的大坡,踩着雪前进,非常爽!

就冲这一段爬坡这一趟也值了。

这里的雪一脚下去都能没过膝盖,躺雪上面来一张把。

旁边那条是常规上来的路,马上到了,看上面云层上去的时候应该还能看见雪山。

登顶。

说实话因为湖还没完全化开又被白雪覆盖,也不能去湖面拍照,导致山顶的风景和可玩性很一般,这块白色不知道的人还以为只是一片平坦的雪地。

别的不说,先打个卡把。

看好了,这一剑,会很帅!(换我来拍的时候后面忽然就起了大雾,没拍上😭。)

因为风雪,准备下山了。

后面下山的风景就不发了,下山的时候倒是碰上领队了,因为要做扫尾工作,我们又是垫底的,所以一直被催着下山,搞的我们下山速度飞快,后半段几乎都是跑着下山的。

回到起点。

下雨了,安排第一波人坐皮卡,这波人回到民宿衣服基本上都湿了,我依旧做的蓬卡下山,不过因为没休息好,有点晕车的感觉,差点吐了。

之后便是返程,回去的时候只有泸州的三位和我们一起,另外两个坐其他车了。

也好,我们这几个人更聊的来,我们边开边聊,倒也不觉得累,还拉个群准备下次有什么活动再一起出行。

直到晚上11点30分才安全到达自贡,他们另外拼车叫了个野猪儿回泸州。

这次行程结束我缓了三天才缓过来,每天下班回家几乎粘上枕头就睡,每天的睡眠时长破天荒的突破了8个小时,临近9小时!

下次出行真的不想再开车了。

Passport

2025年5月18日 10:33

Passport

本插件为 Typecho 博客系统提供密码找回功能。用户可以通过注册邮箱接收密码重置链接,从而重新设置账户密码。
原始仓库地址: typecho-fans/plugins/Passport
此版本在原版基础上进行了功能增强和 UI 优化。

下载

开源地址:https://github.com/little-gt/PLUGION-Passport

功能

  • 通过邮件发送密码重置链接。
  • 支持 SMTP 服务器配置,保证邮件发送的可靠性。
  • 可自定义密码重置邮件模板。
  • 支持多种验证码服务:

    • Google reCAPTCHA v2 (最新版)
    • hCaptcha
    • 可选择不使用验证码
  • 验证码配置项根据所选类型动态显示,界面更简洁。
  • 找回密码和重置密码页面的验证码 UI 左对齐,更美观。
  • 使用最新的 PHPMailer 库特性。
  • 安全可靠的 Token 生成与验证机制。

私有化部署Nexus3镜像源

作者ysicing
2025年5月17日 09:19

昨天升级 Nexus3 翻车了,准备基于 PG 重新搭建 Nexus3 源,并移除一些不用的软件源, 新版软件源支持如下

环境要求

  • 机器配置至少 2C4G
  • 网络要好,推荐境外机器部署
  • 大硬盘可选(不代理 py 等还好)

部署

nexus 服务

使用 compose 或者 k8s,原理差不多,我的环境使用 k3s。

  • docker-compose.yaml
services:
  nexus3:
    image: sonatype/nexus3:latest
    container_name: nexus3
    restart: always
    ports:
      - "8081:8081"
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms2703m -Xmx4G -XX:MaxDirectMemorySize=2703m -Djava.util.prefs.userRoot=/nexus-data/javaprefs
      - NEXUS_DATASTORE_ENABLED=true
      - NEXUS_DATASTORE_NEXUS_JDBCURL=jdbc:postgresql://postgres:5432/nexus3
      - NEXUS_DATASTORE_NEXUS_USERNAME=oup44Fai4ta
      - NEXUS_DATASTORE_NEXUS_PASSWORD=ienah9eiquah7GeiMaengeitie5aeq66
    volumes:
      - /data/nexus/nexusdata:/nexus-data
    depends_on:
      - postgres

  postgres:
    image: bitnami/postgresql:17
    container_name: postgres
    environment:
      - POSTGRESQL_DATABASE=nexus3
      - POSTGRESQL_USERNAME=oup44Fai4ta
      - POSTGRESQL_PASSWORD=ienah9eiquah7GeiMaengeitie5aeq66
    volumes:
      - /data/nexus/postgres:/bitnami/postgresql
    restart: always

在启动前,先创建好目录,并给予权限

mkdir -p /data/nexus/postgres /data/nexus/nexusdata
chmod 777 /data/nexus -R

然后启动, 访问 ip:8081 即可

docker compose up -d

配置 caddy

mirrors.china.12306.work {
	import LOG "/var/log/caddy/mirrors.log"
	@rootOrIndex {
		path /
		path /index.html
		path /mirror.css
		path /.help*
	}

	handle @rootOrIndex {
		file_server {
			hide .git
			root /etc/caddy/pages/mirrors
		}
	}
	reverse_proxy http://100.90.80.3:8081
}

样式我从网易镜像源借鉴来的

目前支持的软件

本文档由 ysicing 收集整理, 希望能对国内开源软件用户有所帮助.

仅列出部分核心软件包:

  • tailscale
  • caddy
  • docker-ce
  • postgresql
  • trivy

为什么没有我用的软件包?因为我暂时用不上, 有好的想法可以联系我.


从赵心童世锦赛夺冠聊聊我的斯诺克情缘

2025年5月17日 07:42
如要阅读全文,点击标题跳转。2025 年 5 月 6 日凌晨 4 点,中国斯诺克运动员赵心童在世锦赛决赛中以 18:12 的比分击败七五三杰之一的老将马克.威廉姆斯,历经将近一个月的赛程,一举夺得世锦赛冠军,成为亚洲首位夺得此殊荣的运动员。仿佛还在梦中一样,难以想象这已经是尘埃落定了的一件事儿。决赛前忐忑的内心感受,至今仍旧在胸中回荡。所幸,历史选择了他,他也不负众望,拿下这一桂冠。在斯诺克领域,最负盛名的三个赛事分别是:斯诺克世界锦标赛、斯诺克英国锦标赛和斯诺克大师赛。如果你一个斯诺克运动员,在职业生涯中拿满这三个大赛的冠军,则会被称作大满贯选手。赵心童在 2021 到 2022 赛季夺得了英锦赛冠军,现在又夺得世锦赛冠军,因此会被称作听牌,只等拿下大师赛冠军,就完成了大满贯。而现在以他的表现,又及正值壮年,因此完成大满贯也只是时间问题。关于比赛本身,已经有太多做视频写文章的人介绍过,我就不再赘述了。单独讲讲自己关于赵心童了解的一些信息。这两天有人翻出来十年前央视的一个纪录片《成为丁俊晖》,我大概在几年前完整看过,其中记录讲解了一群孩子在斯诺克学院,练球追梦的故事。片中讲了几个较有潜质的少年,给我印象比较深的有两个,一个是颜丙涛,一个是赵心童。再过来他们逐渐长大,也慢慢进入到斯诺克赛场,成为职业选手,并开始取得一些亮眼的成绩。这大概就是 2021 年左右,那时候关于赵心童的新闻,褒贬不一,有人被他的球风吸引,有人吐槽他莽夫一个。这些对我而言,都不算是一个严重的问题。当时在某个地方,看到一个言论,使我对赵心童的印象,一直处于不够正面的维度,大概就是说中国的几个年轻小匠,略微有了一点成绩,就不知道自己是谁了,在英国也开始买房之类的。后来也渐渐了解到,赵心童家庭出身本就富足,于是斗志不足似乎也在情理之中,再加上接下来的禁赛,让我对这个年轻人有较大的负面认知。而今重出江湖的赵心童,在世锦赛中的表现,用轻松写意的打法,犀利惊艳的准度,清晰明确的围球,向所有人证明了,在被禁赛的时期,他没有放弃,也没有荒废,相反,不仅准度依旧在线,而且球风更加沉稳,安全球也处理更细腻了。经此一役,让我对他的印象,为之一变。

Nexus3 容器部署3.70.x升级实践-非成功案例

作者ysicing
2025年5月16日 21:09

docker 从 3.70.x 及以下版本升级 3.71.x 及以上版本出现旧数据库不支持错误

官方也很细心给了一份升级文档 upgrading-to-nexus-repository-3-71-0-and-beyond

由于我使用的也是官方镜像,社区也有人给出了解决方案 sonatype/nexus-public#51

友情提示,操作前需要备份持久化数据,避免升级失败,建议先阅读文尾后再参考我的升级

创建 nexus 备份文件

  • 设置-系统-任务-创建任务

  • 搜索 Backup,选择它

  • 创建一下手动触发的任务,备份路径 /nexus-data/backup 就行

  • 创建完任务后,点击 backup 任务运行

  • 触发备份

  • 等待备份完成

不放心,也可以进容器内,看一下:

19:56 ➜  ~ kubectl exec -it pods/nexus3-859bb76886-mc29s -n nat -- bash
bash-4.4$ ls
nexus  sonatype-work  start-nexus-repository-manager.sh
bash-4.4$ cd /nexus-data/
bash-4.4$ ls
backup	blobs  cache  db  elasticsearch  etc  generated-bundles  instances  javaprefs  kar  karaf.pid  keystores  lock	log  orient  port  restore-from-backup	tmp
bash-4.4$ cd backup/
bash-4.4$ ls
analytics-2025-05-16-11-56-22-3.70.1-02.bak  component-2025-05-16-11-56-22-3.70.1-02.bak  config-2025-05-16-11-56-22-3.70.1-02.bak  security-2025-05-16-11-56-22-3.70.1-02.bak
bash-4.4$ exit

示例是 k8s 方式,docker 部署也是类似。

数据迁移

查询对应 nexus-db-migrator 迁移工具

需要下载和你 Nexus 版本匹配的 nexus-db-migrator 工具, 最新版本下载 nexus-db-migrator, 目前最新版本是 3.70.4-02

大概格式如下:

https://download.sonatype.com/nexus/nxrm3-migrator/nexus-db-migrator-<version版本号>.jar

由于我的版本是 3.70.1-02, 选择下载

https://sonatype-download.global.ssl.fastly.net/repository/downloads-prod-group/nxrm3-migrator/nexus-db-migrator-3.70.1-03.jar

可能你对应的版本不存在,可以尝试改 patch 版本,实在不行你就先升级到 3.70.4 最新版本

再次提醒版本一定要匹配上

操作

进入容器内部操作,方便起见, 确保在 /nexus-data/backup 目录下

bash-4.4$ pwd
/nexus-data/backup

下载迁移工具,或者同步到容器内也行(网络不好的情况下)

curl -s -L -O https://sonatype-download.global.ssl.fastly.net/repository/downloads-prod-group/nxrm3-migrator/nexus-db-migrator-3.70.1-03.jar

停 nexus 服务

/opt/sonatype/nexus/bin/nexus stop

执行迁移操作,将数据迁移到 H2 数据库,根据数据量和配置灵活调整 -Xmx4G -Xms4G 值,这里条件有限默认 4G, 执行完成如下图

java -Xmx4G -Xms4G -XX:+UseG1GC -XX:MaxDirectMemorySize=28672M -jar nexus-db-migrator-3.70.1-03.jar --migration_type=h2

再次停 nexus 服务

bash-4.4$ /opt/sonatype/nexus/bin/nexus stop
Shutting down nexus
nexus is not running.

查看迁移的数据

bash-4.4$ ls -ahl nexus.mv.db
-rw-r--r-- 1 nexus nexus 320K May 16 12:19 nexus.mv.db

将迁移的数据文件复制到 db 文件夹中

cp nexus.mv.db /nexus-data/db

然后退出容器

销毁容器

  • compose 部署
docker compose down nexus3
  • k8s 部署副本改成 0
kubectl scale --replicas 0 deploy/nexus3 -n nat

编辑配置文件

为啥不在容器里编辑,啥工具都没有不方便,而且这个配置持久化了

  • 默认配置如下 old
root@nat3:/data/k8s/local/nexus3/etc# cat nexus.properties
# Jetty section
# application-port=8081
# application-host=0.0.0.0
# nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml
# nexus-context-path=/${NEXUS_CONTEXT}

# Nexus section
# nexus-edition=nexus-pro-edition
# nexus-features=\
#  nexus-pro-feature

# nexus.hazelcast.discovery.isEnabled=true
  • 新增 nexus.datastore.enabled=true, 新配置如下
root@nat3:/data/k8s/local/nexus3/etc# cat nexus.properties
# Jetty section
# application-port=8081
# application-host=0.0.0.0
# nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-http.xml,${jetty.etc}/jetty-requestlog.xml
# nexus-context-path=/${NEXUS_CONTEXT}

# Nexus section
# nexus-edition=nexus-pro-edition
# nexus-features=\
#  nexus-pro-feature

# nexus.hazelcast.discovery.isEnabled=true
nexus.datastore.enabled=true

启动老服务,验证数据库切换成功

  • compose 部署
docker compose up -d
  • k8s 部署副本改成 0
kubectl scale --replicas 1 deploy/nexus3 -n nat

观察日志,搜索 H2

2025-05-16 12:33:15,378+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.datastore.DataStoreConfigurationDefaultSource - Loaded 'nexus' data store configuration defaults (Embedded H2)
2025-05-16 12:33:15,710+0000 INFO  [FelixStartLevel] *SYSTEM com.zaxxer.hikari.HikariDataSource - nexus - Starting...
2025-05-16 12:33:16,018+0000 INFO  [FelixStartLevel] *SYSTEM com.zaxxer.hikari.HikariDataSource - nexus - Start completed.
2025-05-16 12:33:16,020+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.datastore.mybatis.MyBatisDataStore - nexus - Loading MyBatis configuration from /opt/sonatype/nexus/etc/fabric/mybatis.xml
2025-05-16 12:33:16,147+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.datastore.mybatis.MyBatisDataStore - nexus - MyBatis databaseId: H2
2025-05-16 12:33:16,346+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.datastore.mybatis.MyBatisDataStore - nexus - Creating schema for UpgradeTaskDAO

升级版本失败

直接替换镜像升级就完成了,然后我的数据就没了 😂, 严格按照官方文档来的哇 😂

后续

  • 云缘生镜像站暂停营业几天
  • 虽然有备份数据,但是不想回滚了,重新部署,数据存储使用 PG 吧

zblog输出热门标签

2025年5月16日 17:41

zblog输出20个热门标签

代码

{php}$tags = $zbp->GetTagList('','',array('tag_Count'=>'DESC'),array(20),'');{/php}
{foreach $tags as $tag}
<a href="{$tag.Url}" rel="tag" title="{$tag.Count} 篇文章">#{$tag.Name}</a>
{/foreach}

其中数量可以自行修改代码中的20,改成你想要的数量

参数说明

标签网址:{$tag.Url}
标签名字:{$tag.Name}
标签下文章数量:{$tag.Count}

Caddy拦截响应处理小记

作者ysicing
2025年5月15日 20:34

简单小记,分享一下,主要还是针对 MinIO 异常的处理,其他服务应该也类似

之前配置 MinIO:

xxxx {
        import ERR
        import LOG "/var/log/caddy/minio.log"
        @rootPath {
                path /
        }
        handle @rootPath {
                respond "EdgeONE 403 Forbidden" 403
        }
        reverse_proxy 127.0.0.1:9000
}

仅禁止访问/,且有问题使用 import ERR 兜底

(ERR) {
	handle_errors {
		redir https://dxgw-{err.status_code}.external.ysicing.net
	}
}

之前没太注意,之前这种情况下,如果 MinIO 放回 400 的话是没法拦截的。这时候需要响应匹配器来干活了

域名 {
		import LOG "/var/log/caddy/minio.log"
	    import ERR
        @rootPath {
                path /
        }
        handle @rootPath {
                respond "EdgeONE 403 Forbidden" 403
        }
        reverse_proxy 127.0.0.1::9000 {
	     		@error status 4xx
	     		handle_response @error {
 						respond "EdgeONE 451 Forbidden" 451
				}
		}
}

handle_response 块内,可以使用任何其他指令,功能还挺强大的


因为一个小玩意又写了另一个小玩意

2025年5月15日 17:29

liusheng

blogscn.fun 一直在改版,最近遇到好多人的rss用php抓取不了,就用py写了一个api,然后就有了上面的产物。

全前端没有后端,用了两个api,一个是rss 抓取的api,一个是头像的gravatar。本来到这里就结束了,订阅的信息全部存储在浏览器缓存。

然后,有个东西加载不出来,清理了一下缓存,然后添加的订阅就,没了!
这就有了下面的折腾,要存就需要登录,需要后端怎么能,不用就存呢?于是乎发现了BaaS,即后端即服务,数据库,验证,存储等。
这不就完美了么!
所以,这个站点只有一个文件 index.html

2025-05-15T09:28:50.webp

地址:https://liusheng.maozi.io/

哎,又一电子垃圾!

天啊!我喝了四天啤酒,居然还发现了人生的意义!

作者Jdeal
2025年5月15日 13:00

真的很久没写东西了,工作忙得我晕头转向,每天都在各种打转。

不过五一假期来啦,我终于能喘口气,和朋友们、发小们聚在一起,连续喝了四天,太爽啦!

说到啤酒,我觉得白啤和精酿啤酒都还不错,比那些普通的工业啤酒有意思多了,每一口都超有惊喜。

我最近特别爱喝青岛白啤和福佳白啤。

青岛白啤就像个调皮的小家伙,看起来有点朦胧,倒进杯子里,泡沫超多,而且能坚持好久。喝一口,麦芽的甜味和淡淡的香味混在一起,就像刚切开的香蕉,还带点微微的苦味,味道层次超丰富。

福佳白啤就更特别啦,里面加了橘皮和芫荽籽,喝起来特别好喝,还有点特别的味道,特别清新。喝一口,整个人都轻松了,感觉超棒!

我还喝了好多精酿啤酒呢。双合盛的小麦精酿,麦芽的香气特别纯净,喝起来顺滑又清爽,特别棒。还有小象的德式小麦精酿,麦香特别浓郁,口感醇厚,喝起来特别过瘾。

总之,啤酒就像朋友一样,总能找到适合自己的那一款。

如果你也喜欢啤酒,快给我推荐推荐吧!

说不定我也能找到属于你的那一杯快乐呢!🍺

*Ps 这两天领导不在,划划水,上博客看看有没有什么可以折腾折腾的,哈哈!

看到X上的一段话描写中年打工人的状态,太真实了!
“身边很多朋友,几乎都在步入社会几年后,明显感觉自己的心气被剥削了,疲意,麻木,无欲无求。
上班就是为了过周末,没心情搞兴趣爱好,打开书读不进去,躺着玩手机刷短视频。
说是爱钱,但也没有狠下心赚钱,浑浑噩噩,失去所有探索欲和分享欲。
学生时代大家经常一起谈论文学、电影、旅行、音乐、爱情,后来聊天主题只剩下吃饭、加班、赚钱、家庭,活得像个空心人。
贾樟柯写,“生命到这个地方就不再有奇迹出现,剩下的就是和时间做斗争的一种庸常人生。”
作为大多数的我们,有太多现实需要理清,飞速的时间,普通的自己。开始成为社会的螺丝时代的燃料,理解并成为小时候排斥的无趣的人。
不再想什么意义不意义什么高级不高级了,吃饭就吃饭,打牌就打牌,喝酒就喝酒,我做很多事,只是为了驱散生活的倦怠。”

Nook简约主题

2025年5月14日 20:41
Nook简约typecho单栏主题.webp

Nook

在喧嚣的网络世界,Nook为你打造一处专注表达的宁静之地。极简的设计、温柔的色调、流畅的阅读体验,让每一篇文字都如角落里的微光,安静却充满力量。

支持自定义字体 支持邮箱提醒 拥有文章归档 友链 后台文章类型展现在归档上

  • 归档使用 Archives.html
  • 友链使用 Links.html
  • 关于使用 About.html
  • 留言使用 Lving.html

演示

{button href="https://demo.typecho.work/?theme=Nook" type="blue"}在线预览{/button}

下载主题

开源地址:https://github.com/JaneLens/Nook

THYUU/星度主题 25.5.0 更新内容

作者
2025年5月13日 20:46

全局

  • 【优化】全局动态面板和页脚区域的背景虚化,减少其饱和度
  • 【优化】由于 Chrome 和 Edge 开始内置思源黑体,考虑资源消耗,全局默认字体不再引入在线字体
  • 【优化】适当增加全局动态面板按钮的点击区域,防止没有点击中心位置而导致的问题
  • 【修复】当点击评论表单时,整体评论区块在 CSS 滚动驱动动画变小的问题

顶栏

  • 【新增】整体动态展现,通过新设计的动效在下滑时隐藏,上滑时显示
  • 【新增】标识下滑常驻显示并伴随渐变高斯模糊效果,更好突出品牌
  • 【新增】标识中主标题(默认站点名称)现支持 HTML 结构,可插入 SVG 增强风格
  • 【优化】自定义标识高度受到 CSS 默认值的影响方式,现在修改 SVG 中的 height 属性值可自定义高度
  • 【优化】文章目录和全局动态面板的弹出式窗口逻辑
  • 【修复】刷新后顶栏状态重置的问题
  • 【预告】未来顶栏将会升级风格更现代的智栏,将集成全局消息中心、页面进度动效、文章标题动态、增强交互工具、AI 交互等,敬请期待

首页

  • 【优化】精选分类的文章卡片调整样式
  • 【优化】焦点板块的自动滑动居中支持小屏设备
  • 【优化】限定主题图片过暗时影响顶栏的观感
  • 【修复】收缩布局下限定主题封面图不全屏的问题

文章页

  • 【新增】通用文章模板首屏文章内容动效
  • 【优化】重新调整 H2-H6 文本区块字体大小,使其区分更加明显
  • 【优化】略微调整封面图高度
  • 【优化】在封面图标题处,现在背景虚化跟随标题内容适应
  • 【修复】当封面图过浅时,导致在暗黑风格下看不清的问题
  • 【修复】收缩布局下封面图不全屏的问题

分类页

  • 【新增】文章描述部分,让访客快速了解文章概要
  • 【优化】标题字体大小,追随 H6 字体大小
  • 【优化】调整文章分类和标签位置
  • 【优化】略微调整封面图高度

THYUU 区块

  • 【新增】现在支持 Live Photo,让生活的每一个瞬间都值得纪念

如何正确拉取超大体积的git项目——开发小技巧

2025年5月14日 15:48

最近遇到一个体积相当大的仓库(40GB+),结果在安利给群内小伙伴时,很多人遇到了直接使用 git clone 克隆仓库到本地时,对本地网络的稳定性有巨大挑战,一旦网络波动导致传输中断,就只能从头下载。
很多人拉项目时习惯git clone一把梭哈,这并不是一个很好的习惯,众所周知国内访问github属于玄学,而且有些项目非常大,搞不好需要好几个小时才能拉取完,长时间+不稳定的网络,很容易出意外。本文就是介绍两种应对超大体积项目的拉取的办法「Git LFS」和「Git fetch」。


Git 的 git clone 命令本身是不直接支持断点续传的,git clone 本质上是创建一个新仓库,并一次性拉取远程所有数据(包括提交历史、分支、标签等)。如果中途中断本地目录会处于不完整状态,直接重试 git clone 会失败,如果你退出终端本地目录也会被清空。

使用 Git Large File Storage(Git LFS)

简单介绍一下 Git LFS

Git Large File Storage(Git LFS)是一个用于管理大型文件的Git扩展工具,旨在解决Git原生对大文件支持不足的问题。

Git LFS 优点

  • Git LFS用轻量级文本指针(如oid sha256:…)替换实际大文件,避免仓库臃肿。
  • 大文件内容存储在专用服务器(如GitHub LFS、块储存、自建储存、CDN),仅按需下载。
  • 支持单个文件GB级别存储,突破Git对文件大小的限制。(比如 github 是单文件小于100MB,单文件大于50MB会出现警告)
  • 因为大文件实际是分离储存的,可以优化拉取速度。
  • 安装Git LFS 并配置好后,日常 git 操作时,几乎是无感的,Git LFS 会自动处理大文件。

Git LFS 缺点

  • 需要项目创建者配合从一开始就使用 Git LFS,如果项目没有采用,对于想要克隆拉取项目的人是没有办法去使用 Git LFS 的。
  • 额外的大文件储存和下载都是需要开发者额外掏钱的,像是GitHub就只有10 GiB的免费空间和流量可用,超过的部分需要额外收费。(如果你是pth之类的AI模型文件,huggingface之类的平台给了免费的 LFS 空间)

不要 git clone 一把梭哈,改用 git fetch 分步拉取

简单介绍一下 git fetch 分步拉取

Git LFS 更多面对的是项目中部分文件体积很大的情况,比如Unity项目中的.asset文件、PSD源文件、CSV数据集。但还有一种情况,项目中每个文件体积都不大,可能只有几兆到十几兆,但是项目中文件数量极多,几百上千个,加到一起后整个项目体积也到了 GB 规模。这个时候更适合分步拉取的方式。

和 git clone 相比 git fetch 的优势

  • git fetch 基于 Git 对象模型,天然支持断点续传。
  • 即使多次中断,只要本地 .git 目录未损坏,均可通过反复执行 git fetch 逐步补全数据。
  • 如果中断期间远程仓库新增了提交,git fetch 会同时下载新增内容和未完成的旧内容,最终保持本地与远程一致。

和 git clone 相比 git fetch 的缺点

除了操作稍微多了几个步骤,没任何额外的缺点。

git fetch 分布拉取的方法

  1. 先手动创建空仓库并构建.git文件
mkdir your-repo && cd your-repo
git init
git remote add origin https://github.com/user/repo.git
  1. 分步拉取数据
git fetch origin --progress  # 如果中间传输中断,就在目录下再执行一次这个命令即可。
  1. 等待拉取完毕(以下仅为举例,请根据实现终端显示为准),这时候项目所有内容都会被打包在.git/objects/pack目录下的.pack文件内。
Resolving deltas: 100% (9999/9999), done.
From https://github.com/user/repo.git
 * [new branch]      master     -> origin/master
 * [new tag]         v1.0       -> v1.0
 * [new tag]         v1.1       -> v1.1
 * [new tag]         v1.2       -> v1.2
 * [new tag]         v2.0       -> v2.0
 * [new tag]         v2.1       -> v2.1
 * [new tag]         v2.2       -> v2.2
 * [new tag]         v2.3       -> v2.3
 * [new tag]         v2.4       -> v2.4
 * [new tag]         v3.0       -> v3.0
  1. 将需要的分支检出,比如我们检出 master 主分支。
git checkout master
  1. 其他优化建议

使用浅克隆减少首次下载量,比如只拉取最新分支

git clone --depth 1 https://github.com/user/repo.git

之后再补全历史(或者就干脆不补全,节约点空间)

git fetch --unshallow

总结

总的来说 Git LFS 主要是应对项目中出现几个巨大体积文件的情况,git fetch 主要是应对一次开机无法完成下载的超大型仓库或不稳定网络环境,比直接 git clone 一把梭哈更加灵活。

PS:顺带提一嘴,如果多次中断和重新拉取,可能会在.git/objects/pack目录下出现很多个.pack文件。如果需要清理的的话,在项目目录下执行git gc --auto
再PS:不用在意.pack文件的巨大体积,因为工作目录下你看到的具体文件,是通过硬链接、符号链接构建的,工作目录内的文件和 .pack 中的对象共享同一份数据,并不会真实占用双倍物理空间。实际占用大概是1.2~1.3倍空间,因为.pack文件还是有一定的压缩的。

The post 如何正确拉取超大体积的git项目——开发小技巧 appeared first on 秋风于渭水.

CodeBuddy腾讯云代码助手初体验

作者ysicing
2025年5月13日 19:52

本文算个安利吧,目前比 trae 实在,至少不是老是排队,暂时免费使用。

CodeBuddy 是什么

腾讯云出品的代码助手插件, 支持 VSCodeJetBrains微信开发者工具

对比 Cursor

对比我用的 cursor,目前有两大优势

  • 暂时免费(Craft 对话限制 50 次/月,很容易用完的)
  • Craft 支持从用户需求转到研发需求,最后拆分成迭代执行(很早之前,就跟同事讨论过这个问题,这个应该是未来发展的趋势)

安装

插件安装教程, 安装完成后,扫码登录

使用

简单演示,空仓库如何写一个 caddy 插件

mkdir go/src/github.com/ysicing/caddy2-admin-ui
cd go/src/github.com/ysicing/caddy2-admin-ui
git init
go mod init

配置助手,默认全启用

  • 用户需求 - 研发需求

  • 研发需求 - 迭代执行

  • 迭代 1 - 迭代 n

可能问题

除写了"垃圾代码"外,偶尔不会重构代码外,Agent 执行命令提示没法获取结果,需要 ~/.zshrc 新增如下配置

[[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path zsh)"

总结

用的不多,偶尔用用还行,更多的还得关注他们后续的付费计划。


说说:#1747109233

2025年5月13日 12:07

我又想出去玩了,之前去的西安,这玩一下上瘾了,想着去更远的地方旅行。

离我比较远的选择,一个是新疆,一个是西藏,一个是广东,感觉广东是要必去的,不是有一句话,食在广东。

另外出国的话,一个是日本一个是东南亚。太远的暂时也不去想了,出国先去近一些的。

日本的话,以前真的想去,但是现在不知怎么的感觉脱敏了。现在更倾向去东南亚看看。东南亚比较乱,感觉马来西亚会很不错,到时候计划一下嘿嘿😁

store站点被人扒了,虽然事件解决了但是还是让人恶心

2025年5月13日 11:22

{cat_tips_warning color=""}事件已解决,记录个人心情,全程打码,请勿对号入座,评论区切勿人身攻击{/cat_tips_warning}

store站点被人扒了,不是借鉴不是抄袭,而是直接复制粘贴。虽然事情解决了但是过了两天想起来还是非常让人恶心,我必须把这个事情写出来心情才能好受一些。就当作记录一下心路历程水一篇文章,因为事件终归是解决了所以本文出现的内容全都打码了,请勿对号入座。


起因是在一个明媚的午后,我像往常一样打开TSBC群去群里水表情。看到一个新进群的,昵称是“某某typecho主题工作室”,想必也是卖主题的,心里想去看看有没有有趣的主题找找灵感。结果一进去,预加载的文字感到似曾相识,结果一进去,果不其然,我的网站被扒了。于是心里很气愤,但是想着这store站我也不用来卖,不过如果是他自己用的话,说明我的网站也是被大家所认可的,也是挺好的,想着就算了,想着让他既然扒的我的,就让他在网站底部写上模板出处就完事了,比较都改完了。于是我就去群里艾特他,问他怎么回事。本以为对方会说一声抱歉,夸夸我的主题好看于是就拿来用了,我也不会说些什么,但是却一直没有回复。并不是没看群消息不知道我艾特,而且还迎面发b站推广链接,这下就让我从平缓的心态一下子就生气了。

但是更让我意想不到的来了,点进他发的b站推广链接,标题是“Typecho主题之XXX主题展示”,在评论区我赫然看到了以下内容:

woc,用我的模板然后说是自己的?这能忍?于是我又去群里问他咋回事,这时他回我了:

他的意思是他加了一个标签页,就不是扒了,复制粘贴不叫扒叫借鉴了,可是你知不知道你这复制粘贴连抄袭都算不上啊。然后这人又不理我了,我压制住火气加了他好友说,来友好的解决一下吧。

下面我贴上和那个人的私聊记录。

私聊记录

实话没想那么多,当初只是看见设计挺有思路。 所以就写了!

一开始为了缓和气氛,我还特地发了一个委屈的表情,对面回答也很友好,说设计很有思路就拿去用了,但是看他的语气并没有道歉的意思,而是看得上你的主题我拿来用是你的荣幸。

然后我就说,你说的是借鉴思路,而你做的不是借鉴思路而是复制粘贴,结果不知怎么的让对面有些语无伦次,而且说了让人感到很莫名奇妙的言论,他发了这一大段,属实是把我气笑了:

我说了不是特意去想做什么坏事,如果真要我也可以去改CSS名和JS名

你说你不去做坏事,那你拿我的主题官上自己的名字,然后别人问你还说没有关系,是你自己开发的?你没改css还不是因为让我发现了?

我之前设计的也一样被人仿过,我群也有很多用户。我也没瞎说什么

你的意思是别人抄了你的,你就可以抄别人的了?我指正出来是我在瞎说?是我无理取闹?

所以现在你老说的我好像要怎么样似的

你扒了我的主题,一句道歉也没有,还觉得自己很无辜?你不知道自己应该做些什么么?

我就纳闷了 说实话我也还知道有人转了你的 改成wp了
但大家的出发点并不是你说的那样

用“大家”这个词,来主张自己的正义性,【因为“大家”都是这么说的,你来挑刺一是说我无理取闹,无事生非;二是为自己开脱,毕竟“大家”都这么做,我这么做即使不对也是情有可原的?】

然后在我没缓过气的时候补上了一句

懂我意思吗?

他首先表明了自己是正义的一方,然后开始为自己辩解找理由:

你老觉得我在别人那说错了?
我的写法我也不确定跟你一样吗
我后端确实也是typecho但设置全是json解析
所以这个东西我在B站回复 我站的角度是后端 不是前端

不是哥哥,后端谁能看得到啊,而且一直在说的是前端啊,就算你说你的意思是后端,你自己的视频展示的不就是前端么?你展示一丁点后端的内容了么?

然后又来了一句反问,让你自己思考人生:

你自己也清楚 并非你说的我好像在特意说什么

最后他可怜我,回答道:

如果你觉得不行
我到时候抽空 全写过
好吧,这样可以了吧
确实一开始没想那么复杂 就是想做个站 真是有那些坏心思

因为我在挑刺,所以他为了照顾我,所以他说他抽空重写前端,这样我就不会再找他茬了


后面的我也不说什么了,图片里都有。后续就是我让他加上我的跳转链接,他也加上了,然后第二天他就重写了前端,链接也下了,至此至终一句道歉也没有,一句友好的对话也没有,我还得没事卖个萌发个表情缓和气氛。

昨晚看了一眼TSBC群,不知怎的狐狸让他当管理了,也许人家互相认识或者,希望他今后能对得起自己网站的标语:“创意无界,设计有魂”,而不是再扒别人的,日后能对Typecho的发展有些许贡献吧...

259、立夏

2025年5月13日 10:38

勒多曼因湖边

立夏时节,熏风南来,天地始交,万物并秀。当夜卧早起,迎朝阳以养心气。晨起可临水而立,观荷听蝉,导引阳气,使志无怒。饮食宜增酸减苦,多食樱桃、青梅、新麦以养心脾,佐以莲子、百合清心宁神。

此时心火渐旺,肾水初衰,当静心寡欲,戒骄戒躁,常持虚静。衣宜选用苎麻丝帛,透气生凉,尤需护住后颈命门。常饮绿豆薄荷饮,解暑生津,兼防疰夏。运动当选五禽戏、太极等舒徐功法,如树展枝,似水潺湲,使气血流通而不沸涌。午后小憩一刻,胜服灵丹,此谓「养长之道」。

立夏这天刚好在山上没网,不过在山上的时候还想着要发节气文章,下来后光顾着P照片去了,补发一章。

如何让老旧打印机支持隔空打印,在Mac上搭建Airprint服务,让USB打印机支持iPhone打印

作者张洪Heo
2025年5月12日 16:26
这篇文章介绍了如何通过AirPrint服务解决打印机无法进行隔空打印的问题。用户面临打印机不支持隔空打印的情况,因此需要手动搭建AirPrint服务以实现打印机共享。文章分步指导了安装打印机驱动、开启共享打印机功能、安装并运行AirPrint脚本服务,以及在测试和故障排除过程中需要注意的事项。文章还包括了GitHub项目的链接,以供用户参考。内容简洁明了,操作流程清晰,适合macOS用户的实际操作。

新版Mac系统安装旧版惠普打印机驱动教程,macos15安装惠普打印机驱动

作者张洪Heo
2025年5月12日 15:23
这篇文章介绍了解决惠普打印机驱动安装问题的步骤。当用户尝试从惠普官网下载驱动文件时,由于兼容性问题无法成功安装。通过以下步骤可以实现驱动的正常安装:1. 下载并安装Pacifist驱动程序(可通过指定链接获取);2. 打开Pacifist并解压下载的打印机驱动包;3. 使用Pacifist引导安装程序(需操作系统提供完整权限);4. 输入相关系统信息,完成安装过程。完成后,打印机驱动将暂时不可见;通过系统设置可重新找到并配置该驱动。建议在安装完成后及时检查系统设备列表以确认驱动是否成功识别。

Zerotier自建planet,Mac和istoreos自组网教程,实现远程访问家庭内网服务

作者张洪Heo
2025年5月12日 14:15
这篇文章介绍了在FRP中转架构下,因子设备无法实现直连导致的访问问题。通过部署ZeroTier自组网,作者为家庭NAS实现了直连访问。针对FRP子设备无法进行直连的痛点,作者基于自建ZeroTier Planet服务器,实现了跨网络设备直连。文章详细描述了部署ZeroTier的步骤,包括Docker容器的配置、防火墙的端口开放、网络的创建和IP地址的分配等关键步骤,并附有MacOS及istoreOS客户端配置方法。通过这种方式,非IPv6环境下只需将设备连接到已经配置好的HDD或NAS即可实时访问。文章除了描述部署ZeroTier的完整操作流程外,还提到了如果家里和外网均存在IPv6,则无需ZeroTier服务,用户可以在公司Mac使用零时序服务(零时序)实现mac与NAS的直连。

ImageFlow一款更适合个人使用的图床项目

作者ysicing
2025年5月11日 22:45

ImageFlow 是一个为现代网站和应用程序设计的高效图像服务系统。它能根据设备类型自动提供最合适的图像,并支持 WebP 和 AVIF 等现代图像格式,显著提升网站性能和用户体验。

主要特性

  • API 密钥认证:安全的 API 密钥验证机制,保护您的图片上传功能
  • 自适应图像服务:根据设备类型(桌面端/移动端)自动提供横向或纵向图片
  • 现代格式支持:自动检测浏览器兼容性并提供 WebP 或 AVIF 格式图片
  • 图片过期功能:支持设置图片过期时间,过期后自动删除(支持本地和 S3 存储)
  • 简单的 API:通过简单的 API 调用获取随机图片,支持标签过滤
  • 用户友好的上传界面:支持拖拽上传,具有暗黑模式、实时预览和标签管理功能
  • 图片管理功能:通过直观的管理界面查看、筛选和删除图片
  • 自动图像处理:上传后自动检测图像方向并转换为多种格式
  • 异步处理:图像转换在后台进行,不影响主服务
  • 高性能:优化的网络性能以减少加载时间
  • 易于部署:简单的配置和部署流程
  • 多存储支持:支持本地存储和 S3 兼容存储(如 R2, 不支持MinIO
  • Redis 支持:可选的 Redis 集成,用于元数据和标签存储,提高性能

项目地址

快速部署

使用 docker compose 快速操作

  • 镜像: soyorins/imageflow
  • 国内镜像: ccr.ccs.tencentyun.com/k7scn/imageflow
git clone https://github.com/Yuri-NagaSaki/ImageFlow && cd ImageFlow

更新配置文件

cp .env.example .env
nano .env
# 主要修改API_KEY和存储方式,想快速体验建议填写local

示例.env

API_KEY=ohji8lob1Sagoh4shizooNe9oxif9pai
STORAGE_TYPE=local
LOCAL_STORAGE_PATH=static/images
CUSTOM_DOMAIN=
MAX_UPLOAD_COUNT=20
IMAGE_QUALITY=75
WORKER_THREADS=4
SPEED=5
WORKER_POOL_SIZE=4
# Debug Mode
DEBUG_MODE=false

启动

docker compose up -d
  • 目前版本存在 panic 问题,等后续版本修复, 可以使用 soyorins/imageflow:2.1.1 版本

常见参数设置

  • API_KEY, 用于保护上传和管理接口, 推荐使用 pwgen 32 1 生成
  • IMAGE_QUALITY, WebP 转换的质量设置, 数值范围 1-100, 越高表示质量越好,文件越大
  • SPEED, 范围:0-8 0-8,0=最慢/最高质量,8=最快/最低质量

如何使用

  • 打开 IP:8686 端口 输入你在 env 设置的 API_Key

  • 上传图片, 支持选择图片的过期时间,添加标签对图片进行分类,图片会自动转换为 WebP 和 AVIF 格式

  • 当删除图片时,所有相关格式(原始、WebP、AVIF)将同时被移除

用户外活动填满五一:瓦屋山篇

2025年5月11日 10:18

瓦屋山

今年的五一感觉是有生以来活动安排的最满的一次。

先是五一假日前老婆把小舅子和大姨妈摇来了自贡,28号落地,带他们吃喝玩乐,30号再去了一次瓦屋山徒步。他们2号走后,3号又马不停蹄的去了康定勒多曼因爬山,来回42公里,最高海拔4600米,5号晚上十一点半才重新回到自贡,我到现在都还有点没缓过劲来。

大姨妈不是第一次来了,小舅子倒是首次过来玩,所以老婆先是安排他们去了上次我们两个人都好评的「电子厂餐馆」——一个本地人都不一定找的到地方的苍蝇馆子,要从主路拐进一条小路,进去走个一两公里才能看到。

小路开了一两公里左右,在这个尽头右手边小巷子里

小店需要提前预订,不接直接过去的客人,菜都是非常新鲜的。招牌菜是腰花和牛蹄筋,整体口味都是偏重油重辣的,小舅子和大姨妈吃完后两个人在自贡待了几天就打了几天「标枪」。

腰花非常嫩,里面配菜用了一些类似油渣的东西吃起来非常香(不知道是不是勾芡掉落焦化的)。牛蹄筋则是他们的招牌,炖的非常软烂又略带胶原蛋白的口感,强烈所有外地来的朋友过来品鉴。

1. 牛蹄筋 2. 腰花

老婆之后请了假带他们去看了自贡灯会和恐龙博物馆,这部分我要上班就没参与,也就没给他们拍照流念了。

因为瓦屋山风景绝美,这段时间又免门票,所以30号当天我们准备开车带他们去感受一下四川的山川河流和我们最近喜欢上的户外徒步。

出发!
瓦屋山位于四川省眉山市洪雅县境内,海拔2830米,是中国最大的平顶桌状山,被誉为“云霭之上的诺亚方舟”。这里拥有原始森林、高山瀑布、杜鹃花海等自然奇观,是世界第二大、亚洲第一大的桌山,与南非开普敦的桌山齐名。山顶平台面积约11平方公里,植被覆盖率超过90%,是珍稀动植物的天堂,如珙桐、大熊猫等。四季景色各异:春赏杜鹃、夏避酷暑、秋观彩林、冬览冰雪,尤其冬季冰瀑堪称一绝。道教文化底蕴深厚,传说为太上老君升仙地,清代曾是川内著名道教圣地。现为国家AAAA级旅游景区,兼具生态与人文价值。

瓦屋山是目前我去过的四川普通爬山类型景点中除九寨沟外资源最全面,最好的一处景点了。

下了高速后还要行使大概20公里左右的盘山公路,最后5公里左右进入景区范围,整个山区被密集且高大的原始雪松森林覆盖,小路在森林中盘旋前进,可以说非常有欧洲或者日本那边原始森林的感觉了。(一路上都是盘山公路,晕车的小伙伴要注意提前吃晕车药)

不多时便能穿山而出看到一个湖,这便是山下一处名为雅女湖的景点,环湖有一条公路,马路边上都是民宿和餐馆。

到达景区大门口时不要停。

我们当时停了,发现到里面游客中心还要一公里左右,又重新上车往里开,最后停在游客中心下的停车场里,20元一天。

准备爬山。

出发

山上的负氧离子非常高,溪流沿着峡谷而下,闻着好闻的空气感觉身体都轻飘飘的。

在山上看到了佛光,后来在朋友圈发现四川这几天到处都能看到这个景象。

佛光,像是被人注视。

路边的溪流和「生命力」照片。

1. 生命力照片 2. 溪流

自拍一个

自拍

1. 一只胖胖的熊蜂把花枝都压的下坠了 2. 步道

在溪边堆了个玛尼堆祈福

1. 清澈的湖水 2、3. 瀑布留影

自拍

山上瀑布资源丰富,而且落差都非常大。

1. 山上的瀑布2 2. 生命力照片 3、4. 自拍

小舅子和大姨妈平时都是不怎么锻炼的,一路上又流连于美景拍照,所以走走停停五六个小时才到金花坪,状态也几近掏空的状态。虽然我们状态还好的很,刚刚热身的感觉,但在询问了工作人员后发现下午4点以后就不准继续往上了,而且最后一班下山的缆车也会在4点还是5点后停运,所以只能坐缆车下山。

其实我们两口子是想快速冲顶的,因为非工作日前100名登顶的客人会送一块奖牌,不过今天肯定是拿不到了,只有下次再来了。

瓦屋山的缆车非常长,几乎坐了十来分钟才到站,想想上次峨眉山七八十的票价几分钟就到了。这么远的距离冬天不敢想象这一路会有多美,而且每站只要50元。

下山
自拍
分两篇写把,下一篇 勒多曼因篇。

大学时光回忆录

作者Innei
2025年5月11日 01:57
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/notes/190

有时间整理了下 iCloud 相册,删除了许多没有留恋价值的图片。回顾老照片,感受到当时的感动和喜悦,现在看来也是一种珍贵的回忆。

这段回忆从大学开始,再之前的也找不到了。

2018 年,大一。

小米笔记本 + 黑果,踏上开发之路。

2019 年,大二。开始学习前端,刚开始,学的是 Vue2 + Epxress。

2019 年底,找到了一个 Remote 的实习机会。学习 React。然后在这条路上越走越远。

2020 年,经过了半年疫情和实习,在下半年返校季,凭自己的努力换上了 Macbook Pro,黑果转正。也买了人生第一台游戏机,Nintendo Switch。

2020 年的十月,原神上线了。而我在沉迷塞尔达传说旷野之息。

2020 年底,我开始写 Mix Space,一写就是 5 年。

我把这个小窝,布置的很好,这也是我最快乐的快乐的一段时间。

现在想起来前司给我过生日还是挺感动的。2021.4.1

我真的很早就有在写 Swift UI 了,虽然那时候和 Lakr 还没有和见过面。于 2021 年 4 月在学校图书馆。

2021 年劳动节,第一次来杭州,见到了 Lakr。准备前往蚂蚁实习的预备。

2021.7.7,第一次租房,在杭州,三墩,单间,2750。现在回看真是被割惨了。

2021.8 底,离开了,这两个月过得非常煎熬,一点都不快乐。

后面就是秋招了。

https://innei.in/notes/104

在后面我就毕业了。

大学时光匆匆。

看完了?说点什么呢

你要学会合群,否则就会被世界抛弃

作者
2025年5月10日 23:41

引言

“你要学会合群,否则就会被世界抛弃”,这样的告诫像紧箍咒般萦绕在每个人的成长过程中。当我们站在成年人世界的门槛前,这个问题却显露出更为深刻的悖论——合群意味着自我消融于集体狂欢中,而刻意不合群又如同在茫茫人海中举起一面孤绝的旗帜。但真正的智慧在于超越这种非此即彼的二元对立,寻找第三条道路:在保持个体完整性的前提下,与世界建立恰如其分的连接。

现代社会的群体焦虑催生了畸形的合群文化。在路口的人潮如同精准运转的齿轮,每个人都面无表情却保持统一节奏。深夜的办公室里,键盘敲击声此起彼伏,格子间里的年轻人与代码共同闪烁着相似的光芒。这样的画面构成了当代社会的经典隐喻——人们害怕被抛弃的恐惧,已经异化成了对整齐划一的病态追求。

心理学研究显示,过度合群会导致”社会认同偏差”,使个体逐渐丧失独立判断能力。《乌合之众》中描述的群体无意识在社交媒体时代被无限放大,朋友圈里的点赞文化、职场中的服从性测试都在强化这种异化过程。我们追求的合群往往不是精神共鸣,而是对主流价值的屈从性认同。

合群

合群的优势

  • ​​资源与机会的拓展​​:合群者通过广泛社交积累“弱关系”,这些关系可能成为职业发展的跳板。
  • ​​归属感与幸福感​​:良好的人际关系是幸福感的决定性因素。
  • ​​降低决策成本​​:随大流的选择能减少独立决策的压力。

合群的弊端

  • ​​自我消解​​:过度合群可能导致个性湮没,例如为迎合群体而压抑真实想法,最终成为“没有棱角”的人。
  • ​​资源浪费​​:合群者可能因攀比陷入无效社交,如为维持人脉被迫参加无意义的聚会,消耗时间与金钱。

不合群

不合群的​优势

  • ​​独立思考与创新突破​​:不合群者往往能跳出群体思维桎梏。
  • ​​专注自我成长​​:独处者可将精力投入热爱之事。
  • ​​避免情绪内耗​​:不合群者无需为迎合他人隐藏真实情绪,从而减少心理压力。

不合群的弊端

  • ​​社会脱节​​:过度不合群可能导致信息闭塞、人脉薄弱。
  • ​​孤独感​​:长期缺乏群体互动可能引发心理问题,甚至诱发抑郁。

结语

树木在森林中既保持根系的独立性,又通过菌丝网络共享养分。这种自然界的智慧启示我们:理想的生存状态应当是”和而不同”。当我们既能保持专业领域的内核稳定,又能在需要时融入多元网络,便实现了对传统身份范式的超越。这不是非此即彼的选择,而是通过持续的身份协商,在流动中共建更开放的价值生态系统。真正的自由,或许就存在于这种进退有度的动态平衡之中。

那些既能享受独处时的深度思考,又能融入群体创造价值的生命状态,才是对抗异化与孤独的最佳姿态。我们不必做燃烧自己的蜡烛,而可以成为既照亮自己也能温暖他人的星火,在浩瀚夜空中书写属于自己的轨迹。

合群与否,不过表象。自洽与否,方为本质。内心丰盈者,独行也如众。

FileCodeBox:告别网盘烦恼,安全高效的文件分享神器

作者ysicing
2025年5月9日 22:13

在日常工作生活中,处理敏感项目文件时,担心文件被第三方泄露; 使用第三方网盘时还要下载客户端(客户端有时还给你偷跑流量,美名加速),有时下载还得开会员。如果有这些问题的话,我强烈安利一款开源利器——FileCodeBox!它开源、好用、安全,完美解决文件分享的各种难题,让你轻松搞定工作和生活中的文件传输。

开源地址

  • 代码仓库 vastsa/FileCodeBox
  • 镜像地址: lanol/filecodebox
  • 国内镜像: ccr.ccs.tencentyun.com/k7scn/filecodebox

三大优势,解决分享痛点

FileCodeBox 专为文件分享的痛点设计,简单几步就能让你的分享体验焕然一新

超快传输,省时省心

速度取决于你的网络带宽

  • 无大小限制:无论是高清设计稿还是海量项目文件,统统支持,轻松上传
  • 拖拽即传:无需压缩打包,直接拖文件到页面,秒速完成上传
  • 批量分享:支持多文件同时上传和分享,项目交付、团队协作 so easy!

安全可靠,隐私无忧

  • 加密保护:分享链接全程加密,只有指定接收者才能访问,杜绝泄露风险
  • 阅后即焚:支持设置文件过期时间,自动删除,防止资料被长期留存
  • 下载控制:可限制链接使用次数或者有效期,避免文件被恶意传播,敏感资料更安心

极致便捷,随时随地

  • 无需注册:凭码取件
  • 清爽体验:界面简洁

部署简单

要求

以下任选一个就行,我推荐使用腾讯云锐驰 200M,存储使用赠送的对象存储 😄

  • 有公网 IP 的服务器
  • 内网穿透走 cloudflare tunnels

PS: FileCodeBox 也支持对象存储,这里我就不推荐 MinIO 了直接使用本地存储就行,因为这个分享服务我定位是短期临时分享或者按次永久分享。

部署

  • docker-compose.yaml
version: "3"
services:
  filecodebox:
    image: lanol/filecodebox:latest
    # image: ccr.ccs.tencentyun.com/k7scn/filecodebox
    container_name: filecodebox
    volumes:
      - /data/filecodebox:/app/data
    ports:
      - "12345:12345"
    restart: always
  • caddy
kd.012321.best {
        import LOG "/var/log/caddy/kd.log"
        reverse_proxy 10.25.123.1:12345
}

分享文件

  • 打开网页,点击"分享文件"
  • 选择或拖拽文件
  • 设置过期时间和次数
  • 获取提取码

获取文件

  • 打开网页,输入提取码
  • 点击获取
  • 下载文件或查看文本

是不是用 FileCodeBox 分享文件,简单到不可思议。整个过程无需复杂操作,接收方也不需要注册,真正“即传即得”!

管理面板

访问 /#/admin
输入管理员密码 FileCodeBox2023, 登录后请立即修改

管理文件和配置

如果需要发送私密信息,建议自建请勿使用第三方服务,避免不必要的问题

最后想说

在数据安全越来越重要的今天,FileCodeBox 不仅是一款工具,更是一种自由、安全的文件分享方式。它让分享变得高效、可控,完美适配各种场景。如果你也厌倦了网盘的限速和收费,或为敏感文件的隐私问题担忧,FileCodeBox 绝对值得一试!
快部署你的 FileCodeBox,体验前所未有的文件分享自由吧!


记录二五年五一之短暂回归家庭

2025年5月9日 18:10
如要阅读全文,点击标题跳转。打工在外,每年能够回家的机会也就五一,十一和过年这三次了,于是,积累在假期账户里的年假,育儿假基本都会在这些假期中,拼接使用,今年五一,我再次多请了几天,从而得以有一个不短的时间在家里陪老婆孩子。本文拾取一些片段,来记录这一小段生活。

使用 React Native Screens 构建一个 Simple Navigation

作者Innei
2025年5月9日 01:01
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/posts/tech/build-simple-navigation-with-react-native-screens

上回说到,我们已经大概了解了 React Native Screen 内部是如何工作的。这篇文章将综合前面的内容,实现一个简单的 React Native Navigation。

构想

一个 Navigation 最基础的就是要实现一个 navigate 方法。

navigate 方法需要实现前进后退的基本行为。

模拟在 iOS Native 中,我们可以使用 pushController 和 presentController 两个方法去实现前进的行为。

那么我们可以命名为:

  • pushControllerView 推入 Stack Navigation
  • presentControllerView 推入 Modal

然后是后退的行为,命名为:

  • back 回退 Stack Navigation
  • dismiss 关闭 Modal

然后我们需要集中式管理 push 和 present 的 navigation 的数据,然后在 UI 中呈现和反馈。

实现

Navigation 框架

顺着上面的构想,我们首先需要一个 Navigation 的类。

class Navigation {}

我们需要把 navigation 的数据保存在这个类中,所以我还需要定义数据的类型。

export interface Route {
  id: string

  Component?: NavigationControllerView<any>
  element?: React.ReactElement

  type: NavigationControllerViewType
  props?: unknown
  screenOptions?: NavigationControllerViewExtraProps
}

export type NavigationControllerViewExtraProps = {
  /**
   * Unique identifier for the view.
   */
  id?: string

  /**
   * Title for the view.
   */
  title?: string

  /**
   * Whether the view is transparent.
   */
  transparent?: boolean
} & Pick<
  ScreenProps,
  | 'sheetAllowedDetents'
  | 'sheetCornerRadius'
  | 'sheetExpandsWhenScrolledToEdge'
  | 'sheetElevation'
  | 'sheetGrabberVisible'
  | 'sheetInitialDetentIndex'
  | 'sheetLargestUndimmedDetentIndex'
>

export type NavigationControllerView<P = {}> = FC<P> &
  NavigationControllerViewExtraProps

上面我们定义 NavigationControllerView 的类型,和 Route 的类型。NavigationControllerView 用于定义 NavigationView 的组件类型,Route 用于在 Navigation 类中保存 navigation 的数据。

为了实现在 UI 中的响应式,我们使用 Jotai 去管理这个数据。

export type ChainNavigationContextType = {
  routesAtom: PrimitiveAtom<Route[]>
}

在 Navigation 类中初始化数据:

export class Navigation {
  private ctxValue: ChainNavigationContextType
  constructor(ctxValue: ChainNavigationContextType) {
    this.ctxValue = ctxValue
  }

  static readonly rootNavigation: Navigation = new Navigation({
    routesAtom: atom<Route[]>([]),
  })
}

Navigation 数据管理

上面已经定义了 Navigation 的类型,然后我们通过对数据的控制来实现 push/back 的操作。

class Navigation {
  private viewIdCounter = 0
  private __push(route: Route) {
    const routes = jotaiStore.get(this.ctxValue.routesAtom)
    const hasRoute = routes.some((r) => r.id === route.id)
    if (hasRoute && routes.at(-1)?.id === route.id) {
      console.warn(`Top of stack is already ${route.id}`)
      return
    } else if (hasRoute) {
      route.id = `${route.id}-${this.viewIdCounter++}`
    }
    jotaiStore.set(this.ctxValue.routesAtom, [...routes, route])
  }

  private resolveScreenOptions<T>(
    view: NavigationControllerView<T>,
  ): Required<NavigationControllerViewExtraProps> {
    return {
      transparent: view.transparent ?? false,
      id: view.id ?? view.name ?? `view-${this.viewIdCounter++}`,
      title: view.title ?? '',
      // Form Sheet
      sheetAllowedDetents: view.sheetAllowedDetents ?? 'fitToContents',
      sheetCornerRadius: view.sheetCornerRadius ?? 16,
      sheetExpandsWhenScrolledToEdge:
        view.sheetExpandsWhenScrolledToEdge ?? true,
      sheetElevation: view.sheetElevation ?? 24,
      sheetGrabberVisible: view.sheetGrabberVisible ?? true,
      sheetInitialDetentIndex: view.sheetInitialDetentIndex ?? 0,
      sheetLargestUndimmedDetentIndex:
        view.sheetLargestUndimmedDetentIndex ?? 'medium',
    }
  }

  pushControllerView<T>(view: NavigationControllerView<T>, props?: T) {
    const screenOptions = this.resolveScreenOptions(view)
    this.__push({
      id: screenOptions.id,
      type: 'push',
      Component: view,
      props,
      screenOptions,
    })
  }

  presentControllerView<T>(
    view: NavigationControllerView<T>,
    props?: T,
    type: Exclude<NavigationControllerViewType, 'push'> = 'modal',
  ) {
    const screenOptions = this.resolveScreenOptions(view)
    this.__push({
      id: screenOptions.id,
      type,
      Component: view,
      props,
      screenOptions,
    })
  }
}

之后,back 的操作也非常简单。

class Navigation {
  private __pop() {
    const routes = jotaiStore.get(this.ctxValue.routesAtom)
    const lastRoute = routes.at(-1)
    if (!lastRoute) {
      return
    }
    jotaiStore.set(this.ctxValue.routesAtom, routes.slice(0, -1))
  }

  /**
   * Dismiss the current modal.
   */
  dismiss() {
    const routes = jotaiStore.get(this.ctxValue.routesAtom)
    const lastModalIndex = routes.findLastIndex((r) => r.type !== 'push')
    if (lastModalIndex === -1) {
      return
    }
    jotaiStore.set(this.ctxValue.routesAtom, routes.slice(0, lastModalIndex))
  }

  back() {
    return this.__pop()
  }
}

从上面的代码不难看出,其实我们只是通过对数据的操作实现 Navigation 的逻辑。而真正要在 UI 中呈现 Navigation 的效果还是需要通过 React Native Screens 来实现。

Navigation UI 框架

在上面的文章中,我们已经知道了我们只需要通过传入不同 React Children 到 React Native Screens 的 <ScreenStack /> 中就能实现原生的 navigate 的效果。

那我们现在只需要透过 Navigation 类中管理的数据,通过一些转换就能实现了。

首先我们在 React 中定义一个 Navigation 上下文对象,确保得到正确的 Navigation 实例(如有多个)。

export const NavigationInstanceContext = createContext<Navigation>(null!)

然后,编写一个 RootStackNavigation 组件。

import { SafeAreaProvider } from 'react-native-safe-area-context'
import type { ScreenStackHeaderConfigProps } from 'react-native-screens'
import { ScreenStack } from 'react-native-screens'

interface RootStackNavigationProps {
  children: React.ReactNode

  headerConfig?: ScreenStackHeaderConfigProps
}

export const RootStackNavigation = ({
  children,
  headerConfig,
}: RootStackNavigationProps) => {
  return (
    <SafeAreaProvider>
      <NavigationInstanceContext value={Navigation.rootNavigation}>
        <ScreenStack style={StyleSheet.absoluteFill}>
          <ScreenStackItem headerConfig={headerConfig} screenId="root">
            {children}
          </ScreenStackItem>
        </ScreenStack>
      </NavigationInstanceContext>
    </SafeAreaProvider>
  )
}

在 App 的入口文件中,我们使用 RootStackNavigation 组件包裹整个应用。

export default function App() {
  return (
    <RootStackNavigation>
       <HomeScreen>
    </RootStackNavigation>
  )
}

const HomeScreen = () => {
  return (
    <View>
      <Text>Home</Text>
    </View>
  )
}

RootStackNavigation 组件的 Children 为首屏,也是 Navigation 的根组件,不参与整体的 navigate 行为,即不能被 pop。

Navigation 数据在 UI 中呈现

接下来我们需要把这些数据转换到 React 元素传入到 React Native Screens 的 <ScreenStackItem /> 中。

const ScreenItemsMapper = () => {
  const chainCtxValue = use(ChainNavigationContext)
  const routes = useAtomValue(chainCtxValue.routesAtom)

  const routeGroups = useMemo(() => {
    const groups: Route[][] = []
    let currentGroup: Route[] = []

    routes.forEach((route, index) => {
      // Start a new group if this is the first route or if it's a modal (non-push)
      if (index === 0 || route.type !== 'push') {
        // Save the previous group if it's not empty
        if (currentGroup.length > 0) {
          groups.push(currentGroup)
        }
        // Start a new group with this route
        currentGroup = [route]
      } else {
        // Add to the current group if it's a push route
        currentGroup.push(route)
      }
    })

    // Add the last group if it's not empty
    if (currentGroup.length > 0) {
      groups.push(currentGroup)
    }

    return groups
  }, [routes])

  return (
    <GroupedNavigationRouteContext value={routeGroups}>
      {routeGroups.map((group) => {
        const isPushGroup = group.at(0)?.type === 'push'
        if (!isPushGroup) {
          return <ModalScreenStackItems key={group.at(0)?.id} routes={group} />
        }
        return <MapScreenStackItems key={group.at(0)?.id} routes={group} />
      })}
    </GroupedNavigationRouteContext>
  )
}

const MapScreenStackItems: FC<{
  routes: Route[]
}> = memo(({ routes }) => {
  return routes.map((route) => {
    return (
      <ScreenStackItem
        stackPresentation={'push'}
        key={route.id}
        screenId={route.id}
        screenOptions={route.screenOptions}
      >
        <ResolveView
          comp={route.Component}
          element={route.element}
          props={route.props}
        />
      </ScreenStackItem>
    )
  })
})

const ModalScreenStackItems: FC<{
  routes: Route[]
}> = memo(({ routes }) => {
  const rootModalRoute = routes.at(0)
  const modalScreenOptionsCtxValue = useMemo<
    PrimitiveAtom<ScreenOptionsContextType>
  >(() => atom({}), [])

  const modalScreenOptions = useAtomValue(modalScreenOptionsCtxValue)

  if (!rootModalRoute) {
    return null
  }
  const isFormSheet = rootModalRoute.type === 'formSheet'
  const isStackModal = !isFormSheet

  // Modal screens are always full screen on Android
  const isFullScreen =
    isAndroid ||
    (rootModalRoute.type !== 'modal' && rootModalRoute.type !== 'formSheet')

  if (isStackModal) {
    return (
      <ModalScreenItemOptionsContext value={modalScreenOptionsCtxValue}>
        <WrappedScreenItem
          stackPresentation={rootModalRoute?.type}
          key={rootModalRoute.id}
          screenId={rootModalRoute.id}
          screenOptions={rootModalRoute.screenOptions}
          {...modalScreenOptions}
        >
          <ModalSafeAreaInsetsContext hasTopInset={isFullScreen}>
            <ScreenStack style={StyleSheet.absoluteFill}>
              <WrappedScreenItem
                screenId={rootModalRoute.id}
                screenOptions={rootModalRoute.screenOptions}
              >
                <ResolveView
                  comp={rootModalRoute.Component}
                  element={rootModalRoute.element}
                  props={rootModalRoute.props}
                />
              </WrappedScreenItem>
              {routes.slice(1).map((route) => {
                return (
                  <WrappedScreenItem
                    stackPresentation={'push'}
                    key={route.id}
                    screenId={route.id}
                    screenOptions={route.screenOptions}
                  >
                    <ResolveView
                      comp={route.Component}
                      element={route.element}
                      props={route.props}
                    />
                  </WrappedScreenItem>
                )
              })}
            </ScreenStack>
          </ModalSafeAreaInsetsContext>
        </WrappedScreenItem>
      </ModalScreenItemOptionsContext>
    )
  }

  return routes.map((route) => {
    return (
      <ModalScreenItemOptionsContext
        value={modalScreenOptionsCtxValue}
        key={route.id}
      >
        <ModalSafeAreaInsetsContext hasTopInset={!isFormSheet}>
          <WrappedScreenItem
            screenId={route.id}
            stackPresentation={route.type}
            screenOptions={route.screenOptions}
          >
            <ResolveView
              comp={route.Component}
              element={route.element}
              props={route.props}
            />
          </WrappedScreenItem>
        </ModalSafeAreaInsetsContext>
      </ModalScreenItemOptionsContext>
    )
  })
})

const ResolveView: FC<{
  comp?: NavigationControllerView<any>
  element?: React.ReactElement
  props?: unknown
}> = ({ comp: Component, element, props }) => {
  if (Component && typeof Component === 'function') {
    return <Component {...(props as any)} />
  }
  if (element) {
    return element
  }
  throw new Error('No component or element provided')
}

const ModalSafeAreaInsetsContext: FC<{
  children: React.ReactNode
  hasTopInset?: boolean
}> = ({ children, hasTopInset = true }) => {
  const rootInsets = useSafeAreaInsets()
  const rootFrame = useSafeAreaFrame()

  return (
    <SafeAreaFrameContext value={rootFrame}>
      <SafeAreaInsetsContext
        value={useMemo(
          () => ({
            ...rootInsets,
            top: hasTopInset ? rootInsets.top : 0,
          }),
          [hasTopInset, rootInsets],
        )}
      >
        {children}
      </SafeAreaInsetsContext>
    </SafeAreaFrameContext>
  )
}

这里需要判断的逻辑可能会有点复杂,需要区分 Stack 和 Modal 的类型,在 ModalStack 中又需要区分 formSheet 等等。同时每个 Modal 中有需要再包裹一层 StackScreen 等等。

从简单来说,就是需要根据 Navigation 的数据,生成对应的 <ScreenStackItem />,然后传入到 <ScreenStack /> 中。

这里的详细的代码均可在下面的链接中查看:

https://github.com/RSSNext/Follow/blob/efc2e9713bcd54f82f9377de35ef5532008d6004/apps/mobile/src/lib/navigation/StackNavigation.tsx

然后我们还需要处理 native navigation 的状态同步,主要在 native 触发 pop 和 dismiss 的时机发送的事件。在前面的文章中讲过,可以通过 ScreenStackItemonDismissed 监听。

这里我们直接对 ScreenStackItem 再次封装。

export const WrappedScreenItem: FC<
  {
    screenId: string
    children: React.ReactNode
    stackPresentation?: StackPresentationTypes

    screenOptions?: NavigationControllerViewExtraProps
    style?: StyleProp<ViewStyle>
  } & ScreenOptionsContextType
> = memo(
  ({
    screenId,
    children,
    stackPresentation,

    screenOptions: screenOptionsProp,
    style,
    ...rest
  }) => {
    const navigation = useNavigation()

    const screenOptionsCtxValue = useMemo<
      PrimitiveAtom<ScreenOptionsContextType>
    >(() => atom({}), [])

    const screenOptionsFromCtx = useAtomValue(screenOptionsCtxValue)

    // Priority: Ctx > Define on Component

    const mergedScreenOptions = useMemo(
      () => ({
        ...screenOptionsProp,
        ...resolveScreenOptions(screenOptionsFromCtx),
      }),
      [screenOptionsFromCtx, screenOptionsProp],
    )

    const handleDismiss = useCallback(
      (
        e: NativeSyntheticEvent<{
          dismissCount: number
        }>,
      ) => {
        if (e.nativeEvent.dismissCount > 0) {
          for (let i = 0; i < e.nativeEvent.dismissCount; i++) {
            navigation.__internal_dismiss(screenId)
          }
        }
      },
      [navigation, screenId],
    )

    const ref = useRef<View>(null)

    return (
      <ScreenItemContext value={ctxValue}>
        <ScreenOptionsContext value={screenOptionsCtxValue}>
          <ScreenStackItem
            key={screenId}
            screenId={screenId}
            ref={ref}
            stackPresentation={stackPresentation}
            style={[StyleSheet.absoluteFill, style]}
            {...rest}
            {...mergedScreenOptions}
            onDismissed={handleDismiss}
            onNativeDismissCancelled={handleDismiss}
          >
            {children}
          </ScreenStackItem>
        </ScreenOptionsContext>
      </ScreenItemContext>
    )
  },
)

定义 NavigationControllerView

export const PlayerScreen: NavigationControllerView = () => {
  return <SheetScreen onClose={() => navigation.dismiss()}></SheetScreen>
}

PlayerScreen.transparent = true

使用 Navigation

那么现在我们就可以在 React 中使用 Navigation 了。

const navigation = useNavigation()

navigation.pushControllerView(PlayerScreen)

那么,一个简单的 Navigation 就完成了。

当然如果你有兴趣的话,也可以查看 Folo 这部分的完整实现,包括如何和 Bottom Tab 结合和页面 ScrollView 的联动。

https://github.com/RSSNext/Follow/blob/6694a346a0bd9f2cea19c71e87484acc56ed3705/apps/mobile/src/lib/navigation

看完了?说点什么呢

安利一下我最近写的两个caddy插件

作者ysicing
2025年5月7日 22:07

我个人 Caddy 粉哈,习惯 Caddy 一梭子,从我历史博客中就可以看出来。最近写了两个 Caddy 的插件,geocngfw.

源码及镜像

源码 ysicing/dockerfiles#caddy

以下是我构建好的镜像,可以根据自己的环境拉取

  • ysicing/caddy2
  • ghcr.io/ysicing/caddy2
  • registry.cn-beijing.aliyuncs.com/k7scn/caddy2
  • ccr.ccs.tencentyun.com/k7scn/caddy2

源码构建

需要 go 环境了

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy build \
    --with github.com/caddyserver/jsonc-adapter \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/caddy-dns/tencentcloud \
    --with github.com/caddy-dns/alidns \
    --with github.com/ysicing/caddy2-geocn \
    --with github.com/ysicing/caddy2-gfw \
    --with github.com/mholt/caddy-dynamicdns \
    --with github.com/mholt/caddy-events-exec \
    --with github.com/WeidiDeng/caddy-cloudflare-ip \
    --with github.com/xcaddyplugins/caddy-trusted-cloudfront \
    --with github.com/mholt/caddy-l4 \
    --with github.com/mholt/caddy-webdav \
    --with github.com/mholt/caddy-ratelimit

插件 geocn

  • 源码:https://github.com/ysicing/caddy2-geocn
  • 用途:识别来源 ip 是否为中国 ip,我的大部分服务都开启了这个,只针对大陆放行,甚至部分服务只针对部分省市(误判比较大,后续有需要也可以开源 😄)
@china {
		geocn 
	}
	file_server @china {
		root ./docker/example/deny
	}

上面是默认参考,正常情况下不需要调整,GeoIP 数据源来自 Hackl0us/GeoIP2-CN,支持自定义

geocn {
 georemote 你的自定义地址
}

插件 gfw

{
    order gfw before respond
}

:80 {
    gfw {
        # 基本规则配置
        block_rule ip:1.2.3.4
        block_rule url:/admin
        block_rule ua:curl
        block_rule_file /path/to/rules.txt
        ttl 24h

        # 额外安全检测(默认关闭)
        enable_extra true
    }
}

目前是所有实例共享黑名单的,命中就 1 天黑名单直接返回 403,之前想的是命中后触发 hook 执行 iptables 封禁 ip,但是容器跑的好像不太方便。

最后

大家对 Caddy 插件有什么的需求或者想法么?


2025年国内外免费AI绘图大比拼:腾讯元宝、通义万相、豆包等10款平台「德州扒鸡创意胶囊」实测

作者斌仔
2025年5月7日 16:47

前言:AI绘图工具为何成为设计师新宠?

随着AI绘画技术的飞速突破,像Stable Diffusion、Midjourney等先进工具已经全面渗透到设计领域,成为众多设计师的得力助手。在当今数字化设计的浪潮中,免费的AI画图平台如雨后春笋般涌现。本文将对腾讯元宝、通义万相、豆包等10款热门免费AI画图平台进行详细实测,通过极具创意的「德州扒鸡创意胶囊」案例,为您揭秘哪款工具最能精准理解中文提示词,为设计师们在选择合适的AI绘图工具时提供有价值的参考。

核心测试案例解析

1.1 创意需求说明

  • 设计目标:精心制作16:9比例的胶囊造型微缩场景,以满足特定的视觉展示和设计需求。

  • 核心元素

    • 地域特色:山东德州,这座历史悠久的城市拥有独特的文化和地域风情,为设计增添了丰富的内涵。
    • 产品植入:传统扒鸡,作为德州的标志性特产,承载着当地的美食文化和历史记忆。
    • 风格要求:3D渲染光泽质感,这种风格能够使画面更加逼真、生动,展现出高品质的视觉效果。

提示词

参考下面的提示词帮我生成胶囊16:9图片,先基于地区和特产更改示例提示词中的内容然后再生成。
地区:德州
特产:扒鸡
示例提示词为:
一个胶囊形状的创意微缩场景,胶囊横放着。胶囊一半为活力的樱花粉色或现代的银灰色,印有白色的字“東京”和英文“TOKYO”。另一半透明,展示东京真实的特色建筑缩影——一座现代的东京塔或晴空塔微缩模型,完全包含在胶囊里面,不超出边界。背景为繁华的涩谷十字路口或浅草寺的微缩景象,整体风格3D渲染,具有梦幻感,使用C4D制作,材质有光泽感。

腾讯元宝

腾讯元宝:侧边栏 - AI画图

腾讯元宝是腾讯公司推出的一款AI助手工具,集搜索、AI 问答、文生图等功能于一体。其文生图功能允许用户通过文字描述生成图像,对于创意设计、内容创作等领域具有重要价值。用户只需输入一段文字,腾讯元宝即可根据文字内容生成相应的图像。此外,2025 年 2 月 21 日,腾讯元宝上线文生图功能,用户上传图片后,可通过 DeepSeek 模型解析内容并生成图文结合的创意结果(如分析图片场景、生成配文);结合混元T1模型,可识别图片中的文字和场景,辅助生成更精准的绘图描述(例如上传风景图后,AI自动生成绘画关键词)。它还支持AI修图、风格、比例的切换,为用户提供了多样化的创作选择。

在本次测试中,腾讯元宝绘制出了示例提示词的图片,但未绘制修改后的图片

腾讯元宝 - AI画图
腾讯元宝 - AI画图

通义千问

通义千问:图像生成

通义千问是由阿里云研发的一款先进的人工智能语言模型,基于Transformer架构,通过创新的训练方法(如动态NTK感知插值、LogN - Scaling、窗口注意力机制)扩展上下文长度,其千亿级参数规模(Qwen2.5 - Max版本)结合混合专家模型(MoE)架构,在自然语言处理、多模态理解等任务中表现出色。它适用于多种自然语言处理任务,包括文本生成、问答系统、机器翻译、文本分类等,在各个领域都能提供出色的表现。同时,它整合图文生成(通义万相)、音视频理解(通义星尘),支持PDF、Excel等多格式文件分析,还具备企业级服务闭环,实现了阿里云生态整合,无缝对接电商、物流、金融等行业解决方案(如天猫精灵智能客服),并且支持私有化部署,满足金融、政务等敏感场景需求。其开源影响力也较大,Qwen系列模型下载量突破1.8亿,衍生模型数达9万,超越Meta的Llama系列。

在图像生成方面,通义千问绘制出了修改后的图片,但只画出了扒鸡,没有绘制出德州等信息。

通义千问 - 图像生成
通义千问 - 图像生成

豆包

豆包:侧边栏 - 图像生成

豆包是字节跳动开发的通用大模型,融合了自然语言处理、计算机视觉和语音识别等技术。它提供聊天机器人、写作助手以及英语学习助手等功能,可以回答各种问题并进行对话,支持网页、客户端、APP、插件等形式。基于豆包大模型,字节跳动打造了AI对话助手“豆包”、AI应用开发平台“扣子”、互动娱乐应用“猫箱”,以及星绘、即梦等AI创作工具,并把大模型接入抖音、番茄小说、飞书、巨量引擎等50余个业务,用以提升效率和优化产品体验。

  • 豆包不仅在文本处理上表现出色,还具备强大的多模态交互能力,并且支持多风格、多比例的一致性多镜头生成,可应用在电商营销、动画教育、城市文旅、微剧本等领域。通过字节跳动内部 50+ 业务场景实践验证,每日千亿级 tokens 大使用量,使得豆包在推理效率和成本控制上具有明显优势。在图片生成方面,它一次性可生成多达 20 张 3D 风格的高质量图片,极大满足了设计、创意和娱乐等多样化需求。

在本次测试中,豆包完整绘制出扒鸡、德州等信息。

豆包 - 图像生成
豆包 - 图像生成

Gemini

Gemini

Google于2023年推出Gemini系列模型,作为其多模态大模型的里程碑,旨在结合文本、图像、音频等多模态能力,同时提升代码生成、对话理解等核心功能。其核心目标包括多模态统一(处理文本、图像、音频等多种输入输出)、长上下文理解(支持超长上下文,如Gemini Pro支持16万token)以及高效推理(在轻量化版本如Gemini Ace中平衡性能与计算资源)。

Gemini系列有多个版本,如基础版本Gemini 1支持多模态任务,适用于通用场景(如问答、摘要生成);高性能版本Gemini Pro面向复杂任务,具备超长上下文处理能力(支持16万token的上下文输入,适合长文档分析或复杂对话),多模态能力增强,可生成或理解高质量图像描述、音频内容,还支持代码生成;轻量化版本Gemini Ace优化成本与速度,具有低延迟推理特点,适合实时交互(如聊天机器人),适用于移动端或资源受限环境;2024年更新的Gemini 2新增视频理解能力,增强了推理和代码生成能力。

  • 在文生图方面,Gemini在自然语言的修改指令理解、材质质感复现、局部细节微调方面,达到了部分生产创作环节完全可用的水准。例如,它能完成简单形体的材质变换、连续微调形态细节、大幅度改变视角,还能将手绘稿转设计渲染图并拍出产品宣传图,一次性生成多套不同风格的设计,以及进行抠图、换背景、打光影等操作。不过,它也存在一些局限性,如多模态生成能力方面图像生成质量可能不如专用模型(如DALL·E),实时视频处理能力复杂视频分析仍需优化,高性能版本(如Gemini Pro)部署成本较高。

在本次测试中,Gemini绘制出了毫无相关的事物,但却有Dezhou字样。

Gemini
Gemini

即梦AI

即梦AI:图像生成

即梦(Jimeng)是字节跳动旗下的一个融合了前沿AI技术的多模态内容创作平台。它不仅仅能生成文本,更能理解和创造图像、音频乃至视频内容。其核心基于自然语言处理(NLP)、计算机视觉(CV)和先进的生成模型(如GANs、Diffusion Models等),采用“模型联邦”策略,整合了针对文本、图像、音频等不同任务优化的专用模型,并通过智能路由(Intelligent Routing)机制,根据用户需求动态调用最合适的模型组合,实现更专业、更高效的生成效果。同时,它在处理长篇内容或系列创作时,展现出良好的上下文理解和一致性保持能力,原生支持文本到图像(Text - to - Image)、图像到文本(Image - to - Text)、文本到音频(Text - to - Speech)等多种跨模态转换。

  • 2025年4月3日,即梦3.0正式启动灰度测试,并于4月7日全量上线。此次更新以中文文本生成能力和影视级画质为核心突破,支持2K分辨率(2560×1440像素)的直出图像,新增的“影视质感”效果可生成更具真实感和细腻度的图像,适用于广告、海报等商业场景。在中文文本生成能力方面,优化了小字稳定性,解决了此前版本中小字模糊、排版混乱的问题,支持更具设计感的字体生成,对中文指令的识别更精准。此外,它还具备智能化操作与效率提升功能,如精准控制功能,用户可通过简单指令调整图像中元素的细节;消除笔工具,针对生成图像中可能出现的冗余元素,提供一键消除功能。在语义理解上进一步优化,能更准确地解析复杂Prompt,支持多种应用场景的定制化生成,如电商广告、影视概念设计、教育内容等。

在本次测试中,即梦AI绘制出了示例提示词的图片,但未绘制修改后的图片。

即梦AI - 图像生成
即梦AI - 图像生成

哩布哩布AI

LiblibAI - 哩布哩布AI:在线生成

开通会员

哩布哩布AI是由北京奇点星宇科技有限公司运营的人工智能平台,是一个基于人工智能技术的创作平台,主要以AI图像生成功能为核心,在2023年5月创立,在短短时间内发展迅速,已经成为国内AI图像赛道的重要平台之一。

  • 它具有多样化的创作模型,涵盖动漫、游戏、摄影、写实、科幻、插画、平面设计、建筑、工业设计等多个领域,平台拥有10W +的模型可供选择,用户可以一键将所需模型入库,方便快捷地获取各类创作资源,节省寻找素材的时间,提高创作效率。其创作流程便捷,智能图像生成功能可让用户通过输入描述性的文本,将这些文本转化为图像;支持一键上传图片,可用于做配图、插图等且质量非常高,还支持高清修复和图生图功能;用户还可以利用其云端计算资源训练自己的AI模型。在用户体验方面,支持筛选和选择不同的创作模型,支持3D立体、扁平抽象等多种设计风格,提供会员专属权益,具有强大的用户社区,方便用户交流和分享创作经验。此外,它操作便捷,无需复杂配置,用户可以直接打开Liblib AI网页端即可使用云端SD – WEBUI,不用部署,不用下载模型;界面友好直观,即使是新手用户也能快速熟悉操作流程。

在本次测试中,哩布哩布AI绘制出了示例提示词的图片,但未绘制修改后的图片。

哩布哩布AI - 在线生成 - 星流Star - 3
哩布哩布AI - 在线生成 - 星流Star - 3

通义万相

通义万相:文字做图

通义万相是阿里云推出的AI多模态内容生成平台,基于阿里通义大模型,能够自动生成高质量的图片、艺术设计、广告素材、数字人形象等,广泛应用于电商、影视、设计、社交媒体等领域。它整合了文生图、图生图、风格迁移等功能,还具备高清修复、个性化定制等特色功能。其技术架构依托阿里巴巴通义大模型,结合扩散模型(Diffusion Model)和Transformer架构进行高质量图像生成。

  • 在文生图方面,它通过文本描述生成高清图像,支持水彩、油画、中国画、扁平插画、二次元、素描、3D卡通等8种风格,并且风格之间的差别、特色都十分显著,生成速度快,复杂的图像生成在 45s 以下,简单图像在30s以下。相似图像生成功能可让用户上传不超过10M的 jpg、jpeg、png、bmp 图片,点击生成按钮,右侧生成4张相似图片可供下载,生成的相似图与原图贴合程度较高。图像风格迁移功能支持输入两张图片,一张为原图,一张为指定风格图,生成的图像会保留原图的内容和风格图的风格。

在本次测试中,通义万相完整绘制出扒鸡、德州等信息(就是不太美观)。

通义万相 - 文字做图
通义万相 - 文字做图

可灵

可灵:图片生成

可灵AI是快手科技旗下的平台,2025年4月15日,可灵AI宣布基座模型再次升级,面向全球正式发布可灵2.0视频生成模型及可图2.0图像生成模型。作为全球首个用户可用的DiT视频生成模型,可灵AI自去年6月上线至今的10个月时间里,月活用户数量增长25倍,全球用户规模已突破2,200万。3月27日,全球知名AI基准测试机构Artificial Analysis发布了最新的全球视频生成大模型榜单,快手可灵1.6 Pro(高品质模式)以1,000分的Arena ELO基准测试评分登陆图生视频(Image to Video)赛道榜首。

  • 可灵2.0模型在动态质量、语义响应、画面美学等维度保持全球领先;可图2.0模型在指令遵循、电影质感及艺术风格表现等方面显著提升。可灵2.0大师版全面升级视频及图像创作可控生成与编辑能力,上线全新的多模态视频编辑功能,能灵活理解用户意图,支持在一段视频的基础之上,通过输入图片或文字,对生成的视频内容实现元素的增加、删减、替换;可图2.0也上线了实用的图像可控编辑功能——局部重绘和扩图,支持图片的增加、修改和修复,还上线了全新的风格转绘功能,只需要上传一张图片加上风格描述,就能一键切换图片的艺术风格,同时精准保留原图的语义内容。

在本次测试中(使用的是可图1.5),可灵绘制出了示例提示词的图片,但未绘制修改后的图片(速度慢,且最新模型可图2.0需要充值VIP)。

可灵 - 图片生成
可灵 - 图片生成

ChatGPT 4o Image

ChatGPT - 4o 图像生成

GPT - 4o 是 OpenAI 在 2025 年 3 月开始迭代的图像生成功能,其独特之处在于它能够在对话中理解上下文,生成更符合用户意图的图像。这一功能自推出以来,因其便捷性和生成图像的高质量,迅速成为 ChatGPT Plus/Pro 等版本的用户喜爱的功能。不过,使用 GPT - 4o 生成图像存在频率限制,一般情况下,ChatGPT Plus 用户每三小时可以使用大约几十次图像生成功能,在系统高峰期,这一限制可能会进一步减少,且该功能与文本生成功能共享 ChatGPT Plus 会员的权益次数。

  • 在图像生成方面,它具有理解提示词准确、一致性强等特点,擅长精确按照提示要求生成内容、多元素组合场景以及文字呈现准确性高的场景,适用于电商产品展示、企业宣传材料、需要准确呈现特定元素的场景等商业应用场景。

在本次测试中,ChatGPT 4o Image完整绘制出扒鸡、德州等信息(符合预期)。

ChatGPT 4o Image
ChatGPT 4o Image

测试效果

模型名称 效果说明 是否有理解修改能力 是否绘制完整
腾讯元宝 绘制出了示例提示词的图片,但未绘制修改后的图片
通义千问 绘制出了修改后的图片,只画出了扒鸡,但是没有绘制出德州等信息 ✔️
豆包 完整绘制出扒鸡、德州等信息 ✔️ ✔️
Gemini 绘制出了毫无相关的事物,但却有Dezhou字样
即梦AI 绘制出了示例提示词的图片,但未绘制修改后的图片
哩布哩布AI 绘制出了示例提示词的图片,但未绘制修改后的图片
通义万相 完整绘制出扒鸡、德州等信息(就是不太美观) ✔️ ✔️
可灵 绘制出了示例提示词的图片,但未绘制修改后的图片(速度慢,且最新模型需要充值VIP)
ChatGPT 4o Image 完整绘制出扒鸡、德州等信息(符合预期) ✔️ ✔️

总结

AI生成图首选:ChatGPT 4o Image > 豆包 > 通义万相

五一劳动节——高山采茶!

2025年5月7日 15:01

五一驾车5小时回家,一路上有惊无险安全到家。回到家第一时间就是大口深呼吸山里的空气,真的太舒服了,一瞬间人就不困了。

本来打算五一在家里好好休息的,没有想到,五一真正劳动了2天,1号去采了自己家的茶园,一个北方人第一次采茶,老婆说你的方法不对。我说看视频中就是这么采的呀!😂

6个人一天采了17斤鲜叶,真正体会到了茶农的不易。因为每一片叶子都是茶农亲手揪下来的,当然也有机器砍的但是那样的茶就会有很多树枝,烂叶。不像我们都是亲手一下一下摘下来的!

2025-05-07T03:21:01.webp

这几年的茶叶也开始禁止打农药。还设立了举报电话。

2025-05-07T03:24:15.webp

第二天,岳父又带着去采了野茶,一路上东揪一点,西采一点,也是采了一点,回家交给姑父给做了绿茶。

2025-05-07T06:56:03.webp

不说别的高山野茶的香味还就是比家门口的茶叶要香一点!岳父讲海拔不同环境不同,各种因素造就了野茶的味道不同。
但是两个茶叶放了一天时间后香味就都差不多了!奇怪!

当然!有需要茶叶的朋友也可以联系我哦!无农药残留,无重金属超标,无添加剂。

Mac新版微信4.0版本以上如何备份聊天记录到移动硬盘

作者张洪Heo
2025年5月7日 11:14
这篇文章介绍了微信备份迁移的相关知识,重点针对mac用户在微信备份文件不兼容问题的解决方法。用户因微信版本更新引发的整个备份布局变化感到困惑,文章详细指导了如何迁移旧版备份文件、创建新备份文件、完成备份操作,并解决了备份文件丢失及无法截图等问题。文章还提供了具体的操作步骤:复制备份文件至移动硬盘、创建软链接、使用codesign命令重新签名微信应用、授予系统访问权限等。同时,文章提醒用户注意备份前的检查,确保硬盘连接正常,以避免操作失误。整体内容结构清晰,语言简洁明了,非常适合mac用户快速掌握微信备份迁移的技巧。

上瘾性行为,正在毁掉我们这代人

作者
2025年5月6日 23:30
本文转载自公众号邓发发,由于十分赞同收藏于杂记中。另外,切勿断错标题。

哈喽,我是发发。因为我想发财,所以叫发发。前两天刷朋友圈,看到一个前同事凌晨两点发了条动态:“又刷短视频到天亮,明天还要上班,我是不是废了?”配图是一张手机屏幕使用时间截图——日均 8 小时。我回复她:“试试把手机调成黑白模式?”她回我:“试过,没用,刷着刷着又调回来了。”

你看,这就是现代人的困境——我们不是在上瘾,就是在寻找下一个上瘾的路上。

我们到底在沉迷什么?

上瘾的本质,是用短期快感填补长期空虚。

  • 短视频:15 秒一个刺激,刷到停不下来,但关上手机,大脑一片空白。
  • 游戏:赢了想再赢,输了想翻盘,一局接一局,时间像被黑洞吸走。
  • 暴食:明明不饿,却停不住往嘴里塞东西,吃完又后悔,第二天继续循环。
  • 囤积信息:收藏一堆课程、文章,告诉自己“以后看”,结果永远没看。

这些行为的共同点是什么?它们都能让你立刻爽,但长期来看,毫无意义。

为什么我们戒不掉?

因为大脑被劫持了。

科学家发现,上瘾行为会劫持多巴胺系统——这是一种让你感觉“快乐”的神经递质。正常的多巴胺分泌是“努力—奖励”模式,比如:你认真工作,完成项目,得到成就感。你坚持运动,身材变好,感到自信。但上瘾行为直接绕过“努力”,给你“即时奖励”。刷短视频,不用思考,直接爽。打游戏赢一局,立刻有成就感。

久而久之,你的大脑会认为:“既然躺着就能爽,为什么还要努力?”于是,你越来越难专注,越来越没耐心,甚至对现实生活失去兴趣。

上瘾的真正代价

很多人觉得,上瘾只是“浪费时间”,没什么大不了的。但它的真正危害是:让你失去对人生的掌控权。

  • 时间黑洞:每天刷 3 小时短视频,一年就是 1095 小时,相当于 45 天。
  • 注意力碎片化:你再也无法专注读完一本书,甚至看不完一篇长文章。
  • 现实感丧失:虚拟世界的快感越强,现实越显得无聊,于是你更想逃避。
  • 自我厌恶:明知道不该这样,却控制不住,最后陷入“放纵—愧疚—更放纵”的恶性循环。

最可怕的是,上瘾会让人失去“延迟满足”的能力。你不再相信“长期坚持会有回报”,只想立刻得到反馈。于是,你放弃健身、放弃学习、放弃任何需要时间积累的事情,变得越来越浮躁。

如何真正戒瘾?

先认清一个事实:戒瘾不是靠意志力

真正的解决方法,是用新习惯替代旧习惯。比如:

  • 想刷短视频时,立刻站起来做 10 个深蹲。
  • 想打游戏时,打开一本轻松的小说读 10 分钟。
  • 想吃零食时,先喝一大杯水,等 5 分钟再决定。

调整环境,减少诱惑

把手机调成灰度模式(黑白屏),降低视觉刺激。卸载最常刷的 APP,或者设置每天使用限额。睡前把手机放在客厅,不带进卧室。

找到真正的“替代满足”

上瘾的本质是逃避现实,所以关键是:找到比上瘾更有意义的事。比如:

  • 培养一个能带来成就感的爱好(剪辑、绘画、写作、手工)。
  • 加入一个正向社群(运动小组、徒步群)。
  • 设定一个小目标(比如每天写 100 字,跑步 30 分钟)。

记住:戒瘾不是剥夺快乐,而是找回真正的快乐。

最后想说

我曾经也是个很容易“上头”的人,熬夜追剧、刷社交媒体到凌晨。后来我发现,真正的自由,不是想做什么就做什么,而是不想做什么就能不做什么。如果你也深陷上瘾性的行为,别急着否定自己。上瘾不是你的错,只是你还没找到更好的生活方式。从今天开始,试着做一个小改变:每天减少 30 分钟刷手机的时间,换成散步、运动或阅读。给自己 1-3 个月,慢慢调整,别追求立刻戒断。

熬过最初的戒断反应,你会发现——原来生活本身,就足够有趣。

原文链接:https://mp.weixin.qq.com/s/J0ll4R9IX08VoFk_ZBIOWQ

一拖再拖的四月月报

2025年5月6日 21:53

本该写在月底的月报,因为提前回老家被拖了,在老家因为懒也一直拖着没写,回到工作岗位,进入工作状态,这才姗姗动笔。

四月份的工作很忙,有时候晚上甚至搞到很晚,因此做自己事情的时间就少了很多。

折腾

因为想要把用了好多年的HHBK拿出来用的时候,发现之前的连接线找不到了,而这个连接线还是很老的mini B接口,真的很难找到,于是又想着去改造成无线了。之前了解到一个YDKB的改装方案,但是价格要400多,就放弃了。这次在淘宝上一搜索,居然有一个100多块钱的方案,并且支持无线/蓝牙/USB三种模式,usb口也改成了type-c接口,这个价格同时还包含了锂电池,这么实惠的价格,立马就下单了。回来替换也很简单,就是把原来的主控板,换成这个新的主控板就好了,原先的usbhub口位置则是变成了开关按钮和模式切换的按键开口,usb-c在原来min b的位置也勉强能插上线,唯一的缺点就是键位模式设置没有设计在原来键位设置的地方,如果需要修改需要拆开键盘。总体,瑕不掩瑜,推荐尝试改造。

另外,上个月提到我使用Flutter做了一个memos的客户端,经过这个月的修改,和google play的封闭测试,目前已经正式上架google play。感兴趣可前往下载,链接:https://play.google.com/store/apps/details?id=me.isming.fymemos, 同时我也开源了代码,感兴趣可以去github查看,也欢迎贡献代码,链接: GitHub - sangmingming/fymemos: A memos client write in Flutter

清明节去了趟台州,徒步到仙居公盂村,还去了临海台州府城,天台国清寺庙,感兴趣可以看我之前的文章

除了公盂这次徒步,还在月中去了一次苏州西山缥缈峰徒步,缥缈峰难度很低,感觉四五岁的小朋友也可以拿下。因为我们出发比较早,一趟畅通,爬完山之后居然还挺早,又在岛上去看了看东村古村和最佳夕阳观赏点(天气不好,看不到夕阳😂)。

接近月底的周末,因为五一要补一天班,周末只有一天,因此选择在上海找个地方露营。前往广富林郊野公园,发现公园装了收费杆,只有七号门可以进,并且排了很长的队,最终只好在附近找了个农田露营。

这个月主要在看《芯片战争》,看完了余盛的版本,同时这个名字的书还有一个美国的版本,在微信读书看了一小部分,发现其中对于中国的部分有删改,于是又找来了台湾版的译本《晶片战争》,看了一部分。篇幅上来说,余盛版本的更长,其中关于中国的部分篇幅比较长,其中关于中国的介绍是比较乐观的,比如中芯国际,长鑫存储,长江存储等的介绍。而台湾版本的,对于中国的部分不多,评价更加中性,但是因为内容是繁体字,其中很多名词和大陆的说法都不同,看的很慢。对于两本书一起辩证看的,对于我们了解芯片的发展和战争会有更加全面的认识。芯片的设计和生产,涉及到的配套和供应链也很多,而被封锁的中国想要突围,仍然任重道远。

电视剧这个月居然看了两部,首先是王宝强主演的《棋士》,感觉还挺不错的,可以看看。

另外老婆在看一个叫《无忧渡》的电视剧,也跟着一起看完了,这部剧典型的俊男靓女片,男女主都有主角光环,同类型的片子还是《唐朝诡事录》更好看一点。

而看了两部剧的代价就是本来会有点时间练字和看书,被看剧给挤占了,因此以后还是要少看剧。

杂项

看到有网友在玩Slowly这样一个笔友软件,于是也去注册完了完。这个软件基于邮票和根据距离限制邮件送达的时间的设计很有意思。在上面写了公开信,也与几个人有了邮件往来。而每次写信,都要借助翻译软件来优化英语内容,或许也能间接学学英语。立个Flag,在上面找到一两个长期笔友,借此提高一下英语表达的水平。

总结

生活一直在继续,工作繁忙也要抽空多出去多走走。虽然拖了几天但还是把终结补上了,哈哈。也感谢你看到了这里。

看完评论一下吧

轻松部署 Alist + MinIO,打造你的专属私人网盘

作者ysicing
2025年5月6日 20:49

还在为网盘限速、空间不足而焦虑?想要一个安全、快速、完全掌控的私人网盘?今天带你一步步用 Alist 结合 MinIO,快速搭建一个高性能的私人云存储,文件管理从此自由无忧!

部署非常简单,也很适合内网私有化部署。另外这也是一个开源项目,社区灵活度特别高,对接的存储类型非常丰富,但是本文还是着重写写对接 minio。今天的音频调了几版,目前这版相关好点

什么是 Alist 和 MinIO?

  • Alist:一款开源免费的目录列表程序,支持挂载多种存储(如本地存储、云盘、对象存储等),提供简洁美观的界面,支持文件预览、下载、分享等功能。简单来说,它是你文件管理的“超级中枢”。
  • MinIO:一个高性能、分布式的对象存储服务,兼容 S3 协议,适合搭建私有云存储。相比第三方网盘,MinIO 让你完全掌控数据,安全又高效。

通过 Alist + MinIO 的组合,你可以轻松打造一个私有网盘,享受无限存储空间和极速访问体验!

Alist + MinIO 的优势

  • 多存储支持:Alist 支持 MinIO、本地存储、OneDrive、阿里云盘等多种存储方式,灵活扩展。
  • 简洁易用:Alist 界面美观直观,操作简单。
  • 高性能:MinIO 提供企业级的对象存储性能,适合大文件存储和高速访问。
  • 安全可靠:数据存储在你自己的服务器上,隐私有保障。
  • 开源免费:Alist 和 MinIO 均为开源项目,自由使用,社区活跃。

部署步骤:Alist + MinIO 一键搞定

以下以 Docker 部署为例,带你快速搭建 Alist 和 MinIO 的组合。这里就跳过 MinIO 部署相关了,之前也讲过,可以查看我之前写的文章:

准备工作

  • 准备好 MinIO 的账号即可,有存储视频资源最好不过

镜像

根据实际情况来,默认 aio 镜像已经包含本地存储缩略图 ffmpeg 和离线下载 aria2, 后面需要用的上

  • xhofe/alist:main-aio
  • 国内镜像 ccr.ccs.tencentyun.com/k7scn/alist:main-aio

创建 docker compose 文件

  • docker-compose.yml
services:
  alist:
    image: xhofe/alist:main-aio
    # image: ccr.ccs.tencentyun.com/k7scn/alist:main-aio
    container_name: alist
    ports:
      - "5244:5244"
    volumes:
      - /data/alist:/opt/alist/data # 应用程序持久化数据
      - /data/share:/opt/share # 本地存储,可选
    environment:
      - TZ=Asia/Shanghai
      - ALIST_ADMIN_PASSWORD=goxee7dieXeihu9uochoo6iquaighail
    restart: always

ALIST_ADMIN_PASSWORD 支持自定义密码,很早之前我提交的 PR😂,估计也就我一个人这么用。

启动容器

docker compose up -d

配置 caddy

caddy 配置比较简单

alist.ysicing.eu.org {
  reverse_proxy 100.90.80.2:5244
}

访问 alist

访问 Alist:在浏览器输入 http://你的服务器IP:5244 或者 caddy域名,进入 Alist 界面。

默认用户名是 admin, 密码是你配置的 ALIST_ADMIN_PASSWORD 值信息

挂载 MinIO 存储

登录 Alist,点击 管理 > 存储 > 添加

选择存储类型为对象存储

填写以下信息:

  • 挂载路径:自定义,例如 /minio。
  • Endpoint:http://minio 域名地址:9000。
  • Bucket:填写你在 MinIO 创建的存储桶名称,例如 ja。
  • Access Key 和 Secret Key:填入 MinIO 控制台生成的密钥。
  • 强制路径样式:默认勾选
  • 地区:默认留空

保存配置后,返回 Alist 主页,即可看到挂载的 MinIO 存储

可以上面的操作后就可以通过 Alist 浏览、分享 MinIO 中的文件,支持在线预览、下载等功能。

其他

官方文档

总结

通过 Alist 和 MinIO 的组合,你可以轻松搭建一个功能强大、安全可靠的私人网盘,告别存储焦虑!无论是个人文件管理还是团队协作,这个方案都能满足你的需求。快动手试试吧!


手搓点焊机

作者
2025年5月5日 21:47

需求

对于偏爱折腾电子产品的中年男人,拆装电子设备的工具必须配置齐全的。不过,由于使用率偏低,而且受成本因素影响,我至今未增设点焊机。

近日,常使用的短途出行工具GY6小型摩托车频繁出现打火不顺畅的故障。经过排查是电瓶故障引起的,因使用率偏低(一周大概骑行2天,总路程大概10KM),电瓶由于长时间无法及时进行充电,长期亏电损耗导致的容量下降,一般解决方案是修复或者更换新电瓶。观察电瓶生产日期,使用仅大半年,不至于会出现电瓶内部隔板损坏的情况,倘若直接更换有点浪费,因此尝试使用电瓶脉冲修复工具对其进行修复。经过数个小时的修复工作,电瓶容量虽恢复到能用水平,但实测撑不过超一周的停车自然损耗。每次出现亏电无法打火情况便需要拆卸电瓶回家进行数小时修复充电,显然这不是科学解决方案,眼下购置应急启动电源成为一个优选方案。

GY6电瓶
GY6电瓶

优选法拉电容方案

常见的应急启用电源方案:

方案一:锂电池电芯

这个方案和日常用的移动充电宝类似,使用锂电池作为电源,提供较大电流输出实现启动车辆的效果。还有一个优点,日常不用的情况下,还能当充电宝使用。缺点也比较明显,和锂电一样需要定期对其进行充放电维护,且日历寿命较短。

方案二:法拉电容

这个方案使用多个大容量法拉电容进行串并链接组合使用,提供短时间大电流输出实现启动车辆的效果。优点是寿命长,日常无需维护,因此无法当充电宝使用。缺点也比较明显,需要先进行充电后才能使用,充电后能使用次数较少。

应急充电宝技术原理是提供短时间大电流输出,这和点焊机是一致的,因此两个需求的方案可以综合一起进行筛选。因为日常车辆维护会对电瓶健康情况进行检查,这些年需要小车进行应急启动的次数真的屈指可数,而且目前汽车保险公司均免费提供汽车搭电服务,因此个人自备应急启动电源作为汽车应急使用算是伪需求。

方案容量充电时间寿命单次可用价格
锂电芯≥8000mha5V2A,≥4小时3-5年充电一次500焊¥150
法拉电容摩托车50F,小车200F电源16V5A:50F,3分钟;200F,12分钟;10年单次焊接回电大概1-2秒50F,¥60,只能启动摩托车;300F,¥200,大部分汽车

综合上表,最终我选择50F的超级电容方案,实测能启动3次摩托车,点焊基本能秒回电。如果本身有应急启动电源,直接买点焊板会更划算。

组装和物料费用

物料参数价格渠道备注
法拉电容2.7V,6串,带均衡板60海鲜市场
充电板16V5A5.3首富家
外壳200x120x1139.7首富家
端子EC5x1,XT60Ex1,18首富家、PDD
电压表DC4-100V4.6首富家
点焊板K838PDD
搭电钳EC5,50CM15PDD
192

电容预充电
电容预充电

点焊板
点焊板

组装前调试
组装前调试

焊接2个21700试手
焊接2个21700试手

组装了个充电宝
组装了个充电宝

建议和提醒

DIY电子产品有风险,并不适合大部分人使用,综合成本并没有太大价格优势,请谨慎选择。如无法评估自身动手能力,请选择合格的厂家生产的成品。

私有化部署无名杀卡牌游戏

作者ysicing
2025年5月5日 19:52

部署非常简单,非常适合收藏,内网私有化部署。另外这是一个开源项目,灵活度比较高。

项目地址

https://github.com/libnoname/noname

镜像

可以根据自己的网络情况选择对应的镜像下载,镜像比较大, 大概 3.5G 左右。

  • hsiaoshun/noname
  • ccr.ccs.tencentyun.com/k7scn/noname

部署 compose

services:
  noname:
    image: hsiaoshun/noname
    # image: ccr.ccs.tencentyun.com/k7scn/noname
    container_name: noname
    ports:
      - '6080:80'
    restart: always

端口配置

  • 80 游戏本体网页版入口
  • 8080 WS 协议,联机大厅服务(客户端使用)

caddy 代理

示例,不建议公网跑,对带宽有点要求

sgs.ysicing.eu.org {
reverse_proxy 100.90.80.2:6080
}

联机大厅配置说明

目前只支持 windows 和安卓

注意: 结尾的/不能省略, 如果没有证书就是 ws,有证书就是 wss

其他

如果有更多兴趣的话,可以看看无名杀懒人包。


Debian常用初始化流程

作者ysicing
2025年5月5日 14:50


在搭建 k3s 轻量级 Kubernetes 集群时,Debian 系统因其稳定性和灵活性成为首选。然而,Debian 默认配置可能无法满足 k3s 的需求,需要通过初始化优化系统设置。本文将分享一套针对 k3s 环境的 Debian 初始化方案,涵盖基础包安装、系统配置和防火墙规则,仅供参考。

安装基础包

以下命令安装 k3s 集群所需的基础工具,保持系统轻量:

export DEBIAN_FRONTEND=noninteractive
apt update -qq
apt remove -y -qq ufw lxd lxd-client lxcfs lxc-common
apt install --no-install-recommends --no-install-suggests -y -qq nfs-common iptables conntrack jq socat bash-completion open-iscsi rsync ipset ipvsadm htop net-tools wget psmisc git curl nload ebtables ethtool procps

配置系统

配置 ssh

修改 ssh 端口,设置密钥登录,禁用密码登录。这些比较常见,这里就不细说了。

更新内核

之前好像也写过,通常我都是使用最新内核,仅供参考.(通常也会踩坑比较多)

curl https://c.ysicing.net/oss/scripts/debian-upcore.sh | bash
# 或者
apt install -t bookworm-backports linux-image-amd64 -y

配置 system 相关

调整 Systemd 的资源限制和日志设置

mkdir -pv /etc/systemd/system.conf.d
cat > /etc/systemd/system.conf.d/30-k8s-ulimits.conf <<EOF
[Manager]
DefaultLimitCORE=infinity
DefaultLimitNOFILE=100000
DefaultLimitNPROC=100000
EOF

mkdir -pv /etc/systemd/journald.conf.d /var/log/journal

cat > /etc/systemd/journald.conf.d/95-k3s-journald.conf <<EOF
[Journal]
# 持久化保存到磁盘
Storage=persistent
# 最大占用空间 2G
SystemMaxUse=2G
# 单日志文件最大 100M
SystemMaxFileSize=100M
# 日志保存时间 1 周
MaxRetentionSec=1week
# 禁止转发
ForwardToSyslog=no
ForwardToWall=no
EOF

systemctl daemon-reload
systemctl restart systemd-journald

cat > /etc/modules-load.d/10-k3s-modules.conf <<EOF
br_netfilter
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF

systemctl daemon-reload
systemctl restart systemd-modules-load

配置防火墙规则

提示:8.8.8.8 为示例白名单 IP,请替换为实际 IP,搭配rc.local

  • /data/scripts/iprule.sh
#!/bin/bash
iptables -I INPUT -s 8.8.8.8 -j ACCEPT
iptables -I INPUT -p udp -j ACCEPT
iptables -I INPUT -i lo -j ACCEPT
iptables -I INPUT -i tailscale0 -j ACCEPT
iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -I INPUT -s 10.0.0.0/8 -j ACCEPT
iptables -I INPUT -s 172.16.0.0/12 -j ACCEPT
iptables -I INPUT -s 192.168.0.0/16 -j ACCEPT
iptables -I INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p icmp -j DROP
iptables -A OUTPUT -j ACCEPT
iptables -A INPUT -j DROP

防火墙规则没考虑使用 iptables-save 等保存恢复,而是每次开启时重新配置。

总结

通过以上步骤,我们完成了一套针对 k3s 环境的 Debian 系统初始化,优化了网络、资源限制和安全性。你可以直接使用以下脚本一键初始化

curl https://c.ysicing.net/oss/scripts/init.sh

Typecho根据slug添加icon

作者老孙
2025年5月5日 10:50
AI摘要:文章展示了在Typecho中通过slug匹配添加自定义icon的方法,分别针对分类和页面提供了switch-case代码示例,根据不同的slug值显示对应的图标。

使用穷举的方式来匹配自定义icon

根据分类的slug来匹配

                            <?php 
                            switch($categories->slug) {
                                case 'images': echo '<i class="bi bi-images me-1"></i>';
                            break;
                                case 'share': echo '<i class="bi bi-share-fill me-1"></i>';
                            break;
                                case 'NULL': echo '<i class="bi bi-speaker-fill me-1"></i>';
                            break;
                                case 'memos': echo '<i class="bi bi-chat me-1"></i>';
                            break;
                                case 'codes': echo '<i class="bi bi-code me-1"></i>';
                            break;
                                case 'logs': echo '<i class="bi bi-person-fill me-1"></i>';
                            break;
                                case 'test': echo '<i class="bi bi-calendar-fill me-1"></i>';
                            break;
                                case 'tools': echo '<i class="bi bi-tools me-1"></i>';
                            break;
                                case 'music': echo '<i class="bi bi-music-note me-1"></i>';
                            break;
                                case 'links': echo '<i class="bi bi-link me-1"></i>';
                            break;
                                case 'video': echo '<i class="bi bi-camera-video me-1"></i>';
                            break;
                                case 'life': echo '<i class="bi bi-heart-fill me-1"></i>';
                            break;
                                case 'study': echo '<i class="bi bi-book-fill me-1"></i>';
                            break;
                                case 'news': echo '<i class="bi bi-newspaper me-1"></i>';
                            break;
                                case 'themes': echo '<i class="bi bi-palette me-1"></i>';
                            break;
                                case 'plugins': echo '<i class="bi bi-gear-fill me-1"></i>';
                            break;
                                case 'photo': echo '<i class="bi bi-images me-1"></i>';
                            break;
                                default: echo '<i class="bi bi-folder-fill me-1"></i>';
                            } ?>

同样也可以根据自定义页面的slug匹配

<?php $pages = Typecho_Widget::widget('Widget_Contents_Page_List'); ?>
<?php while($pages->next()): ?>
<li>
    <a href="<?php $pages->permalink(); ?>">
    <?php 
    switch($pages->slug) {
        case 'about': echo '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><circle cx="12" cy="7" r="4" /><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" /></svg> '; // 关于页面
        break;
        case 'links': echo '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-link" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5" /><path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5" /></svg>'; // 链接页面
        break;
        case 'archives': echo '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-archive" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z"/><rect x="3" y="4" width="18" height="4" rx="2" /><path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10" /><line x1="10" y1="12" x2="14" y2="12" /></svg>'; // 归档页面
        break;
        case 'gbook': echo '<svg  xmlns="http://www.w3.org/2000/svg"  class="icon icon-tabler icon-tabler-article" width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-article"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 4m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" /><path d="M7 8h10" /><path d="M7 12h10" /><path d="M7 16h10" /></svg>'; // 博客页面
        break;
        case 'messages': echo '<svg  xmlns="http://www.w3.org/2000/svg"  class="icon icon-tabler icon-tabler-messages" width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-messages"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M21 14l-3 -3h-7a1 1 0 0 1 -1 -1v-6a1 1 0 0 1 1 -1h9a1 1 0 0 1 1 1v10" /><path d="M14 15v2a1 1 0 0 1 -1 1h-7l-3 3v-10a1 1 0 0 1 1 -1h2" /></svg>'; // 留言页面
        break;
        default: echo '<svg  xmlns="http://www.w3.org/2000/svg"  class="icon icon-tabler icon-tabler-file" width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-file"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4" /><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" /></svg>'; // 默认图标
    } ?>
    <span><?php $pages->title(); ?></span>
    </a>
</li>
<?php endwhile; ?>

诗和远方

作者Liyun
2025年5月5日 02:18

最近头痛时不时复发,只能强制自己远离工作的紧张节奏,专注于自我的平和。

某种意义上,我其实很适合高强度的工作。压力越大,越是冷静。这可能是以前工作训练出来的,也可能是飞行执照训练出来的。当周围的人都进入狂暴模式的时候,我却一如既往的淡定,稳如泰山。我可以一个个劝住,在大家都手足无措的时候指明方向。工作是长跑,是漫长的登山,越是困难越让我冷静。

某种意义上,我其实是很适合硅谷的丛林法则的。这也可能是这么多年来我在工作上并没有遇到太多天花板,一直能找到突破的方向的缘故。不给自己设限,也便有了无数的可能。

但是,退一万步讲,这真的是我想要的生活吗?功名利禄,好像并没有我真正在乎的。钱变成了数字,让我感觉越来越陌生。我的生活质量并没有因为钱而变得更高,反而失去的时间和自由让我会不时感到窒息。最快乐的日子并不是多了多少标签,而是沉浸在自我的世界中专注。能感受到自己还活着,并非行尸走肉。

年龄越大,越觉得自己跟同龄人的割裂,一种宛如代际的割裂感。他们的高谈阔论,我的诗与远方。我试图融入,可是越融入越痛苦。索性不再假装,索性回归本心。 人生苦短,何必过多地向现实妥协。或许我太早地被经济学的功利主义洗脑,却也侥幸早早得以看清功利主义的局限。

愿我可得一世自由。

Debian 双栈网络时开启 IPv4 优先(音频版)

作者ysicing
2025年5月3日 23:32

PS: 用 AI 生成的图老是不合法微信封面的比例,放到文尾。本文也提供音频版, 欢迎订阅我的微信公众号。

在如今的网络世界,IPv6 正在逐渐普及,但 IPv4 依然是许多场景的“老大哥”。如果你用的是 Debian 系统,并且身处 IPv4 和 IPv6 共存的双栈网络环境,可能会发现系统默认优先使用 IPv6——这在某些情况下并不理想,比如某些服务只支持 IPv4,或者 IPv6 连接不稳定。今天,我们就来聊聊如何在 Debian 上实现 IPv4 优先,甚至在需要时完全禁用 IPv6。跟着这篇教程,轻松搞定网络配置!

为什么需要调整网络优先级?

先来点背景知识:双栈网络指的是设备同时支持 IPv4 和 IPv6 协议栈。现代操作系统(如 Debian)和浏览器通常默认优先使用 IPv6,只有当 IPv6 连接失败时才会“退而求其次”用 IPv4。这听起来很智能,但在实际场景中可能会遇到问题:

  • 服务兼容性:某些老旧服务或内网应用只支持 IPv4,IPv6 优先可能导致连接失败
  • 网络性能:部分网络环境下,IPv6 的延迟或稳定性不如 IPv4
  • 特殊需求:比如开发测试时,你可能希望强制使用某一种协议

所以学会调整 IPv4 和 IPv6 的优先级,或者在极端情况下禁用 IPv6,是每个 Debian 用户的“进阶技能”。下面,我们一步步教你搞定!

让 IPv4 优先:修改 gai.conf 文件

Debian 系统中,/etc/gai.conf 文件控制了 getaddrinfo 函数的行为,这个函数决定了系统如何选择 IPv4 或 IPv6 地址。默认情况下,IPv6 优先,但我们可以通过简单修改让 IPv4 站到“C 位”。

修改步骤

打开终端,输入以下命令编辑 /etc/gai.conf

#precedence ::ffff:0:0/96  100

去掉 # 号,修改为:

precedence ::ffff:0:0/96  100

保存并退出。

懒人福利:如果你不想手动编辑,可以直接用这条命令一键搞定:
bash

sed -i 's/#precedence ::ffff:0:0\/96  100/precedence ::ffff:0:0\/96  100/' /etc/gai.conf

测试效果

配置完成后,用 curl 命令测试一下:

curl ip.sb

也可以使用

# 查询本机外网IPv4地址
curl 4.ipw.cn

# 查询本机外网IPv6地址
curl 6.ipw.cn

# 测试网络是IPv4还是IPv6访问优先(访问IPv4/IPv6双栈站点,如果返回IPv6地址,则IPv6访问优先)
curl test.ipw.cn

如果返回的是类似 6.6.6.6 的 IPv4 地址,恭喜你,IPv4 优先已生效!如果返回的是类似 2001:db8::2 的 IPv6 地址,检查是否正确保存了配置。

原理

::ffff:0:0/96 是 IPv4 地址在 IPv6 协议中的映射范围,设置其优先级为 100(高于默认 IPv6 的优先级),系统就会优先选择 IPv4 地址。

特殊场景:强制 IPv6 优先

有些朋友可能有“奇特”需求,比如测试 IPv6 环境或某些服务明确要求 IPv6 优先。别担心,我们也可以反向操作!

同样编辑 /etc/gai.conf, 在文件末尾添加以下两行:

label 2002::/16    1
label 2001:0::/32   1

保存退出,或者用命令一键添加:
bash

echo -e "label 2002::/16    1\nlabel 2001:0::/32   1" | sudo tee -a /etc/gai.conf

原理

2002::/162001:0::/32 是常见的 IPv6 地址段,设置它们的 label 优先级为 1,确保系统优先选择这些 IPv6 地址。IANA 目前分配的公网 IPv6 地址还未覆盖到 3000:0000::/4,所以这招基本万无一失

(这个未测试过,仅供参考)

极端情况:完全禁用 IPv6

如果你的网络环境压根不需要 IPv6,或者 IPv6 总给你添乱,可以直接禁用它。以下是禁用 IPv6 的方法,适合“断舍离”爱好者。
编辑 /etc/sysctl.conf 文件:

net.ipv6.conf.all.disable_ipv6 = 1
# 禁用eth0的ipv6
net.ipv6.conf.eth0.disable_ipv6 = 1

结语

通过简单的配置文件调整,你就可以在 Debian 双栈网络中自由掌控 IPv4 和 IPv6 的优先级,甚至彻底禁用 IPv6。无论是提升网络兼容性、优化性能,还是满足特定需求,这些技巧都能让你事半功倍!


又是一段短暂而美好的中世纪旅程

2025年5月3日 14:07

想不到,新作来的太快,而且剧情还是接着上一部,就很开心。

总体玩下来感觉有些拖沓,没有第一部时的惊艳。

整体基调和第一部基本一致,但是玩下来就感觉,不够创新。都是一些零散的小事件,然后各种拼凑。说是体验中世纪生活,但是事件关联并不细致,感觉不如上古卷轴5那种关联。但是剧本部分也是很认真的,比如真假隐士任务,在开局的时间线上,隐士还没死,但是已经奄奄一息了,随着任务推进时间流逝,玩家到达隐居所时隐士就已经死了。

游戏里同性恋选择挺难绷的,而且左右恋爱剧情都像是硬插进去的,整体显得很割裂,反而感觉还不如不加感情线好了。另外结局也挺难绷的,还玩道德绑架,不愧是欧美游戏,这种套路就像日系游戏的反派洗白套路一样,都是必备套路,玩着人家的游戏习惯一下就好了,谁让咱自己不出这类的游戏呢。之前玩的游戏看的动画夹带私货还骂,现在想想都是一个性质。

{cat_gallery}









{/cat_gallery}

Debian 12 解决 /etc/rc.local 开机启动问题

作者ysicing
2025年5月3日 06:59

在 Debian 12(以及 Debian 9 及以上版本)中,/etc/rc.local 是配置开机自启动脚本的传统方式,但默认未启用,导致自定义脚本无法自动运行。本文将详细指导你在 Debian 12 上启用和配置 /etc/rc.local,步骤同样适用于 Debian 9 Stretch、10 Buster 和 11 Bullseye

问题背景:rc.local 为什么不生效?

Debian 9 起采用 systemd 作为初始化系统,传统的 /etc/rc.local 默认不生效。尽管系统内置了 rc-local,但默认处于禁用状态:

root@debian:~$ systemctl status rc-local.service
○ rc-local.service - /etc/rc.local Compatibility
     Loaded: loaded (/lib/systemd/system/rc-local.service; static)
    Drop-In: /usr/lib/systemd/system/rc-local.service.d
             └─debian.conf
     Active: inactive (dead)
       Docs: man:systemd-rc-local-generator(8)

以下是默认的 rc-local.service 配置,表明它会在 /etc/rc.local 可执行时自动拉起:

root@docker:~$ systemctl cat rc-local.service
# /lib/systemd/system/rc-local.service
#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local
After=network.target

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no

# /usr/lib/systemd/system/rc-local.service.d/debian.conf
[Unit]
# not specified by LSB, but has been behaving that way in Debian under SysV
# init and upstart
After=network-online.target

# Often contains status messages which users expect to see on the console
# during boot
[Service]
StandardOutput=journal+console
StandardError=journal+console

解决步骤:启用 /etc/rc.local

以下步骤助你快速启用 /etc/rc.local,实现开机脚本自动运行

创建 /etc/rc.local 文件

默认没有 /etc/rc.local,我们需要手工添加一个 /etc/rc.local 文件:

cat <<EOF >/etc/rc.local
#!/bin/bash
# 这是一个示例 rc.local 文件
# 在这里添加你的开机执行命令
# 示例:启动一个自定义脚本
# /path/to/your/script.sh
exit 0
EOF

设置可执行权限

确保文件具有可执行权限

chmod +x /etc/rc.local

启用并立即启动 rc-local 服务

启动 rc-local 服务,此时可能会弹出警告,可直接忽略

systemctl enable --now rc-local

检查服务状态, 状态显示 active (exited) 表示服务已运行

root@docker:~$ systemctl status rc-local.service
● rc-local.service - /etc/rc.local Compatibility
     Loaded: loaded (/lib/systemd/system/rc-local.service; static)
    Drop-In: /usr/lib/systemd/system/rc-local.service.d
             └─debian.conf
     Active: active (exited) since Fri 2025-05-02 18:21:26 EDT; 3s ago
       Docs: man:systemd-rc-local-generator(8)
    Process: 333116 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
        CPU: 4ms

May 02 18:21:26 docker systemd[1]: Starting rc-local.service - /etc/rc.local Compatibility...
May 02 18:21:26 docker systemd[1]: Started rc-local.service - /etc/rc.local Compatibility.

添加自定义开机脚本

在 /etc/rc.local 的 exit 0 前添加命令。例如:

#!/bin/sh -e
#
# rc.local
#
# By default this script does nothing.
/data/scripts/ip.sh || true
exit 0

注意:使用绝对路径(如 /data/scripts/ip.sh),确保脚本有执行权限。|| true 可防止脚本失败影响。

测试配置

重启系统

总结

通过以上步骤,你可以在 Debian 9 及以上版本快速启用 /etc/rc.local,实现开机自动运行脚本。尽管 systemd 提供更现代的方案,rc.local 仍适合简单任务


深入浅出 MinIO:身份管理与权限配置实战

作者ysicing
2025年5月2日 01:21

前面刚刚讲了如何搭建 MinIO,本文趁热打铁手把手教你如何配置 MinIO 权限配置。对于略懂 MinIO 的用户,配置权限可能是个挑战:如何安全地让别人读取存储内容,但不能列出所有存储桶或文件列表?或者让某个存储桶的内容可以列出? 本文将深入讲解 MinIO 的身份管理和权限配置,聚焦存储桶权限(private、public、custom)的区别和 匿名访问 的应用,通过清晰的场景示例,教你实现安全分享,同时保护数据

MinIO 身份管理基础

MinIO 的身份管理负责用户认证和授权,默认使用内置身份提供者(IDP)。核心概念包括:

  1. 用户(User)
    用户通过 Access Key(用户名)和 Secret Key(密码)访问 MinIO,可用命令行工具 mc 或 Web 控制台管理。
  2. 服务账号(Service Account)
    服务账号是为应用程序设计的专用凭证,无法登录控制台,但可通过 API 访问资源,适合自动化脚本或服务集成。
  3. 策略(Policy)
    JSON 格式的策略定义用户、服务账号或匿名访问对存储桶(Bucket)和对象(Object)的权限,基于 AWS S3 语法。
  4. 匿名访问(Anonymous Access)
    允许未认证用户(无 Access Key)通过 URL 或 API 访问特定资源,需通过存储桶权限配置。

用户组:可通过 mc admin group 批量管理用户权限,本文不展开。

我们的目标是:安全地让匿名用户或服务账号读取特定内容,限制列出存储桶或文件列表,或有选择地允许列出某个存储桶的内容。

准备工作

  1. 确保 MinIO 运行:假设 MinIO 部署在 http://localhost:9000,管理员账号为 homes4,密码为 aiy0ooCheephai0ohNahmu3Aijee6eiv
  2. 安装 mc 工具:下载 MinIO 客户端(mc),用于配置权限(支持 Windows、Mac、Linux)。
  3. 配置 mc
mc alias set homes4 http://localhost:9000 homes4 aiy0ooCheephai0ohNahmu3Aijee6eiv
Added `homes4` successfully.

准备好后,我们开始配置权限!

PS:命令行方式和可视化操作效果是一样的,下文会穿插着来,但是主要还是以可视化 web 操作为主

存储桶权限:Private、Public 和 Custom

MinIO 的存储桶权限控制匿名访问行为,分为 privatepubliccustom 三种模式,可通过 mc anonymous 命令设置。以下是它们的区别

Private(私有)

  • 定义:禁止所有匿名访问,仅允许认证用户或服务账号(有 Access Key 和策略授权)访问。
  • 适用场景:保护敏感数据,如内部文档、用户数据。
  • 效果:匿名用户访问存储桶或对象时,返回 403 Forbidden
  • 配置:mc anonymous set none homes4/web

Public(公开)

注意:风险较高,容易暴露所有文件,慎用。(我个人基本不用)

  • 定义:允许匿名用户访问,权限包括:

    • download:只读(s3:GetObject)
    • upload:只写(s3:PutObject)。
    • public:读写均可。
  • 适用场景:分享公开资源,如网站静态文件、开源软件。

  • 配置(只读):mc anonymous set download homes4/web

  • 效果:匿名用户可通过 URL(如 http://localhost:9000/web/file.jpg)读取对象,可能列出文件列表(若未限制)。

Custom(自定义)

推荐用

  • 定义:通过 JSON 策略精确控制匿名访问权限,如限制特定路径或操作。
  • 适用场景:部分公开,如只分享某个文件夹,或禁止列出文件列表。
  • 配置:见下文场景示例。
  • 效果:灵活性最高,匿名用户只能执行策略允许的操作。

存储桶权限区别总结

模式 匿名访问权限 适用场景 配置命令
Private 禁止匿名访问 敏感数据 mc anonymous set none
Public 读、写或读写(看设置) 公开资源 mc anonymous set download upload/public
Custom 自定义(JSON 策略) 部分公开、精确控制 mc anonymous set-json

实战:安全分享存储内容

通过一个最常见的场景,教你如何:

  • 让匿名用户只读特定文件,禁止列出存储桶或文件列表。

此外,还会写如何用服务账号为应用程序提供类似权限。

新建存储桶

先创建一个存储桶,默认创建的存储桶都是私有权限

root@docker:~$ mc mb homes4/cli
Bucket created successfully `homes4/cli`.
root@docker:~$ mc ls homes4
[2025-05-01 11:26:11 EDT]     0B cli/
[2025-05-01 11:23:57 EDT]     0B ddd/
[2025-05-01 10:38:53 EDT]     0B homes4/
[2025-05-01 11:29:06 EDT]     0B web/

场景:只读特定文件,禁止列出存储桶或文件列表

需求:存储桶 web 包含 public/photo.jpg 和 private/secret.pdf。想让匿名用户只读 photo.jpg,但不能列出 web 桶中 的文件列表,也不能访问其他文件或存储桶

步骤:

设为 Private(默认安全)

mc anonymous set none homes4/web

创建自定义策略:只允许匿名读取 public/photo.jpg

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": ["s3:GetObject"],
      "Effect": "Allow",
      "Resource": ["arn:aws:s3:::web/public/photo.jpg"],
      "Principal": "*"
    }
  ]
}
  • s3:GetObject:允许读取对象
  • Resource:精确到 photo.jpg, 例如你想某个目录读写 arn:aws:s3:::web/public/*
  • Principal: "*":表示匿名用户
  • s3:ListBucket:禁止列出文件列表

应用策略

mc anonymous set-json custom.json homes4/web

控制台可以直接编辑存储桶的 Access Policy,改成 Custom,内容和上面一致

查看策略

root@docker:~$ mc anonymous get-json homes4/web
{
 "Statement": [
  {
   "Action": [
    "s3:GetObject"
   ],
   "Effect": "Allow",
   "Principal": {
    "AWS": [
     "*"
    ]
   },
   "Resource": [
    "arn:aws:s3:::web/public/photo.jpg"
   ]
  }
 ],
 "Version": "2012-10-17"
}

效果

  • 匿名用户可通过 http://localhost:9000/web/public/photo.jpg 下载 photo.jpg。
  • 其他访问都是 403

服务账号:为应用程序配置相同权限

需求:为应用程序(如网站后端)提供只读 public/photo.jpg 的权限,类似上述场景,但通过服务账号实现

步骤:

创建用户(服务账号需绑定到用户):

密码长度需要 8-40

mc admin user add homes4 app1user app1pass

web 可视化操作,policy 那里随便选个小权限的,后面需要调整

创建服务账号:为 app1user 生成服务账号,绑定 custom.json 策略

mc admin user svcacct add homes4 app1user --access-key svc1 --secret-key svc1pass --policy custom.json

可视化操作创建服务状态,凭证信息会随机生成,且只显示一次

策略信息只能在生成 access key 后才能编辑操作

实用技巧:兼顾安全与便利

  • 优先 Custom 模式:比 public 安全,精确控制分享内容。
  • 避免 Public 模式:除非真想完全公开,否则可能暴露所有文件。
  • 检查权限:定期用 mc anonymous get-json homes4/web 确认存储桶权限
  • 随机存储桶名:用随机名,降低被猜到风险
  • 用预签名 URL 临时分享:生成带有效期的链接,过期失效:
# 分享下载链接,下载速度限制10MB/s有效期7天
mc share download --limit-download 10MB homes4/web/private/secret.pdf

总结

MinIO 的权限配置简单而灵活,用服务账号拥有某个存储桶的只读权限就行了。


超简单!5分钟用群晖搭建 MinIO + Caddy 对象存储

作者ysicing
2025年5月1日 18:12

对象存储和 Web 服务是开发者必备工具。MinIO 凭借高性能和 S3 兼容性成为存储领域的“顶流”,Caddy 则以自动 HTTPS 和极简配置深受开发者喜爱。结合 Docker Compose,只需 5 分钟,你就能搭建一个安全、高效的对象存储服务!本文手把手教你部署 MinIO + Caddy。

前提要求

  • 群晖/大盘鸡(大硬盘 VPS):运行 MinIO 服务。
  • 大带宽机器(如腾讯云锐驰 200):运行 Caddy,代理 MinIO 服务。
  • 组网服务(如 Tailscale/EasyTier):确保内网互联互通

我的网络环境通过组网服务实现全链路打通,家里的群晖与腾讯云锐驰无缝互联,Caddy 代理内网 MinIO 服务,借助锐驰大带宽对外提供高效访问。

为啥选择 MinIO + Caddy

  • MinIO:开源、S3 兼容、支持分布式存储,轻松应对海量数据。
  • Caddy:自动 HTTPS、配置简洁,专为高并发优化,支持丰富插件。
  • Docker Compose:一键部署多容器,省时省力。

组合优势:Caddy 为 MinIO 提供安全访问和负载均衡,Docker Compose 确保部署简单,完美适配静态文件托管、API 服务或私有云存储。

5 分钟快速部署

根据需求,可选择在同一机器上部署或分布式部署。我因跨机器需求,选择分布式部署。

部署 MinIO

群晖已支持 Docker Compose,通用配置如下:

  • docker-compose.yaml
version: "3"
services:
  minio:
    image: bitnami/minio:2025
    # image: ccr.ccs.tencentyun.com/k7scn/minio:2025
    container_name: minio
    restart: always
    environment:
      - MINIO_ROOT_USER=homes4
      - MINIO_ROOT_PASSWORD=aiy0ooCheephai0ohNahmu3Aijee6eiv
      - MINIO_DEFAULT_BUCKETS=homes4
    ports:
      - '9000:9000'
      - '9001:9001'
    volumes:
      - '/volume1/docker/minio/data:/bitnami/minio/data'

注意:可能遇到目录权限问题,可运行以下命令解决:
chmod 777 /volume1/docker/minio/data -R

访问 MinIO 管理界面

在浏览器输入 http://NAS_IP:9001http://域名:9001,进入 MinIO 登录页面,使用 MINIO_ROOT_USERMINIO_ROOT_PASSWORD 登录。

安装客户端

提供 Linux/amd64 的 mc 客户端下载链接:

https://c.ysicing.net/oss/tiga/linux/amd64/mc

或从官网获取最新客户端 Install mc

部署 Caddy

  • docker-compose.yaml
services:
  caddy:
    image: ysicing/caddy2
    # image: ccr.ccs.tencentyun.com/k7scn/caddy2
    container_name: caddy
    restart: always
    # 可选host或者端口映射
    network_mode: host
    volumes:
      - '/data/caddy/cfg:/etc/caddy'
      - '/data/caddy/data:/data'
      - '/data/caddy/config:/config'
      - '/data/caddy/log:/var/log/caddy'

此 Caddy 镜像为我的定制版,内置以下常用插件:

xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/caddy-dns/tencentcloud \
    --with github.com/caddy-dns/alidns \
    --with github.com/caddy-dns/dnspod \
    --with github.com/ysicing/caddy2-geocn \
    --with github.com/mholt/caddy-l4 \
    --with github.com/mholt/caddy-ratelimit

Caddy 配置

配置文件位于 /data/caddy/cfg,目录结构如下:

/data/caddy/cfg# tree
.
├── Caddyfile
├── load.sh
└── site
    ├── cr.caddy
    ├── dev.caddy
    ├── hub.caddy
    ├── http.caddy
    └── minio.caddy

Caddyfile 示例

(LOG) {
	log {
		output file "{args[0]}" {
			roll_size 50M
			roll_uncompressed
			roll_local_time
			roll_keep 3
			roll_keep_for 7d
		}
		format json
	}
}

(COMCFG) {
	encode zstd gzip
}

(ERR) {
	handle_errors {
    	# 异常重定向
		redir https://dxgw-{err.status_code}.caddy.local
	}
}

{
	debug
	# admin off
}

(TLS) {
tls {
  dns tencentcloud {
    secret_id AKID***
    secret_key CH85***
  }
}
}

import /etc/caddy/site/*.caddy

MinIO 配置

minio.caddy

域名 {
	import ERR
    # 如果是内网域名可以设置import TLS开启dns签发证书
    # import TLS
    import LOG "/var/log/caddy/minio.log"
	@rootPath {
		path /
	}
	handle @rootPath {
		respond "EdgeONE 451 Forbidden" 451
	}
    # 内网minio地址
    reverse_proxy 100.90.80.2:9000
}

同理,minio 控制台也是一样,通常控制台不对外开放,仅限内网访问。

minio-api.caddy

域名 {
	import ERR
    # 如果是内网域名可以设置import TLS开启dns签发证书
    # import TLS
    import LOG "/var/log/caddy/minio-api.log"
    @denied not remote_ip 192.168.1.0/24
    respond @denied "Access Denied" 403
    # 内网minio api地址
    reverse_proxy 100.90.80.2:9001
}

重新加载配置

curl "http://localhost:2019/load" -H "Content-Type: text/caddyfile" --data-binary @Caddyfile

使用 MinIO

配置 MinIO 客户端(mc)以访问服务:

# 内网
mc alias set home http://100.90.80.2:9000 homes4 aiy0ooCheephai0ohNahmu3Aijee6eiv
# 外网
mc alias set home https://域名 homes4 aiy0ooCheephai0ohNahmu3Aijee6eiv

更多场景可结合 restic,rclone,下载服务

写在最后

通过 MinIO 和 Caddy 的组合,你可以快速搭建一个高性能、安全的对象存储服务。本文提供的配置仅供参考,MinIO 还有更多玩法等待探索!


从某一个行业来看受 AI 影响的部分因素理解

作者
2025年5月1日 00:26
加载中

前言

在当今的数字化时代,优秀的用户体验已经成为互联网产品成功的关键因素之一。真正优秀的前端开发者,不仅要有扎实的技术功底,更要能够设计出如同 Apple 官网产品介绍般精致、流畅且极具吸引力的体验,为用户带来无与伦比的视觉与交互享受。然而,在国内独特的环境下,前端项目的评价标准却显得有些独特。

现状的枷锁

代际差​

在国内,对于一个前端项目,评价标准往往并非从美观、交互、设计、价值等专业角度出发,而是“领导满意的设计才是最好的设计”。这一现象背后有着深层次的原因。领导们通常更注重项目的稳定性和安全性,这就是守江山的思维,担心新事物带来不可控的风险,往往尽量在位期间不出错平稳度过。但掌权者又存在恋权的情况,因此审美迟迟无法提高。

畸形生态​

以政府部门的官方网站为例,很多网站页面设计多年来变化不大,依然是传统的布局和样式。因为领导们希望网站能够稳定运行,不出差错,所以宁愿选择保守的方案,也不愿意冒险采用新的设计理念和技术。掌权者这种恋权且求稳的心态,使得他们审美提升的动力不足,秉持着“能用就行”的策略。在这样的氛围下,前端开发者不得不迎合领导的需求,减少学习成本,采用更保守的开发方案。

马太效应​

这种“能用就行”的策略,直接导致前端行业的要求逐渐下降。当项目对美观和交互的要求不高时,开发者就无需花费过多时间和精力去钻研前沿的设计和技术。教育机会和前瞻技术的获取逐渐减少,形成了一个恶性循环。前端开发者难以接触到新的知识和理念,技术水平难以提升,进而导致整个行业的竞争力下降,工资水平也持续偏低。人们向来用行业薪资来评价行业的等级,因此前端逐渐在后端面前失去了竞争力。

此消彼长

我国互联网发展的初期,后端平台的建设是重中之重,就像新中国大基建需要建设坚固的地基一样。在这个阶段,后端开发者承担着搭建服务器、数据库、后台系统等重要任务,他们是互联网行业的基石。大量的资源和人才都投入到了后端开发中,以支撑互联网业务的快速发展。例如,阿里巴巴等电商巨头的崛起,离不开强大后端系统的支持,它们需要处理海量的交易数据、保证系统的稳定性和安全性。

然而,随着互联网行业的不断发展,后端平台搭建基本饱和。像一些传统的企业管理系统、电商平台后端,技术架构相对成熟,不会再有大量新的开发需求。此时,互联网企业为了降低成本,开始进行裁员,只保留少量的维护人员即可。这就是一个典型的行业发展过程,从快速发展到逐渐饱和,不再需要过多开发人员,互联网企业开始裁员,只保留维护的人才即可。

机遇与挑战

当人们的审美水平逐渐提高时,前端开发者的价值才有可能真正凸显。就像苹果公司通过其精致的产品设计和用户体验,引领了全球科技产品设计的风尚。如果消费者对产品的审美和要求提高,那么企业和组织就会更加重视前端开发。

然而,AI 的快速发展给前端开发者带来了巨大的挑战。AI 技术的进步使得它能够快速生成代码、设计页面,甚至能够根据用户需求自动生成交互方案。我国是一个人口大国,每个人的技术水平和发展程度差异很大。在 AI 快速发展的背景下,对未来各行各业的影响都不容忽视。当教育程度跟不上 AI 发展程度时,这就是一个很大的矛盾存在。

结语

AI 本是帮助人们更好工作的工具,然而仅仅对于本就很厉害的人才是工具。AI 真正取代的不是行业,而是行业里的普通人。

从心所欲

作者Liyun
2025年4月30日 15:28

吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩

年轻的时候并不是很能读懂这段话,年纪大了才恍然时间快进。

现在的日子基本上是 知天命、耳顺、从心所欲的阶段了。每天看着聒噪的人群,我已经心生羡慕。和长者的间隙已经远远小于和二十多岁年轻人的代沟。是从什么时候开始,经历了什么,让我的人生越来越勇敢,也越来越无所谓了呢。

初心还在吗,还在的。梦想还在吗,还在的。那一束束不曾泯灭的光亮,闪耀着年少与年长。

放下的越多,只能说明人越来越老,力不从心。跨越千山万里,只为回眸一笑,是一种人生。和猫一起睡到天昏地暗,也是一种人生。习惯了疯狂之后,人生好像也没什么界限了。

从心所欲,不逾矩,却也不设限。

五一大盘大带宽物理服务器活动推荐

作者ysicing
2025年4月30日 09:39

之前,也有小伙伴让我推荐几家物理服务器(后面简称杜甫), 趁着五一假期有活动,推荐两家我都在用的,各有优势,但是活动期价格会有优惠。

物语云

优点

  • 工单响应速度快
  • 有 DDoS 攻击防护
  • 大带宽流量套餐/上行限速的不限流量套餐(账单日可互换)
  • 测试不满意满足条件可退款

不足

  • 多台机器暂不支持组内网
  • 暂不支持 ipv6
  • 暂时只支持国内
  • 只支持操作系统安装

目前活动

立减优惠码 Event8259

物语云计算物理机活动:双路铂金物理机600G防护399/月起。
🎉限时立减100循环优惠码:
Event8259
※优惠码使用流程:登录账号后选择对应产品,在下单处点击“我有优惠码”之后填入。

硬件配置:
物理服务器|NVMe SSD|资源完全独享|自助管理面板
CPU:双路铂金Platinum8259CL 48核96线程
内存:128G RECC DDR4
硬盘:1TB NVMe SSD

网络规格:
以下四种网络规格可选,价格相同:
 - 浙江宁波/电信/50Mbps独享/100G防护/不限流量
 - 湖北十堰/电信/50Mbps独享/600G防护/不限流量
 - 浙江宁波/移动/50Mbps独享/10G防护/不限流量
 - 浙江宁波/电信/500Mbps流量计费/100G防护/双向2TB

优惠活动:
销售价:1150/月 优惠价:499/月
活动价:399/月,循环优惠,续费同价。

※上述产品均为独享带宽,并非“峰值”、“共享”带宽,可7x24小时随时跑满,拒绝带宽竞技场!

<服务协议与退款>
云服务器产品 24 小时内上行方向使用不超过30GB流量均支持退款,
物理服务器产品 24 小时内上行方向使用不超过100GB流量均支持退款,每用户每月最多退款 1 次。

购买链接 物语云杜甫

测评

具体可以参考 https://www.nodeseek.com/post-303956-1,带宽是可以长时间跑满的。

狗云

多年资深狗云老用户了,目前我只用他们家的 KC 杜甫、重庆联通杜甫

优点

  • 相比较其他杜甫首发优惠力度很大
  • 预购活动多
  • 香港大带宽流量套餐
  • 支持多台机器组网
  • 支持 ipv6, 掩码 /64
  • 安装系统支持种类多
  • 支持快递硬盘

缺点

  • 只支持工单响应(可以 PY 德克狗老板)
  • 杜甫 VNC 需要工单才能开,且有时间限制(每次半小时,记得不太清了)
  • 重装每小时限制一次
  • 需要实名

目前活动

预售活动

预售活动力度大些,折扣也比较高, 走 aff 成功绑定后返利一半

预售地址 二代铂金服务器预售

主要说一下网络规则,支持切换每次手续费 20, 推荐优化线路,优化线路可以附加国际线路,但是不支持附加精品线路

网络规格:
以下三种网络规格可选,价格相同:
 - 精品线路 75 Mbps, 额外附加20元/个/月
 - 优化线路  750 Mbps,额外附加15元/个/月
 - 国际线路  1000 Mbps,额外附加10元/个/月
 - 额外附加高防IP200元/个/月

五一 · 劳动节促销

活动一:
折扣码“51”,新开弹性云7折,新开经典云(特价机除外)8折。
折扣码“jian100”,新开独立服务器优惠100元。

活动二:
5月1日-5月5日,单笔充值每满100元送10元。

活动三:
5月1日-5月5日,幸运大转盘每日抽取5折码,流量,余额等奖品。

活动四:
二代铂金服务器预售:https://ds.dogyun.com/server/preorder

香港-KC物理服务器邮件硬盘添加活动将在5月10日开始接收

测评 KC

具体可以参考 https://www.nodeseek.com/post-314492-1

其他 CQA

他们家的老款绝版 CQA 月付 200 还是挺不错的,稳定的很,可以收一收。

最后

祝各位老板五一假期玩的开心。


欢迎关注我的微信公众号

探索DeepWiki:代码世界的智能百科全书

作者斌仔
2025年4月29日 17:33

在当今的软件开发领域,高效理解和管理代码库是开发者面临的重要挑战之一。DeepWiki作为一款新兴的工具,为解决这一问题提供了创新的解决方案。本文将深入探讨DeepWiki是什么、有哪些特点、能做什么,以及它的官网、免费在线体验途径、常见问题、应用场景与影响、局限与挑战,还有与其他工具的对比,并在最后进行总结和测试。

一、DeepWiki是什么

DeepWiki 是一个免费、开源的GitHub代码库百科全书,由AI领域的知名公司Cognition AI于2025年4月开发并推出。它利用先进的AI技术,特别是大型语言模型(LLM),将任何公开的GitHub代码库转化为结构化、互动式的维基百科式知识库,系统性地解读代码的结构、逻辑与设计。就像是一位“超级智能的图书管理员”,DeepWiki为开发者阅读、分析每一行代码,并用清晰易懂的语言解释复杂的概念。
DeepWiki将GitHub代码库转化为结构化知识库的概念图

二、DeepWiki的特点

1. 开放与共享

DeepWiki 具有免费和开源的特性,这表明它致力于降低知识获取门槛,服务广大开发者社区。公共代码库可以免费使用,无需注册,同时它也支持私有库(需付费授权),能够满足企业的不同需求。

2. AI驱动

该工具利用先进的AI技术深入理解代码语义,而不仅仅是进行表面分析。它集成了由AI软件工程师Devin支持的对话式AI助手,用户可以使用自然语言提问关于代码的任何问题,AI会基于对代码库的深度理解,直接从代码中提取信息,提供清晰、上下文相关的答案。

3. 结构化与互动式

DeepWiki能将代码库转化为结构化、互动式的知识库,提供交互式代码图谱,如可点击、可缩放的类层次结构图、依赖关系图、工作流程图等。这些图谱比纯文本更能直观地揭示代码结构和关联,用户可以交互式地探索连接。
DeepWiki的交互式代码图谱示例

4. 多维度代码洞察

DeepWiki 提供全方位的代码洞察,包括智能生成文档、交互式代码图谱、智能问答、深度研究能力等,能够满足不同开发者的需求。

三、DeepWiki能做什么

1. 智能生成文档

DeepWiki可以自动分析代码文件、README、配置文件等,利用AI理解代码逻辑,生成结构化、详细且易读的知识库文档。其内容涵盖功能描述、技术栈、依赖关系、文件结构和模块说明,节省了手动编写文档的时间,解决了文档缺失或过时的问题。
DeepWiki智能生成代码库文档的过程

2. 交互式代码图谱

它能生成可点击、可缩放的类层次结构图、依赖关系图、工作流程图等可视化图表,直观地揭示代码结构和关联。还能智能生成架构图,并尝试标记潜在的设计缺陷,辅助代码审查。

3. 智能问答

集成的对话式AI助手让用户可以用自然语言提问关于代码的任何问题,AI基于对代码库的深度理解提供答案,如同拥有一位“AI高级工程师”随时提供技术指导。
用户使用DeepWiki的智能问答功能与AI助手交互

4. 深度研究能力

为高级用户提供“深度研究”查询,进行更复杂的分析,如发现潜在错误、提出优化建议、比较不同代码库等,帮助用户深入理解设计理念、最佳实践和潜在优化方向。

5. 按需索引

如果开发者关心的公开仓库还未被DeepWiki收录,只需通过简单的请求操作,DeepWiki便会为其进行索引。

6. 轻松分享

生成的Wiki页面和问答结果都可以通过链接方便地分享,在团队协作场景中能确保整个团队的信息同步,提升协作效率。

四、DeepWiki的官网

DeepWiki的官网是deepwiki.com ,用户可以通过官网直接访问,探索已经收录的热门开源项目的Wiki。也可以将GitHub链接中的「github.com」替换为「deepwiki.com」,无缝跳转到该仓库的DeepWiki页面。此外,DeepWiki.directory作为一个专门的导航网站,提供了便捷的搜索和发现功能,帮助用户按照编程语言、流行度或用途浏览各种项目。
DeepWiki官网页面展示

五、免费在线体验途径

对于公共GitHub代码库,用户无需注册即可免费使用DeepWiki。体验方式如下:

1. 官网访问

直接访问deepwiki.com ,通过名称或URL搜索代码库。

2. URL替换

  • 在GitHub URL 中将 github.com 替换为 deepwiki.com 即可跳转,无缝集成现有工作流。例如 github.com/owner/repo 变为 deepwiki.com/owner/repo。
  • 把 com 改为 pm 直接跳转对应 deepwiki。例如:https://github.com/mark3labs/mcp-go 变为 https://github.pm/mark3labs/mcp-go

3. 第三方脚本

社区开发的Tampermonkey脚本可在GitHub页面添加“Go DeepWiki”按钮,实现一键访问。脚本地址:GitHubGreasyFork

六、常见问题

1. DeepWiki支持哪些代码库?

目前主要支持GitHub上的公共和私有代码库。

2. 使用DeepWiki需要安装什么软件吗?

不需要,用户只需通过浏览器访问官网或替换URL即可使用。

3. 私有仓库如何使用DeepWiki?

私有仓库需要付费授权,开发者可以通过注册Devin账户(devin.ai),将私有仓库与账户关联,从而享受DeepWiki为私有仓库生成文档、提供问答等服务。

4. 如果我关心的仓库还未被索引怎么办?

对于公开仓库,用户可请求DeepWiki进行索引;对于私有仓库,需先完成授权关联等操作。

5. AI生成的内容准确吗?

AI生成的内容可能需要人工验证以确保精确性,尤其是在复杂项目的文档方面。
用户咨询DeepWiki常见问题的场景

七、应用场景与影响

应用场景

1. 开发者入职

帮助新开发者快速了解项目结构,缩短学习曲线,更快地融入项目开发。

2. 代码审查与理解

帮助团队审查贡献或理解复杂代码段,提高代码审查效率。

3. 教育资源

为学生和有抱负的开发者提供关于真实代码库和最佳实践的见解,辅助学习编程。

4. 项目评估

使开发者能够在采用或贡献前评估项目质量和结构,做出更明智的决策。

5. 企业内部知识管理

帮助企业团队实现私有代码库文档的自动化,提高协作效率,降低知识传递成本。

影响

1. 提升开发者效率

通过自动化生成结构化文档、提供交互式代码图谱以及即时响应的AI问答,极大地缩短了开发者熟悉代码所需的时间,使他们能够更专注于解决更具挑战性与创造性的核心问题,提升个人与团队的整体生产力。

2. 推动开源协作

降低阅读和理解源代码的难度,使得来自不同背景和经验水平的开发者都能更容易地接触并理解开源项目,拓宽了贡献者的来源,为开源生态注入了新的活力。

3. 促进技术教育

为学生和初学者提供了学习优秀开源项目的平台,有助于培养更多优秀的开发者。
DeepWiki在开发者入职、代码审查、教育等应用场景中的作用

八、局限与挑战

1. 范围限制

不支持搜索GitHub Issues或Pull Requests,限制了某些用例,例如在进行代码审查时无法直接查看相关的讨论和修改记录。

2. 私有仓库使用门槛

私有仓库访问需要注册,对小团队可能是一个障碍,增加了使用成本和管理复杂度。

3. 准确性问题

AI生成的内容可能需要人工验证以确保精确性,特别是在处理复杂项目时,文档的准确性可能存在波动。

4. 中文支持不足

中文项目文档质量波动较大,关键术语翻译存在歧义,影响了国内开发者的使用体验。

5. 动态更新延迟

提交历史同步存在15 - 30分钟延迟,不能及时反映代码库的最新变化。

6. 生态整合不足

与Jira、Confluence等协作工具的联动尚未打通,不利于在现有工作流程中集成使用。
DeepWiki面临的局限与挑战

九、与其他工具对比

工具 主要功能 显著特点 与DeepWiki对比
DeepWiki AI驱动的代码文档 动态文档、自然语言查询、深度研究 专注于代码库文档生成、分析和交互,提供多维度的代码洞察和智能问答功能,支持公共和私有代码库,但闭源商业产品,使用成本较高,中文支持和生态整合有待加强。
GitHub Copilot AI辅助代码完成 实时编码建议 专注于实时代码生成,在编码过程中提供建议,与DeepWiki的文档重点互补。
传统文档工具 静态代码文档 手动维护,更新缓慢 静态的README或wiki落后于DeepWiki的动态更新,无法提供实时的代码分析和交互功能。
其他AI文档工具 部分代码文档 范围有限,无中央平台 缺乏DeepWiki的全面、集中的仓库覆盖范围和多维度的代码分析能力。
KoalaWiki AI驱动代码知识库平台 完全开源、本地部署、多模型支持、定制化能力强 作为开源替代品,提供了与DeepWiki相似的功能,但具有完全开源、本地部署、多模型支持、定制化能力强等优势,数据安全性更高,使用成本更低。

十、总结及测试

DeepWiki作为一款创新的代码库百科全书工具,具有诸多显著的优势。它通过AI技术将GitHub代码库转化为结构化、互动式的知识库,为开发者提供了智能生成文档、交互式代码图谱、智能问答等多维度的代码洞察功能。在开发者入职、代码审查、教育等多个应用场景中都能发挥重要作用,提升了开发者效率,推动了开源协作,促进了技术教育。然而,它也面临着一些局限与挑战,如范围限制、私有仓库使用门槛、准确性问题等。

在与其他工具的对比中,DeepWiki展现出了独特的功能和特点,但也存在一些不足之处。与GitHub Copilot互补,比传统文档工具和其他AI文档工具更具优势,但与KoalaWiki相比,在开源性和定制化能力方面还有提升空间。

为了更好地了解DeepWiki的实际效果,建议开发者亲自进行测试。可以通过官网免费在线体验公共代码库的功能,也可以付费授权使用私有仓库。在测试过程中,关注其功能的实用性、准确性以及与自身工作流程的适配性。通过实际使用,开发者可以更全面地评估DeepWiki是否适合自己的需求,从而做出更明智的选择。

Qwen3:大型语言模型的新里程碑(内含免费API)

作者斌仔
2025年4月29日 17:02

在人工智能飞速发展的今天,大型语言模型不断推陈出新,为各个领域带来了前所未有的变革。Qwen3作为Qwen系列大型语言模型的最新成员,凭借其卓越的性能和丰富的功能,成为了众多开发者和企业关注的焦点。本文将为您详细介绍Qwen3,包括它是什么、有哪些特点、能做什么、官网信息、免费在线体验方式、模型及价格、常见问题、如何部署到本地、硬件要求以及使用教程资源等方面。

Qwen3大型语言模型

一、Qwen3是什么

Qwen3 是Qwen系列大型语言模型的最新力作。该系列推出了多个不同参数规模的模型,涵盖了开源的两个MoE模型(Qwen3 - 235B - A22B和Qwen3 - 30B - A3B)以及六个Dense模型(Qwen3 - 32B、Qwen3 - 14B、Qwen3 - 8B、Qwen3 - 4B、Qwen3 - 1.7B和Qwen3 - 0.6B)。这些模型均在Apache 2.0许可下开源(Github开源地址),这意味着开发者可以免费下载、使用这些模型,还能将其用于开发商业产品,为开源社区和企业应用提供了极大的便利。

二、Qwen3的特点

1. 多种思考模式

Qwen3多种思考模式
Qwen3 支持思考模式和非思考模式。在思考模式下,模型会逐步进行推理,这种模式非常适合处理复杂问题,能够深入分析问题并给出准确的答案。例如,在解决数学难题、进行逻辑推理等方面,思考模式可以发挥出强大的优势。而非思考模式则提供快速响应,适用于对速度要求较高的简单问题,比如日常的简单问答、信息查询等。这两种模式的结合增强了模型“思考预算”的控制能力,用户可以根据实际需求在成本效益和推理质量之间实现更优的平衡。而且,在这两种模式之间切换时,模型几乎不损失性能,真正做到了 “一脑双模,稳定输出”。

2. 多语言支持

Qwen3多语言支持
Qwen3支持119种语言和方言,涵盖了印欧语系、汉藏语系、亚非语系、南岛语系、德拉威语系、突厥语系、壮侗语系、乌拉尔语系、南亚语系等多个语系。这一特性为国际应用开辟了新的可能,使得不同语言背景的用户都能够使用Qwen3进行交流和协作,打破了语言障碍,促进了全球范围内的信息共享和沟通。

3. 增强的Agent能力

Qwen3增强的Agent能力
Qwen3优化了Agent和代码能力,加强了对MCP的支持,在工具调用能力方面表现出色。它原生支持强大的工具调用能力,能够灵活地调用外部API或工具来完成各种任务,比如查询天气、预订机票、操作软件等。这使得Qwen3不仅是一个语言交互工具,还可以成为用户的智能助手,帮助用户完成各种实际操作。

4. 预训练数据扩展

Qwen3预训练数据扩展
与Qwen2.5相比,Qwen3的预训练数据集显著扩展,使用了约36万亿个token,并且同样涵盖了119种语言和方言。在构建数据集时,Qwen3不仅从网络收集数据,还从PDF文档中提取信息,并利用专家模型合成数学和代码数据。这种多元化的数据来源使得Qwen3能够学习到更广泛、更深入的知识,从而提高其性能和泛化能力。

5. 训练阶段合理

Qwen3训练阶段
Qwen3的预训练分为三个阶段,逐步提升模型的能力。后训练实施四阶段训练流程,通过这种科学合理的训练方式,开发出了具备思考推理和快速响应能力的混合模型。这种训练模式使得Qwen3在不同场景下都能够表现出色,既能够深入思考复杂问题,又能够快速响应用户的简单需求。

6. 性能强大

Qwen3性能强大
Qwen3采用混合专家(MoE)架构,总参数量达到235B,但激活仅需22B。评测显示,Qwen3在推理、指令遵循、工具调用、多语言能力等方面均大幅增强,创下了所有国产模型及全球开源模型的性能新高。例如,在奥数水平的AIME25测评中,Qwen3斩获81.5分,刷新了开源纪录;在考察代码能力的LiveCodeBench评测中,Qwen3突破70分大关,表现甚至超过了Grok3;在评估模型人类偏好对齐的ArenaHard测评中,Qwen3以95.6分超越了OpenAI - o1及DeepSeek - R1。这些优异的成绩充分证明了Qwen3的强大性能。

7. 成本降低

Qwen3成本降低
Qwen3的参数量仅为DeepSeek - R1的1/3,成本大幅下降。其部署成本仅为同等性能的DeepSeek - R1的35% ,仅需4张H20即可部署Qwen3满血版,显存占用也仅为性能相近模型的三分之一。这使得Qwen3在成本效益方面具有明显的优势,对于企业和开发者来说,能够以更低的成本获得更强大的模型性能。

8. 开源免费商用

Qwen3开源免费商用
从参数量0.6B的“小不点”到235B的“巨无霸”,Qwen3总共推出了8款不同尺寸的模型,并且全部采用宽松的Apache 2.0协议开源。这意味着开发者可以自由地下载、使用这些模型,无论是进行学术研究还是开发商业产品,都无需担心版权问题。这种开源免费商用的模式为人工智能的发展和应用提供了更广阔的空间。

三、Qwen3能做什么

1. 通用问答

Qwen3通用问答
Qwen3可以回答各种领域的问题,无论是科学知识、历史文化、生活常识还是娱乐八卦等,都能提供准确的答案。在思考模式下,它能够深入推理复杂问题,给出详细的解答和分析;在非思考模式下,它可以快速响应简单问题,满足用户的即时需求。

2. 代码相关任务

Qwen3代码相关任务
在代码领域,Qwen3有出色的表现。它可以进行代码生成,根据用户的需求生成各种编程语言的代码;还能够进行代码理解,对已有的代码进行分析和解释。在考察代码能力的LiveCodeBench评测中,Qwen3突破70分大关,表现甚至超过了Grok3,这充分证明了它在代码处理方面的强大能力。

3. 多语言交流

Qwen3多语言交流
由于 Qwen3 支持119种语言和方言,它能够满足不同语言用户的交流需求。无论是跨国企业的商务沟通、国际学术交流还是个人的跨文化交流,Qwen3都可以作为一个有效的语言桥梁,帮助用户实现无障碍交流。

4. Agent交互

Qwen3 Agent交互
通过 Qwen - Agent,Qwen3 可以进行工具调用,与环境进行交互,完成特定任务。例如,用户可以让 Qwen3 查询天气情况、预订机票、操作软件等。Qwen3 会根据用户的指令,调用相应的外部 API 或工具来完成任务,为用户提供便捷的服务。

5. 创意写作与角色扮演

Qwen3创意写作与角色扮演
Qwen3 具有卓越的人类偏好对齐能力,在创意写作、角色扮演、多轮对话和指令跟随方面表现出色。它可以根据用户的要求创作各种类型的文章,如故事、诗歌、小说等;还可以扮演不同的角色,与用户进行互动,提供更自然、更吸引人和更具沉浸感的对话体验。

四、官网

如果您想了解更多关于Qwen3的信息或试用该模型,可以通过以下途径:

  • Qwen Chat网页版:访问 chat.qwen.ai,在网页上直接试用 Qwen3。
  • 手机APP:可以在手机上下载相应的 APP,随时随地使用 Qwen3。
  • 阿里云百炼:通过阿里云百炼可以调用 Qwen3 的API服务,满足企业级的应用需求。
  • PAI Model Gallery访问地址,该平台支持云上一键部署 Qwen3 全尺寸模型,方便开发者进行模型的部署和使用。

五、免费在线体验

目前,有多种方式可以免费在线体验 Qwen3:

  • Qwen Chat 网页版和手机 APP:您可以在chat.qwen.ai网页版或手机APP中直接试用 Qwen3,感受它的强大功能。
  • 夸克:夸克即将全线接入 Qwen3,届时用户可以在夸克平台上使用 Qwen3。
  • 魔搭社区、HuggingFace等平台:全球开发者、研究机构和企业均可免费在魔搭社区HuggingFace等平台下载Qwen3模型并商用,为开发者提供了更多的选择和便利。
  • Free Qwen3:现已支持 Qwen3-30B-A3B 大语言模型,完全免费。Qwen3-30B-A3B 采用混合专家模型架构,总参数量 300 亿,激活参数量 30 亿,支持 128K 上下文长度,在数学推理、代码生成和通用任务处理方面表现出色。无需注册,高峰时段可能需要短暂排队。

六、模型 & 价格

模型

Qwen3系列提供了多种不同参数规模的开源模型,满足不同用户的需求:

1. MoE模型

  • Qwen3 - 235B - A22B:拥有2350多亿总参数和220多亿激活参数的大模型,是Qwen3系列的性能天花板,各项指标全球领先。它适合对安全性有要求、对性能有极致要求的企业级应用和科研探索,如金融风险评估、复杂科学计算等。
  • Qwen3 - 30B - A3B:拥有约300亿总参数和30亿激活参数的小型MoE模型,性能堪比之前的Qwen2.5 - 32B,但实现了10倍以上的性能杠杆。它适用于消费级显卡部署,非常适合个人开发者、AI爱好者本地部署,以及对性能和成本有均衡要求的场景,如个人智能助手开发、小型企业的智能客服系统等。

2. Dense模型

Qwen3 - 32B、Qwen3 - 14B、Qwen3 - 8B、Qwen3 - 4B、Qwen3 - 1.7B和Qwen3 - 0.6B是传统的稠密型模型,参数量相对较小或中等,适合不同层级的应用和部署需求。例如,Qwen3 - 0.6B可以用于资源受限的设备,如移动终端;而Qwen3 - 32B则可以用于对性能要求较高的场景,如大型企业的数据分析和处理。

Qwen3不同参数规模模型
各模型具体参数如下:

Models Layers Heads (Q / KV) Tie Embedding Context Length
Qwen3 - 0.6B 28 16 / 8 Yes 32K
Qwen3 - 1.7B 28 16 / 8 Yes 32K
Qwen3 - 4B 36 32 / 8 Yes 32K
Qwen3 - 8B 36 32 / 8 No 128K
Qwen3 - 14B 40 40 / 8 No 128K
Qwen3 - 32B 64 64 / 8 No 128K
Models Layers Heads (Q / KV) # Experts (Total / Activated) Context Length
Qwen3 - 30B - A3B 48 32 / 4 128 / 8 128K
Qwen3 - 235B - A22B 94 64 / 4 128 / 8 128K

价格

目前,虽然已知Qwen3成本仅为DeepSeek - R1约三分之一,但尚未查询到具体的定价信息。不过,从其开源免费商用的特点以及成本降低的优势来看,Qwen3在价格方面应该具有一定的竞争力,值得开发者和企业期待。

七、常见问题

目前暂未搜索到关于Qwen3的常见问题相关内容。如果您在使用过程中遇到问题,可以关注官方网站或社区论坛,获取最新的帮助和支持。

八、如何部署到本地

1. 使用通用工具部署

对于本地使用,您可以使用Ollama、LMStudio、MLX、llama.cpp和KTransformers等工具。例如,通过运行简单的命令ollama run qwen3:30b - a3b,就可以使用ollama与模型进行交互。

2. 创建API endpoint

部署时,您可以使用sglang>=0.4.6.post1vllm>=0.8.4来创建一个与OpenAI API兼容的API endpoint:

  • SGLang:使用命令python - m sglang.launch_server --model - path Qwen/Qwen3 - 30B - A3B --reasoning - parser qwen3
  • vLLM:使用命令vllm serve Qwen/Qwen3 - 30B - A3B --enable - reasoning --reasoning - parser deepseek_r1
  • 如果您想禁用思考模式,可以移除参数--reasoning - parser(以及--enable - reasoning)。

3. 在Mac设备上借助Ollama部署

  • 安装Ollama:访问Ollama官方网站(ollama.ai)下载适用于Mac的安装包并进行安装。
  • 拉取Qwen3模型:打开终端,使用 ollama run <model_name> 命令拉取您想要部署的Qwen3模型。<model_name> 会根据Qwen3在Ollama模型库中的命名而定,通常会包含模型尺寸和量化方式,例如 qwen:7b - chat - q4_0。Ollama会自动下载所需的模型文件。
  • 运行模型:模型下载完成后,Ollama会自动启动模型,您就可以在终端中直接与模型进行交互了。您也可以通过Ollama提供的API或与其他支持Ollama的应用进行集成。

4. 使用阿里云PAI Model Gallery进行云上一键部署

  • 在Model Gallery模型广场找到Qwen3系列模型,或通过链接直达该模型。
  • 在模型详情页右上角点击「部署」,已支持SGLang、vLLM高性能部署框架。在选择计算资源后,即可一键完成模型的云上部署。
  • 部署成功后,在服务页面可以点击“查看调用信息”获取调用的Endpoint和Token,想了解服务调用方式可以点击预训练模型链接,返回模型介绍页查看调用方式说明。

九、硬件要求

1. 通用硬件要求

Qwen3的部署成本大幅下降,仅需4张H20即可部署Qwen3满血版,显存占用仅为性能相近模型的三分之一。这使得Qwen3在硬件资源的利用上更加高效,降低了部署的门槛。

2. Mac设备硬件要求

Qwen3在Mac设备上的硬件要求
在Mac设备上部署Qwen3模型时,内存(统一内存)是决定可以运行哪个尺寸模型以及其性能的关键因素:

  • 8GB统一内存:建议加载的模型尺寸在0.6B到8B之间。GGML文件大小通常在0.2GB到4.8GB。运行此类模型时,除了模型本身,系统和KV缓存也需要占用内存,因此仍需给系统保留约1.5GB到8GB内存。对于上下文长度较小的应用(<=2k token)较为适合。推荐使用Q4_0量化的0.6B,1.7B,4B,8B模型。
  • 16GB统一内存:可以考虑加载8B (Q8_0) 或14B (Q4_0)模型,GGML文件大小约为1.6GB到8.7GB。在保证系统运行和KV缓存空间的前提下,可以支持更长的上下文。例如,KV缓存4k tokens大约再吃2 - 3GB内存,仍能并行运行VSCode和Chrome等应用。推荐使用Q8_0量化的8B模型或Q4_0量化的14B模型。
  • 32GB统一内存:建议加载14B (Q8_0)、30B (A3B) 或32B (Q4_0)模型。GGML文件大小在15.7GB到18.7GB。拥有超过10GB的KV缓存空间,支持长上下文。推荐使用Q8_0量化的14B模型,A3B量化的30B模型,或Q4_0量化的32B模型。
  • 64GB统一内存:可以轻松运行32B (Q8_0)、30B (A3B) 或30B (Q5_K/M)模型。GGML文件大小约为37GB或22GB。足够支持128k上下文或同时运行多个模型。推荐使用Q8_0量化的32B模型,A3B量化的30B模型,或Q5_K/M量化的30B模型。
  • 96/128GB统一内存:可以挑战235B - A22B (Q4_0) 或更高精度的32B/30B A3B模型。GGML文件大小约为133GB (Q4_0)。对于235B - A22B Q8_0量化版本,其大小超过250GB,运行可能较为勉强,需要关闭其他大型程序。

十、使用教程资源

1. Hugging Face中使用示例

在Hugging Face transformers中,提供了使用Qwen3 - 30B - A3B的标准示例代码。通过这些代码,您可以实现文本生成等功能,还可以通过修改enable_thinking参数切换思考模式,以满足不同的应用需求。

2. 高级用法

Qwen3提供了软切换机制,允许用户在enable_thinking = True时,在用户提示或系统消息中添加/think/no_think来逐轮切换模型的思考模式。同时,还给出了多轮对话的示例代码,帮助用户更好地掌握这种高级用法。

3. Agent示例

推荐使用Qwen - Agent来发挥Qwen3的Agent能力。官方给出了定义可用工具、定义Agent以及进行流式生成的示例代码,方便用户进行Agent交互开发。

4. Transformers库使用说明

您可以使用pipeline()接口或generate()接口在transformers中用Qwen3生成文本。同时,还给出了使用pipeline进行多轮对话的基本示例代码,并说明了创建pipeline的一些重要参数,帮助用户更好地使用Transformers库与Qwen3进行交互。

综上所述,Qwen3作为一款强大的大型语言模型,具有多种优秀的特点和丰富的功能。无论是对于开发者、研究机构还是企业来说,Qwen3都提供了一个极具吸引力的选择。随着人工智能技术的不断发展,相信Qwen3将在更多的领域发挥重要作用,为我们的生活和工作带来更多的便利和创新。

GitHub 阻止中文用户访问了 吗?(附临时解决方案)

2025年4月29日 14:43

引言

今天群里不小小伙伴都表示在访问 GitHub 时遇到了「对本网站的访问受到限制」 (access to this site has been restricted) 、「访问已被限制」(Access has been restricted)的提示。

之前 GitHub 曾因失误部署了屏蔽所有中国 IP 地址的规则,中国 IP 地址访问时会出现禁止访问提示,之后 GitHub 更新了规则,中国 IP 地址重新可以访问了。GitHub 给出的解释当初是部署错误。

如果之前是失误那现在肯定就是故意的了,这次如果你使用代理访问,并且使用的是中文 (仅限 zh_CN),那么你就有可能被 GitHub 阻止访问。



情况

那么问题来了,GitHub 是打算主动屏蔽所有中国用户吗?
经过我和群内小伙伴们的测试,答案是:应该不是,看起来更像是 GitHub 针对中文爬虫设定的反爬措施。

实际触发这个限制的条件逻辑是:
1. 基于IP或者UA等判断(比如是不是机房IP,代理IP,常见爬虫UA,模拟浏览器头)
2. 基于一些流量模型判断(比如访问频率过高,访问范围过广)
3. 是不是请求头的语言部分包含 zh_CN
4. 只有上边每一层检测,都触发了“是”,那么才会触发访问限制。
5. 并且这个限制是分功能的,不是完全不可用,有可能你可以在浏览器中浏览项目,编辑文件,但你这时却无法在 浏览器内 raw,无法在终端里 git clone。

也就是说,对于正常的中文用户,如果你的IP比较干净,不是使用奇形怪状的浏览器访问,都是可以正常访问 GitHub 的。

碎碎谈

感觉 GitHub 大概率是为了反爬虫、反抓取,毕竟现在 AI 训练爬虫在对 GitHub 疯狂抓取用来训练模型。微软虽然家大业大也没钱了嘛。不过国内爬虫是有多少啊,都能让 GitHub 把语言当作一个过滤条件了。之前那次屏蔽中国IP,搞不好起因也是这个,只是个某个管理错误的把中国的IP段全部给拉黑了。
不过把请求头语言项作为爬虫检查项,意义不大吧,这个特征也不难改……

如果你使用的代理 IP 质量不佳,IP 被万人骑,实在太黑了,导致被 GitHub 拦截了,比较简单的办法就是:
– 换个IP
– 使用一些浏览器请求头修改扩展,将请求头语言部分改成 accept-language = en_US,en;q=0.9,zh;q=0.8 (英语优先,中文备选)。
– 直接去浏览器设置里修改网页首选语言(所有网页都会收到影响,比如不登录状态下谷歌和bing就会给你返回英文网页和英文搜索结果优先了)

以Header Editor 4.1.1 为例,修改请求头

启用请求头修改前,部分位于https://camo.githubusercontent.com的图报HTTP429「Access has been restricted」

启用请求头修改后,马上恢复正常。

The post GitHub 阻止中文用户访问了 吗?(附临时解决方案) appeared first on 秋风于渭水.

沉迷塔防的乐趣:推荐Kingdom Rush系列游戏

作者ysicing
2025年4月28日 20:41

作为一名开发者,我下班后除了写写代码、折腾技术,也喜欢通过游戏放松一下。塔防类游戏是我的最爱,尤其 Ironhide Game Studio(铁皮) 的 Kingdom Rush 系。这系列塔防游戏以扎实的玩法、精致的设计和恰到好处的挑战性,俘获了无数玩家的心。今天,我想向大家安利这个系列,希望你也能爱上它的独特魅力!

Kingdom Rush 系列简介

We make the games we'd love to play!

Steam 直达

Kingdom Rush 系列始于 2011 年的 Flash 游戏《Kingdom Rush》(早期我记得叫 皇家守卫军, 现在普遍翻译为 王国保卫战),随后推出了多款续作,覆盖 PC、移动端(iOS/Android)和主机平台,核心玩法保持一致,同时不断创新。目前包括以为五代作品(衍生品不算):

-《Kingdom Rush》(2011):初代经典,奠定系列基础。
-《Kingdom Rush: 前线》(2013):地图更复杂,敌人多样。
-《Kingdom Rush: 起源》(2014):优化塔升级,精灵主题, 节奏感比较好。
-《Kingdom Rush: 复仇》(2018):扮演反派,暗黑风格暗黑风格搭配创新塔种, 我最推荐。
-《Kingdom Rush 5: 联盟》(2024):双英雄机制,通关难度适中,兼顾新老玩家。

游戏设定在一个奇幻世界,玩家扮演指挥官,通过建造防御塔、操控英雄和使用魔法,抵御兽人、巨魔、恶魔等多样化敌人的进攻。核心玩法直观易上手:沿固定路线部署防御塔,合理分配资源,阻挡敌人前进。防御塔分为弓箭塔、兵塔、魔法塔和炮塔四类,每种塔可升级并解锁独特技能,如弓箭塔的连发箭、魔法塔的闪电链,策略性极强。
此外,系列引入了英雄系统,每位英雄拥有独特技能,例如弓箭手擅长远程狙击,骑士适合近战肉搏。玩家需操控英雄移动,堵截漏网敌人或支援薄弱防线。魔法技能(如陨石雨、援军召唤)则为战斗增添变数,关键时刻往往能逆转局势。特别值得一提的是,《复仇》的凤凰能喷射烈焰清场,《联盟》的双龙组合则带来史诗级输出,令人印象深刻。

为什么推荐 Kingdom Rush?

Kingdom Rush 系列由* Ironhide Studio* 精心打造,品质始终如一。以下是我推荐它的几大理由:

难度平衡,适合各水平玩家

关卡设计用心,普通模式新手友好,挑战模式(如老兵模式,即高难度关卡)则满足硬核玩家。敌人波次与资源分配恰到好处,既不无脑也不虐心。

高可玩性,玩法丰富多样

每作包含十余主线关卡,外加挑战关和隐藏关,内容充实。防御塔与英雄的多样组合,让每关都有新鲜玩法,重复游玩也不乏味。

碎片时间友好,单机体验纯粹

游戏无需联网,随时暂停,适合忙碌的玩家抽空玩一把。单机设计无内购压力,带来纯粹的游戏乐趣。

性价比高,物超所值

游戏在 Steam 和移动端定价多在几十元,经常打折。DLC 内容丰富,诚意十足,堪称塔防游戏的良心之作。

我的个人体验

高中时,我在 4399 平台接触到初代 Kingdom Rush,被它精致的画面和紧凑的节奏深深吸引。熬夜尝试不同防御塔组合,解锁隐藏关卡的成就感让我乐此不疲。后来,系列续作不断推出,我始终保持热情。2024 年 Kingdom Rush 5: 联盟 发布后,我迫不及待挑战老兵模式。双英雄机制让战斗更灵活,但也略微降低了通关难度。相比之下,我更偏爱《复仇》的暗黑风格和高难度设计,每次通关都充满成就感,尽管偶尔会被棘手的关卡“虐”得抓狂。

写在最后

Kingdom Rush 系列适合各类型玩家,无论是喜欢策略的硬核玩家,还是想休闲娱乐的轻度玩家,都能找到乐趣。作为技术人,我欣赏它在关卡设计和技能平衡上的用心。如果你喜欢塔防,或想找款轻松耐玩的单机游戏,不妨试试 Kingdom Rush。一旦上手,可能就停不下来!


探索扣子空间:开启AI智能体的无限可能(内含邀请码)

作者斌仔
2025年4月28日 16:01

在当今数字化办公和生活的时代,是否有一种工具能像一个万能助手,帮我们轻松解决各种复杂任务,还能根据不同需求输出多样的成果?答案就是扣子空间,它是字节跳动基于自主研发的国产大模型“豆包1.5 Pro”打造的AI智能体平台。支持零代码或低代码快速创建智能体,就如同字节的“通用实习生”和“领域专家”,为我们的工作和生活带来了全新的体验。

扣子空间

扣子空间的特点

零门槛操作

扣子空间真正实现了零基础、无门槛完成任务。它就像一个“Agent的自动调用器”,内部会自动调用各种智能体,无需用户介入,就能完成各类任务。对于用户而言,它不只是能回答问题,更能解决实际任务,成为我们得力的办公助手和工作伙伴。例如在办公场景中,当我们需要快速生成一份报告时,只需向扣子空间提出需求,它就能自动调用相关智能体,整合信息,快速生成一份内容丰富、结构清晰的报告,大大节省了我们的时间和精力。
零门槛操作

双模式协作

扣子空间配置了“探索模式”和“规划模式”两种不同的模式。探索模式下,AI能够自主快速响应,适合时效性强的简单需求。在这种模式下,AI会自动完成各个步骤,速度较快。比如当我们需要查询一些简单的信息,如明天的天气情况,使用探索模式,扣子空间能迅速给出准确的答案。而规划模式下,AI会进行深度思考与执行,它会先拆解任务步骤,用户还可随时介入调整,适合复杂任务,专攻高复杂度项目。例如在进行一个大型的项目规划时,扣子空间会先将任务分解成多个小步骤,展示给用户,用户可以根据实际情况对步骤进行调整和优化,确保项目的顺利进行。
双模式协作

MCP协议支持

平台支持模型上下文协议(MCP),这使得它可以接入飞书、多维表格、高德地图、语音合成等插件,极大地扩展了智能体的能力边界。系统能够在执行任务时智能选择并调用最适合的MCP扩展,首批集成了飞书多维表格、高德地图、墨迹天气、Notion等多个常用服务。未来,还将支持开发者通过“扣子开发平台”发布自定义插件。这意味着,扣子空间的功能将不断丰富和拓展,能够满足更多不同用户的个性化需求。比如在旅行规划中,扣子空间可以调用高德地图插件,为我们规划最佳的出行路线;还可以调用墨迹天气插件,查询目的地的天气情况,让我们的旅行更加顺利。
MCP协议支持

智能体生态丰富

扣子空间内置了通用智能体和专家智能体,其中专家智能体针对特定领域(如用户调研、股票分析)提供专业支持。例如“华泰A股观察助手”可生成每日股市早报与答疑,为投资者提供专业的投资参考;“用户研究专家”能深度分析用户数据,帮助产品经理快速生成用户访谈提纲、调研问卷,甚至模拟虚拟用户数据并生成分析报告。这些丰富的智能体生态,让扣子空间在不同领域都能发挥出强大的作用。
智能体生态丰富

功能丰富且输出多样

扣子空间支持从信息整理到复杂任务执行,能自动规划任务、搜集资料,还能调用多种工具,输出网页、PPT、报告等多种格式的成果。它可以自动整理信息,生成报告、网页、PPT等;自动搜索并扩展关键词,完成资料搜集;在规划模式下模拟电脑操作,执行订票、数据录入等任务。例如,当我们需要进行市场调研时,扣子空间可以自动搜集相关资料,整理成一份详细的市场调研报告,还可以将报告以网页或PPT的形式呈现给我们,方便我们进行展示和分享。
功能丰富且输出多样

用户体验良好

扣子空间的界面简单易懂,左侧是任务列表,右侧是一个对话框,用来输入新任务,符合用户的直觉。在使用过程中,它能够实时跟随用户的操作,不会让人在任务完成过程中有割裂感。同时,浏览器、文件等渲染集成做得也很好,无缝、平滑,几乎不会出现执行卡壳或长时间等待的情况。这使得用户在使用扣子空间时能够感受到流畅、便捷的体验。
用户体验良好

多源信息整合

扣子空间支持从搜索引擎、社交媒体、视频平台、专业网站等渠道获取信息,还支持从网页中提取关键信息,并转化为结构化数据。这一特点使得它能够整合多源信息,为用户提供更全面、准确的信息服务。例如在进行市场调研时,扣子空间可以从多个渠道搜集相关信息,提取其中的关键数据,转化为结构化的数据表格,方便用户进行分析和研究。
多源信息整合

能力拓展性强

通过MCP扩展集成,扣子空间不断拓展AI Agent的能力边界,让其能涉足更多领域,应对更复杂多样的需求。随着技术的不断发展和进步,扣子空间的能力也将不断提升,未来它可能会在更多领域发挥出重要的作用。
能力拓展性强

扣子空间的用途

办公场景

信息整理与报告生成

扣子空间可以自动整理信息,生成报告、网页、PPT等。例如,用户只需输入需求,智能体就能自动搜集相关历史资料,生成带时间线和统计图的网页报告,节省了大量资料整理时间。还能根据用户提供的文档内容,整理成结构清晰、内容丰富的PPT演讲稿,并适当加入动效交互设计。这使得我们在办公过程中能够更加高效地完成信息整理和报告生成的工作。
办公场景 - 信息整理与报告生成

数据处理与分析

扣子空间支持从网页中提取关键信息,并转化为结构化数据,还能对数据进行分析和可视化。如输入2024年国民经济和社会发展统计公报网页,扣子空间可以生成可视化报表。这对于需要进行数据分析和处理的办公场景来说非常实用,能够帮助我们快速准确地获取数据中的关键信息。

任务执行与协作

在规划模式下,扣子空间可以模拟电脑操作,执行订票、数据录入等任务。还能将任务分配给AI,AI会自动分析需求、拆解任务、调用工具并生成结果,大大提升团队协作效率。例如,输入“帮我订明天北京到上海的高铁票”,智能体会帮你查询车次和票价,自动填充订票信息(支付需用户完成)。这使得我们在办公过程中能够更加便捷地完成各种任务,提高工作效率。

用户研究与市场调研

扣子空间内置用户研究专家智能体,帮助产品经理快速生成用户访谈提纲、调研问卷,甚至模拟虚拟用户数据并生成分析报告。还能进行市场调研,分析市场趋势和竞争对手情况。这对于企业的产品研发和市场推广具有重要的意义,能够帮助企业更好地了解用户需求和市场动态。

股票分析与投资参考

华泰A股观察助手每日生成股票早报,分析上市公司,提供专业的投资参考。还能针对具体股票问题进行答疑,帮助投资者做出更精准的投资决策。这对于投资者来说非常有价值,能够帮助他们更好地了解股票市场的动态,做出明智的投资选择。

生活场景

旅行规划

扣子空间能够根据用户输入的预算、时间、目的地及偏好等信息自动规划最佳行程,实时比价机票和酒店价格,创建个性化旅行手册,包含每日行程、费用和实用建议等信息。例如,生成从北京出发的西班牙定制游计划,提供详细的行程安排和简单的HTML旅行手册。这使得我们在旅行前能够更加轻松地规划行程,节省时间和精力。

商品推荐

电商平台可以用扣子空间根据用户的浏览历史和购买记录,精准推荐商品。这能够提高用户的购物体验,帮助用户更快地找到自己需要的商品。

个性化播客

播客创作者可以根据天气、新闻等信息,自动生成播客脚本并合成音频,让内容更贴合听众需求。这为播客创作者提供了一种新的创作方式,能够提高播客的质量和吸引力。

学习场景

教学资料生成

扣子空间支持编写教案,添加图表、动画和图片等视觉元素,能够编译教学材料,制作生动的PPT或视频,从而提高教学资源的质量和吸引力。例如,生成宇宙天文知识的科普小游戏,有动态的太阳系八大行星,点击可以学习对应行星的英文单词和简单特点介绍。这对于教师来说非常有帮助,能够让教学更加生动有趣。

高考志愿填报

扣子空间可以根据学生的成绩、所在地、意向专业等信息,生成高考填报志愿的指南,包括常见的名词解释、政策解读、填报流程和技巧等,并生成可视化、精美排版的网页。这对于考生和家长来说非常实用,能够帮助他们更好地了解高考志愿填报的相关信息,做出合理的选择。

娱乐场景

游戏开发

扣子空间可以开发各种类型的游戏,如俄罗斯方块的HTML小游戏、愤怒的小鸟的游戏等。这为游戏开发者提供了一种新的开发方式,能够降低游戏开发的门槛,让更多的人参与到游戏开发中来。

艺术创作

扣子空间能够生成各种类型的艺术作品,如海报、图片、视频等。例如,生成百家姓氏的头像生成器的网站,用户输入姓氏,点击生成就能马上生成一张好看的头像图。这为艺术创作者提供了一种新的创作工具,能够激发他们的创作灵感。

扣子空间的风格类型

空间风格化相关风格

在图像流工具的空间风格化应用中,扣子目前提供了七种风格模式,分别为现代、法式、新中式、轻奢、日式、美式和北欧。这些风格可应用于建筑设计、室内设计、虚拟空间设计、游戏和电影制作等领域,利用人工智能技术对空间进行视觉或感官上的改造,使其具有特定的风格或特征。例如,在室内设计中,AI可以帮助设计师快速预览不同风格下的室内装饰效果,从而更好地进行设计决策。

网页设计风格

在网页生成任务中,扣子空间可以根据用户需求实现不同的网页设计风格。如在生成旅游方案的网页时,可采用Bento Grid风格的视觉设计,以纯黑色底配合亮橙色颜色作为高亮,强调超大字体或数字突出核心要点,画面中有超大视觉元素强调重点,与小元素的比例形成反差,中英文混用,中文大字体粗体,英文小字作为点缀,运用高亮色自身透明度渐变制造科技感,模仿apple官网的动效,向下滚动鼠标配合动效等。此外,还可以根据不同的主题和需求,设计出具有科技感、卡通风格、简洁优美等不同风格的网页。

其他风格

扣子空间还能根据具体任务生成具有不同风格的成果,如在生成PPT时,可以根据主题和用途设计出可爱、商务等不同风格的PPT;在生成播客系统时,可以设计出网易云音乐风格的播放页面等。

扣子空间的应用案例

旅行规划类

北欧旅行计划

用户要求制定为期15天的北欧旅行计划,使用探索模式,扣子空间总体完成度较高,以markdown格式输出,但未给出酒店、机票预订、签证办理等必要的引申链接。不过对于常见的婚礼、活动策划场景,扣子空间无需提示词技巧即可胜任。

杭州5日游规划

用户提出“查询未来5天的天气,制定一个杭州5日游的出行计划,描述具体的出行路线,并生成每个景点的图片,给出穿搭推荐”的任务,扣子空间在自主规划过程中,调用了墨迹天气接口查询天气信息,调用地图查询规划信息,最后调用图片生成接口生成场景图片。

西班牙定制游计划

用户需要一个从北京出发的5月1 - 7日为期7天的西班牙蜜月出行安排,预算为4000 - 6000美元,喜欢弗拉明戈、品酒、历史建筑等,还需一个较为隐秘的地点推荐。扣子空间花费约50分钟,搜索近百个网页,完成了一个漂亮的旅游计划,但高德地图未显示成功。

青海大环线自驾游规划

用户要求用高德地图规划去青海大环线的自驾游,并完成一个配备丰富景点图片的网页。扣子空间完成度较高,调用高德接口生成了首页的行程图,美观度有加分,已达到可用程度。

研究报告类

中国外卖市场分析报告

用户让扣子智能体生成中国外卖市场分析报告,采用规划模式,智能体在过程中搜索了大量联网信息,形成的过程文档中引用了大量数据,但最终生成的报告不尽人意,相比其他一些平台的深度研究还有差距。

波音747飞机发展史网页报告

用户只需输入需求,扣子空间的通用智能体就能自动搜集相关历史资料,生成带时间线和统计图的网页报告,节省了大量资料整理时间。

中文互联网播客行研报告

用户要求制作一份详尽的、可读性强的中文互联网的播客行研报告,并以PPT形式展示。扣子空间生成的PPT排版和图片乍一看还行,但内容质量欠佳,只能提供一个模板。

网页与应用开发类

调色盘选色生成图片网页

用户提出“设计一个网页,可以从调色盘上选择4种颜色,用户选择完之后,可以自动生成一张以4种颜色为主色调的图片”的需求,扣子空间不仅理解了需求,还给出很多自定义部分和预览图,每次生成的图片都是随机的,用户给出了满分评价。

2048游戏开发

用户要求完成一个2048游戏,游戏里的美术设计都使用合乎逻辑的海贼王角色和海贼王的元素。扣子空间完全按照要求完成,游戏可正常游玩,但存在乌索普头像用成路飞头像的小问题。

心理测试程序设计

用户要求设计一个包含10个题目的心理测试程序,测试结果为海贼王里的几个性格鲜明的特定角色,题目为单选题,网页设计优良,最后的角色有头像,评测结束页面有语音介绍用户的性格特色。扣子空间完成度较高,但调用音频生成插件时不出声音。

办公协作类

HR SaaS领域产品对比分析

用户以HR SaaS领域为例,要求扣子空间对国内几家头部产品进行全方位对比。扣子空间不仅完成了分析报告,还自动将其部署上线,分析框架完整,核心要点把握准确,整个过程仅用时11分钟,后期只需对不准确的小细节进行人工修正。

AI对话类型PRD撰写

用户要求编写一个AI对话类型的产品需求文档并保存至飞书。扣子空间在执行过程中曾“遗忘”保存至飞书的指令,再次提醒后继续完成任务,最终生成的PRD文档结构清晰、内容完整,各个关键模块一应俱全,整个任务耗时仅约3分钟。

数据录入与表格生成

在酒吧装修场景中,用户需要购买5个85 - 100寸的电视,扣子空间用“探索模式”在十分钟内完成了小米电视相关产品的在售信息整理,并生成对照网页。此外,在处理精酿啤酒相关信息时,通过规划模式和MCP(模型上下文协议),将数据存入飞书多维表格。

金融投资类

股票早报定制

华泰A股观察助手可根据用户需求生成股票早报,分析上市公司情况,提供专业的投资参考。例如为投资者对比顺丰、圆通、申通、韵达的股价表现和财务情况,分析哪家更值得投资。不过,由于规划内容较多,整体执行耗时较长,大概20多分钟。

股票前景分析

用户要求扣子空间分析中科曙光股票前景并制作为图表,扣子空间先对任务进行步骤规划,获得用户确认后开始任务。在执行过程中,它会从专业财经媒体和法定披露内容中选择有权威性的信息,最终生成的结果呈现了企业基本面、财务数据图表和专业机构评级等信息,并用可视化方式展示。

用户研究类

用户调研问卷与报告生成

某初创企业产品经理小李,利用扣子空间的“用户研究专家”智能体,快速生成访谈提纲和调研问卷,并模拟100条虚拟用户反馈,省去大量调研前期准备时间,快速获得用户洞察。

访谈记录总结

用户可以让扣子空间总结整理的一批访谈记录文件,以获取更清晰的信息和结论。

结尾总结

扣子空间以其零门槛操作、双模式协作、MCP协议支持、智能体生态丰富、功能丰富且输出多样、用户体验良好、多源信息整合和能力拓展性强等核心特点和优势,在办公、生活、学习、娱乐等多个领域都发挥着重要的作用。它就像一个万能助手,为我们解决各种复杂任务,带来了极大的便利和创新。

展望扣子空间的发展前景,随着技术的不断进步,它可能会有更多的功能和应用场景出现。例如,未来它可能会与更多的行业进行深度融合,为不同行业提供更加专业、个性化的服务;也可能会在人工智能技术的不断升级下,变得更加智能、高效。

对于广大用户来说,我建议大家不妨尝试使用扣子空间,体验它带来的便利和创新。无论是在工作中提高效率,还是在生活中享受便捷,扣子空间都值得一试。相信在使用的过程中,你会发现它的更多魅力和价值。

未使用邀请码

文武科技社
文武科技社
为尊重作者劳动成果,请输入验证码查看隐藏内容
微信扫码关注本站微信公众号(文武科技社/wwkejishe),回复 验证码 获取。

扣子空间邀请码开通和扣子空间邀请码获取步骤

第一步:打开官网注册 :https://www.coze.cn/home

第二步:开通扣子空间 :https://www.coze.cn/space-preview

第三步:输入邀请码激活扣子空间

第四步:创建任务,等执行完成即可获得5个邀请码

25年踏春——大黑山一日游

2025年4月28日 10:58

又是一年赏花季,今年选择去大黑山

早上一大早就坐公交车去,路途还算顺畅

我妈说想从下面爬上去,到山顶去看杜鹃花海

于是我们俩爬呀爬,正好赶上大中午,那个晒啊

带的两瓶水很快就喝光了,就去山顶上买水

买的康师傅五块一瓶,雪碧七块一瓶,还有三块钱的冰棍

结果后面是超大的上坡,超级的累

于是就在半山腰放弃了呜呜呜,真的走不动了

半山腰也能看到杜鹃花海,也不算是遗憾吧

今年天气回暖比较晚,花期也晚一些,但是也拍到了

还想计划去横山寺啥的去看郁金香,呜呜呜这就五一了

{cat_waterfall}










{/cat_waterfall}

微信H5开发:js限制页面只能在微信浏览器打开,禁止外置浏览器访问

2025年4月28日 10:17

很久以前遇到过这个问题,不是微信浏览器,就跳转“请在微信客户端打开链接”,今天看到一个博主的文章,试了一下是个可以的。

记录一下:

只要不是在微信内部打开网页的,就会跳转提示页面,appid必写,可随意写,也可以写已获取的。
<script>
        var ua = navigator.userAgent.toLowerCase();
        var isWeixin = ua.indexOf('micromessenger') != -1;
        if (!isWeixin) {
            window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=888"
        }
</script>

地址:https://wx.113211.xyz/

如果你是在非微信浏览器中打开就会提示
https://wx.113211.xyz/

如果在微信浏览器中打开就会提示你想要展示的内容
2025-04-28T02:16:33.webp

在群晖上使用 Docker 部署 Proxmox Backup Server

作者ysicing
2025年4月27日 20:45

在群晖上使用 Docker 部署 Proxmox Backup Server

Proxmox Backup Server(后续简称 PBS) 是 PVE 容器、虚拟机的备份解决方案,支持增量、重复数据消除备份,可以节省存储空间,同时支持加密和完整性校验。

PBS 官方提供了 iso 格式的镜像,同时社区也有开源的 Docker 镜像的部署方式,为了在群晖等 NAS 上部署方便,本文使用 Docker 的方式进行部署,项目地址:https://github.com/ayufan/pve-backup-server-dockerfiles

镜像:

  • ayufan/proxmox-backup-server:latest
  • ccr.ccs.tencentyun.com/k7scn/proxmox-backup-server:latest (国内镜像)

部署 PBS

使用 Docker 或者 Docker Compose 方式部署都可以。方便起见,统一使用 docker compose 方式部署, 不管群晖还是飞牛都适用。

安装配置 PBS

参加官方的 compose 示例,可以配置如下:

  • docker-compose.yaml
services:
  pve-backup-server:
    image: ayufan/proxmox-backup-server:latest
    # image: ccr.ccs.tencentyun.com/k7scn/proxmox-backup-server:latest
    container_name: pbs
    ports:
      - "8007:8007"
    volumes:
      - /data/pve/pbs/etc:/etc/proxmox-backup
      - /data/pve/pbs/log:/var/log/proxmox-backup
      - /data/pve/pbs//lib:/var/lib/proxmox-backup
      - /data/pve/pbs/data:/backups
    tmpfs:
      - /run
    restart: always

唯一需要关注的是

  • /data/pve/pbs/data:/backups 存储你的备份数据的,所属目录空间需要大一些

配置 PBS

在启动容器后,访问 https://<ip>:8007/ 端口即可进行登录,默认的用户名是 admin,密码是 pbspbs,选择 Proxmox Backup authentication server 进行登录,可把语言切换成中文,证书问题可忽略

配置备份存储

配置存储路径

首次登录是没有配置存储的,在 数据存储中添加数据存储,将刚才映射的 /backups 目录作为存储路径

配置用户权限

默认情况下,新用户和 API 令牌没有任何权限。给用于备份的 admin 用户添加备份路径的访问权限

获取 PBS 指纹信息

指纹用于在 PVE 中添加备份时进行认证

在 PBS 仪表盘里显示指纹备用

配置 PVE

在 PVE 的数据中心-存储中选择添加 Proxmox Backup Server,输入认证信息和指纹;Datastore 为 Proxmox Backup Server 的数据存储的名称,如 local, 相关参数都是上面步骤配置的

验证备份是否可用

在 PVE 的数据中心-备份中添加备份计划,按需添加,添加完成后选择现在运行即可开始备份

在 PBS 处验证看是否有备份数据

End

到这里 PVE 系列教程结束了,新买的杜甫应该算折腾好了,趋向于稳定了。后面会陆续写些之前工作积攒的一些小技巧,吐槽一下 Caddy 新版本 libdns 变更真是一个特别大的破坏性更新。


Typecho1.2.3版本发布,非官方的

2025年4月26日 22:03

鉴于Typecho1.3.0迟迟未发布,故发布1.2.3版本,相比之前发布的非官方1.2.2修复了4处伪静态未适配问题

下载

旧版本升级指南

删除旧版本根目录下varadmin文件夹,上传新版本的varadmin文件夹即可!

后续

后续如果升级typecho1.3.0之类的版本,按照官方升级文档升级即可,不会任何影响

jkOptionsFramework

2025年4月26日 21:24

jkOptionsFramework

jkOptionsFramework 是由即刻学术开发的一款 Typecho 选项框架,移植于WordPress

它能做什么?

熟悉Typecho的伙计都知道,typecho 以简洁为主,然而,开发主题或者插件的过程中,其设置选项对于用户来说并不是很友好。
由于内容很多,只简要介绍部分设置。
例如,一个typecho 的主题设置可能长这样:

2025-04-26T13:21:00.webp

而使用 jkOptionsFramework 后,它可以是这样:
2025-04-26T13:21:13.webp

下载

使用文档:https://www.wolai.com/xvse6BfZ5vMxd9LgjYBjRM

Proxmox VE 添加监控

作者ysicing
2025年4月26日 20:32

Proxmox VE 添加监控

PVE 支持添加 InfluxDB 或者 Graphite 作为指标数据的存储;在添加配置后,PVE 会主动上报相关监控数据,用于记录和监控 PVE 的状态

效果图

使用 InfluxDB 和 Grafana 对 PVE 进行监控,效果如图:

本文适用 8.x 版本,仅在 PVE8.3、PVE8.4 版本测试过。

安装配置 InfluxDB

当前 PVE 版本需要使用的 InfluxDB v2 版本,使用 Flux 语法进行查询。方便操作,本次仅提供 compose 部署方式,k8s 部署也是类似比较简单。

  • docker-compose.yaml
services:
  influxdb:
    image: bitnami/influxdb:2
     # image: ccr.ccs.tencentyun.com/k7scn/influxdb:2
    container_name: influxdb
    environment:
      - INFLUXDB_ADMIN_USER_PASSWORD=Cha3ie7gahthooyeech1xohgaeyax7Gi
      - INFLUXDB_ADMIN_USER_TOKEN=Eing5yaew6ujoo9ohd3saeH6neeshei3
      - INFLUXDB_USER_ORG=proxmox
      - INFLUXDB_USER_BUCKET=proxmox
      - INFLUXDB_USER=proxmox
      - INFLUXDB_USER_PASSWORD=Ao2eeGh7aDoh2eich0zeith6viyae4er
    volumes:
      - /data/influxdb:/bitnami/influxdb
    ports:
      - '8086:8086'
      - '8088:8088'
    restart: always

密码可以使用 pwgen 工具生成, 例如:

# 生成一个32位的随机密码
pwgen 32 1
  • INFLUXDB_ADMIN_USER_TOKEN 后续上报还是 grafana 获取监控数据都使用这个配置

由于我这个 influxdb 只有 PVE 使用,故初始化组织和 Bucket 都为 proxmox

配置 PVE

登录 PVE 后,PVE 的 服务器视图 下,选择数据中心 - 指标服务器,选择添加 InfluxDB,输入相关的配置;
协议选择 HTTP,组织添加 INFLUXDB_USER_ORG 配置的值,插槽添加 INFLUXDB_USER_BUCKET 配置的 Bucket, 令牌填写 INFLUXDB_ADMIN_USER_TOKEN 配置的 Token

添加后 PVE 就会将监控指标推送到 InfluxDB 的 Bucket 中了。

登录 InfluxDB 验证配置是否正确,使用 admin 账号密码登录

配置 Grafana

启动 Grafana

在上面的 docker-compose.yaml 的基础上,添加 grafana

grafana:
    image: bitnami/grafana:11
    # image: ccr.ccs.tencentyun.com/k7scn/grafana:11
    container_name: grafana
    ports:
      - '100.90.80.15:3000:3000'
    environment:
      - 'GF_SECURITY_ADMIN_PASSWORD=joh1AhDah9quah8ruteexaeloh1Ohyuc'
    volumes:
      - /data/grafana:/opt/bitnami/grafana/data
    restart: always

配置启动 grafana

docker compose pull
docker compose up -d

添加 InfluxDB 数据源

访问 http://ip:3000/connections/datasources/new, 使用 InfluDB 作为数据源

  1. Query Language 选择 Flux
  2. URL 填写 InfluxDB 的地址,如 http://100.90.80.15:8086
  3. Auth 下的配置不需要启用,默认启动 Basic auth,去掉勾选
  4. Custom HTTP Headers 添加一个新的配置,Header 名称为 Authorization, Value 为 Token+ 配置的 Token,如 Token Eing5yaew6ujoo9ohd3saeH6neeshei3(需注意 Token 和值中间有一个空格)
  5. OrganizationDefault Bucket 填写和上面配置的值一致就行,如果没变更填 proxmox
  6. 配置完成后,点击 Save and Test,如果提示成功则表示配置正确

添加 Grafana 图表

Grafana Dashboard 中搜索 proxmox,选择支持 Flux 查询语法的图表进行添加,如添加 Proxmox Flux,根据 ID 导入 Grafana 即可看到 PVE 的监控指标

这里推荐 ID 使用 15356

访问 http://100.90.80.15:3000/dashboard/import

导入 Dashboard 后效果如下:


天然气热水器国补下单

2025年4月26日 18:08

Photo by Risa / Unsplash

前些日子家电国补线上线下宣传的如火如荼,但是一直觉得国补这个东西好像我占不到啥便宜,因为我暂时没有任何更换家电、手机的想法。

但是老天爷似乎都要推波助澜让我去凑凑热闹——家里的电热水器忽然坏了。

是的,但是其实也并不意外,这台和这个我们家房子同样年纪的万和热水器服役已经十多年了,其实也就这两年开始慢慢有些小毛病,这个时候即便坏了我也不觉得意外。

其实也叫不上坏,热水器的错误码为E1,是点火器故障,去年还是前年开始就出现过几次,我拆开打磨了一下点火器的头子后其实就好了。不过我妈说干脆让这个老家伙退役算了,修来修去的也麻烦。

线上的热水器品牌五花八门,如海尔、美的、万和、樱花等等。不过价格在一片绿色国补到手价的标签下显的尤其划算。16L带增压、水伺服的一级能耗的机器线上基本上只要1100-1300左右,二级能耗就更低了,基本都在1000以下。

我也去线下的实体店看了一下,因为保护线下市场的缘故,线上线下基本上不会售卖同型号的机器,不过同配置参数线下始终要贵个几百,毕竟像是京东、淘宝这种平台还有平台优惠不是。

而且线下不卖一级能耗的产品,不知道为啥。

刚开始的时候我选的美的、海尔的牌子,后来我妈提醒让我看看万和,这才了解到万和才是国内专业做热水器的,而且我们之前那个台服役十多年的热水器也是万和,后来安装的师傅也认同了这个看法。

我最后选的是万和的F9DMAX ,各种优叠加下来价格如下,性价比相当高,国补真的没毛病。👍

  • 商品总价:¥2199.00
  • 运费:+ ¥0.00
  • 商品优惠:- ¥110.00
  • PLUS专享立减:- ¥8.80
  • 以旧换新:- ¥130.00
  • 国家补贴:- ¥390.04
  • 实付款:¥1560.16

产品主要参数如下。

  • 16L水量
  • 国家一级能效
  • 水伺服
  • 下置风机
  • TSI 210% 增压
  • 8年质保

其中我体验下来,各项参数参考的优先级为,排前的我认为重要性更高:

  1. 质保:热水器强制报废是8年,现在几乎都是8年质保了,不过也有5年的。
  2. 能效:国家一级能效每年能比二级的少个1000块左右,我感觉还是相当重要,就是会产生冷凝水,需要有排水的地方,但是这个水量很小,我没观察过,但是估计和空调差不多把。
  3. 水量:我们家之前那台是13L的,有两个地方同时用水时,水量会变的非常小,十分影响体现。
  4. 水伺服:这个其实就是控制水温的,保证多地同时用水时的水温恒定,之前那台没有这个功能,经常厨房用水后洗澡水温度会忽然变凉,很不爽。
  5. 下置风机:相比于上置,下置风机因为做了密封,噪音真的小了非常多,以前那台上置的一开,我在卧室都能听到动静,现在站在旁边都不容易发觉,还得用眼睛看热水器面板。
  6. 增压:之前想着带这个增压会不会让多个地方用水的时候水量变大,其实不会,这个因为热水管、烧水量只有那么大,同时经过的热水是恒定的,不会有非常明显的变化,复式楼层可能感知会明显很多。

最后,还要注意上门安装的费用,一般情况下安装人员上门安装是不收费用的,但是他们只会免去箱子内所有材料的费用(毕竟那是你已经花钱买了的),如果需要使用额外的材料是需要另外付费的,但是一般品牌放会明码标价。

我这次因为是替换旧机,原有管路什么都是现成的,没有用他们的任何材料,所以师傅上门安装好后就能直接使用,也没做任何推销和歪心思,没出任何费用,体验还是相当不错的。

我们家的情况师傅说如果冷热水管安排到位,其余材料用他们的费用也只会在100多一点,也不算太贵。

另附我这次整理的热水器型号清单。

一番酒场-日本料理

作者
2025年4月26日 18:04

吃喜爱的美食,过可爱的人生,料理本就是一场原地旅行,在这个日益忙碌的时代,在一番酒场我们可以脱下白天的忙碌,享受片刻的宁静,点上些许啤酒,一杯敬白朝的奔波,一杯敬夜幕的漂泊。一番酒场是一家地道的日式烧鸟居酒屋,在这可以尝尽各式日本料理,喝尽各类扎啤清酒,我们也一直秉承着精选食材,优质服务的理念,在拥有十分亲民价格的同时,保证了日料的品质与口感。

💾

💾

RobotPush

2025年4月26日 12:15

RobotPush

Typecho机器人提醒插件,支持登录提醒,支持评论提醒,支持钉钉、飞书、企微机器人。

下载

开源地址:https://github.com/xyzbz/Robotpush

食用方法

  • 将插件文件夹 RobotPush 上传到 Typechousr/plugins/ 目录下。
  • 登录 Typecho 后台,进入“控制台” -> “插件”,找到 RobotPush 插件并启用。
  • Webhook地址,三家平台创建方式相同,简约步骤:创建群聊-添加机器人-添加自定义机器人,安全属性选择IP地址并配置自己的服务器IP即可。

效果图

Typecho机器人提醒插件.webp

DingTalkLoginNotify

2025年4月26日 12:06

DingTalkLoginNotify

登录Typecho后台,会触发一条登录推送,通过钉钉机器人webhook,发送至群提醒,一定程度上可以避免密码泄露被登录。目前可以显示登录的用户名,登录时间,登录IP。

下载

使用

安装插件

  • 将插件文件夹 DingTalkLoginNotify 上传到 Typechousr/plugins/ 目录下。
    启用插件
  • 登录 Typecho 后台,进入“控制台” -> “插件”,找到 DingTalkLoginNotify 插件并启用
    获取钉钉机器人链接
  • 登录钉钉,我这里是个人创建的团体,建一个群,添加自定义机器人。安全设置选择IP,配置自己的服务器IP即可,获取到地址!
    6789ed35646c0.webp

配置插件

  • 在插件设置页面填写钉钉机器人的 Webhook 地址和消息模板。
    6789ed36117a5.webp

插件作者

https://xyzbz.cn/archives/1312/

野草云:香港、美国云服务器/VPS/独立服务器优惠活动整理(2025年4月)

作者斌仔
2025年4月25日 16:19

野草云(原野草主机)成立于2012年6月份,致力于为中小企业与个人提供免备案的香港云主机、vps专享服务器、香港独立服务器、美国独立服务器、云虚拟主机、国外域名注册等产品服务。

野草云是国内的一家主机提供商,商家主要提供香港机房服务器、云服务器以及VPS产品,线路上BGP优化、BGP+CN2、阿里云专线。

本文主要整理野草云香港云服务器、香港VPS、香港独立服务器的最新优惠活动,供小伙伴们参考。

野草云主机VPS优惠活动
野草云主机VPS优惠活动

官网地址

https://www.yecaoyun.com

支付方式

当前支持 PayPal、信用卡、支付宝等多种付款方式。

香港AMD VPS

新老用户同享,可选优质BGP/精品BGP,首年价格,优惠码:12THBIRTHDAY

CPU 内存 硬盘 流量/带宽 价格 购买
1核 1G 15G 600G/100M 81元/年 链接
1核 2G 15G 600G/100M 96元/年 链接
1核 4G 30G 800G/100M 133元/年 链接

第一、香港AMD VPS(年付)

CPU 内存 硬盘 带宽/流量 年付 购买
1核 2G 30G 100M 500G 128元 链接
2核 4G 50G 100M 600G 228元 链接
4核 8G 90G 100M 800G 358元 链接

第二、香港不限流量VPS(年付)

优惠码:2024SUMMER

CPU 内存 SSD 带宽 年付 购买
2核 2G 15G 5M 110元 链接
2核 4G 30G 6M 150元 链接
4核 8G 70G 10M 300元 链接

第三、香港SSD VPS(月付)

带宽及流量可选100M BGP限流、BGP不限,或者是BGP+CTG不限三种。

4.7折优惠码:202401HKSSDVPS

CPU/内存 硬盘 宽带 流量 价格 购买
1核2G 15GB 5M BGP 不限 22元/月 选择
2核4G 30GB 6M BGP 不限 29元/月 选择
2核8G 40GB 8M BGP 不限 49元/月 选择

第四、香港高防VPS

CPU/内存 硬盘 宽带 防御 价格 购买
2核2G 15GB 5M BGP 50G 183元/月 选择
2核4G 30GB 6M BGP 50G 195元/月 选择
2核8G 40GB 8M BGP 50G 225元/月 选择

第五、美国SSD VPS

CPU/内存 硬盘 宽带 流量 价格 购买
1核1G 20GB 30M 500G 38元/月 选择
1核2G 20GB 30M 500G 60元/月 选择
2核4G 20GB 30M 500G 118元/月 选择

第六、香港服务器租用

立减100优惠码:2024DLFWQOFFER

线路可选30M BGP、15M CN2+BGP、 15M华为混合网三种。

CPU 内存 SSD 宽带 IP 价格 购买
E3-1230v2 16G 240G 30M BGP 3个 199元/月 选择
E5-2650 16G 240G 30M BGP 3个 299元/月 选择
E5-2630*2 32G 500G 30M BGP 3个 399元/月 选择

第七、测试IP

香港BGP测试IP:103.143.80.254

香港CTG(CN2)+BGP测试IP:45.11.47.254

美国CN2 GIA测试IP:103.59.144.254

清明节 · 香港一日游

作者老孙
2025年4月5日 07:56
AI摘要:清明节香港一日游记录,包含老板请客、街头风景、摩天轮、菲佣聚会、摆渡轮、星光大道和太空馆等景点照片。

作为一个在广东生活了十几年的人来说,没去过香港算不算得上是一件很稀罕的事儿呢?

老板请客

不得不说,香港的物价真的离谱.

香港街头

摩天轮
游客贼多,摩天轮都要排队两小时

菲佣节日聚会
天桥下,地下通道出入口很多菲律宾人

摆渡轮
坐摆渡轮也排队一个小时

星光大道

星光大道

星光大道李小龙

太空馆

LskyUploader

2025年4月24日 11:00

LskyUploader

LskyUploader 是一个 Typecho 插件,用于将图片和其他文件上传至兰空图床(Lsky Pro)。它基于 isYangsLskyProUpload 插件开发,经过优化和改进,提供稳定的文件上传功能。

插件下载

开源地址:https://github.com/xiangmingya/LskyUploade

填写配置项

Api

  • 输入您的兰空图床域名
  • 格式:https://your-lsky-domain.com
  • 注意:不要在末尾添加斜杠/

Token

  • 输入兰空图床的 API Token
  • 获取方式:在兰空后台“个人中心 - API令牌”中生成
  • 格式示例:1|UYsgSjmtTkPjS8qPaLl98dJwdVtU492vQbDFI6pg

Strategy_id(可选)

  • 存储策略 ID,若留空则使用默认策略
  • 获取方式:在兰空后台“存储策略”中查看

Album_id(可选)

  • 相册 ID,若留空则不指定相册
  • 获取方式:在兰空后台“相册管理”中查看

最后保存设置

  • 点击“保存设置”按钮完成配置

使用方法

上传文件

  • Typecho 后台撰写文章时,点击编辑器中的“添加媒体”
  • 选择图片或其他文件上传
  • 插件会自动将文件上传至兰空图床

caifeng极简主义

2025年4月24日 10:45
caifeng极简主义typecho单栏主题

caifeng

「采风」是一款专为创作者设计的极简风格 Typecho 主题,强调内容呈现与阅读体验,适合技术博客、个人随笔与生活记录。

演示

{button href="https://demo.typecho.work/?theme=caifeng" type="blue"}在线预览{/button}

主题下载

开源地址:https://github.com/lovefc/typecho_caifeng

✨ 主题特性

  • 无干扰的极简布局
  • 自适应宽屏/窄屏显示
  • 响应式布局(移动端优化)
  • 轻量级代码架构
  • 渐进式增强设计

🛠️ 快速安装

  • 下载最新 release
  • 解压至 /usr/themes/ 目录
  • 后台启用主题
  • 建议搭配 Markdown 编辑器使用

四月杂记:五一去露营吧

作者刘郎
2025年4月24日 10:16

生活素常,波澜不兴。吾尝以为,家宅安宁,小女无疾,此中真意,尽在平淡之间。适值公余偷闲,聊发动态以记近况。

溯至上月中浣,小女染疾,往复就医,至本月朔前,皆困于病院。吾亦请告旬日,悉心照料。幸得天佑,小女疾渐愈,今者稚女入学,吾辈返岗,举家复归常轨,心下始安。

数日前,天朗气清,与二三知友议及五一之期。吾辈各有室家,兼营事业,向者聚少离多,难复昔年之乐。初议聚餐为会,俄而吾念及露营之约。盖昔年曾与诸君期,待得闲时,当共赴郊野,露营啸歌,把酒言欢。今虽俗务缠身,然此念未歇,遂重提旧事,竟得诸君同声相应。于是论及野炊之食、游冶之乐,遥想届时小女与童稚嬉于芳草,吾辈笑谈风月,对酒当歌,心下欣然向往。虽节期尚隔半月,口约未定,然神往久之。

至于公事,去岁以来,公司业盛,吾辈案牍渐繁。近闻部门新纳一士,虽与吾不同组,于个人之役未必轻减,然念及日后协作或更便捷,亦觉稍慰。但守本职,夫复何求?

观天象,迩来旬日,气温适均,愿天公作美,无雨无霾。今距五一尚余七日,心下已盼诸君之会,庶几得享浮生半日之闲云尔。

平淡中的小确幸

作者Innei
2025年4月24日 00:40
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/notes/189

又是一个月过去了。

今年开始基本每个月只会写一篇手记了。生活中发生的事很少,表达欲也变淡了许多。

相聚片刻

自上个月以来,这个月没有去太多地方。借群友的路过,在我家住了一晚,也为了过了一次生日,一起吃了饭,那天我也没有太孤独,很是感动。第二天跟着一起去了上海,在上海和朋友们吃了一顿日料自助,解锁了新的吃法,因为不吃生海鲜,把甜虾放到寿喜锅煮熟了吃也是一种美味。一下子炫了有二十只。还有其他的串、板烧之类的,自己还是挺能吃的。但是减肥一直是一个大问题了。(本来先找找有没有照片,发现一张都没拍。)

而之后的日子,再没有出过城了。最远的地方只是去县城的郊区和曾经的高中同学看了一次花。起因是 '浙江“阿勒泰” - 小红书',但是照骗,去了也没找到在哪。

终于把很多人吹爆的「葬送的芙莉莲」第一季追完了,最近联名活动也比较多,只是可惜我居住的地方没有这类活动。人类生命短暂,错过机会会带来遗憾,活在当下,珍惜与亲人共度的时光,这些时刻将成为珍贵的回忆。

https://www.themoviedb.org/tv/209867

上个月说会免费试用 FSD 一个月,但是后面被叫停了。心心念念想体验一次,问问很多次都不能在店里试乘。终于最近有机会能去体验了一把。但是从我的试乘感受上来说,并没有网上说的那么神。首先是,一开始就翻车了,直接闯了红灯,在中间紧急接管刹车。然后又是走错车道,左转走了直行道。最后,在一条施工道路也走错了,跨越实线进入了非机动车道。整体乘坐体验还是挺舒适的,但是科目一确实不及格。

摄影初探

不知道什么时候开始突然有了一个想法,想买个相机了。虽然我一点都不会拍照。之前在上海的朋友用了下富士之后,这个念头更加强烈了。但是在 2025 年的今天想买一个 2020 年生产的机器居然还要溢价购买,或者线下店抽签或者线上点踩点蹲抢真是离了个大谱。又是过了段时间,找了淘宝上的授权店,加了一个 35定镜头,原价购买了 1650 套机。然后学学摄像。没事开车出去可以拍拍景。发几张个人觉得还行的。

:::gallery

:::

项目重启

工作上偶尔需要写写 Swift。空闲之余,我也在再一次入门 Apple 设备的 native 开发。而这次我选择重写两年前写的一个 macOS app - Process Reporter。这个 app 是用来上报当前我正在使用的 app 和 media 到 Mix Space 的。在我的网站上,呈现为:

两年前,我使用不成熟的 SwiftUI 糊了一个,全是 bug,功能也不太全面。而这次我选择用 AppKit 为主,SwiftUI 为辅去重写一个。经历了两周左右的马拉松和慢慢的打磨,现在功能基本差不多了。

这次加强了一下对 media process 的识别。现在可以准确的知道是哪个播放器在放歌了。(PS. macOS 在 15.4 及后续版本对私有接口加强了管制,导致后续版本无法正常识别任何 media 信息了)。另外加上了自动上传 app 的图标到图床的功能,没有预设的 app 也能正常显示图标。

最后,还有一个更好玩的。如果你使用 Slack 办公。那么就能实现这个效果。

非常简单的设置即可实现:

开源在:

https://github.com/Innei/ProcessReporter

看完了?说点什么呢

在 Docker 沙箱中运行 MCP Server

2025年4月23日 22:01

MCP 是今年 AI 开发行业很热门的一个协议,但是由于它的 C/S 架构,导致使用者必须在本地运行 MCP Server。

MCP Server 常见的运行方式有 npx(NPM 生态)、uvx(Python 生态)、Docker 等 stdio 方式和 HTTP(SSE/Streaming) 方式。但是 npx 和 uvx 运行命令有着极大的风险。如果不慎执行恶意软件包,可能会导致隐私数据泄露带来极大的安全风险。具体可以看 Invariant 的 MCP Security Notification: Tool Poisoning Attacks 这篇文章。

作为一个软件行业从业者,对安全的关注度极高。让 ChatGPT 整理了一下最近 5 年的 NPM 和 PyPI 供应链攻击事件,让人不寒而栗。

时间事件概要与影响范围
2021年2月“依赖混淆”漏洞披露安全研究员 Alex Birsan 利用依赖混淆(Dependency Confusion)技术,在 NPM/PyPI 上传与多家企业内部库同名的软件包,成功入侵了包括苹果、微软等35家大厂内部服务器 (PyPI flooded with 1,275 dependency confusion packages)。这一演示引发业内对供应链风险的高度关注。
2021年10月UAParser.js 库遭劫持NPM上每周下载量超700万的流行库 ua-parser-js 被攻击者通过维护者账户入侵发布恶意版本 (A Timeline of SSC Attacks, Curated by Sonatype)。受感染版本在安装时植入密码窃取木马加密货币挖矿程序,波及大量开发者系统。
2021年10月假冒 Roblox 库投毒攻击者在 NPM 上传多个假冒 Roblox API 的软件包(如 noblox.js-proxy),内含混淆的恶意代码,安装后会植入木马和勒索软件等Payload (A Timeline of SSC Attacks, Curated by Sonatype)。这些包下载数千次,显示出攻击者通过typosquatting手法诱骗游戏开发者。
2021年11月COA 与 RC 库连续劫持NPM上热门库 coa(每周下载数百万)和 rc(每周1400万下载)相继被入侵发布恶意版本。受害版本执行与 UAParser.js 案例类似的凭证窃取木马,一度导致全球众多使用 React 等框架的项目构建管线中断 (A Timeline of SSC Attacks, Curated by Sonatype) (A Timeline of SSC Attacks, Curated by Sonatype)。官方调查认定原因均为维护者账户被盗用。
2022年1月Colors/Faker 开源库“自杀”著名的颜色格式库 colors.js 和测试数据生成库 faker.js 的作者出于抗议,在最新版本中注入无限循环等破坏性代码,导致包括Meta(Facebook)和亚马逊等公司在内的数千项目崩溃 (A Timeline of SSC Attacks, Curated by Sonatype)(虽非外部攻击,但属于供应链投毒范畴)。
2022年1月PyPI 1,275个恶意包集中投放一名用户在1月23日一天内疯狂向 PyPI 发布了 1,275 个恶意软件包 (A Timeline of SSC Attacks, Curated by Sonatype)。这些包大多冒用知名项目或公司的名字(如 xcryptographySagepay 等),安装后收集主机名、IP等指纹信息并通过 DNS/HTTP 回传给攻击者 (PyPI flooded with 1,275 dependency confusion packages) (PyPI flooded with 1,275 dependency confusion packages)。PyPI 管理员在收到报告后一小时内即下架了所有相关包 (PyPI flooded with 1,275 dependency confusion packages)。
2022年3月Node-ipc “抗议软件”事件前端构建常用库 node-ipc 的作者在 v10.1.1–10.1.3 版本中加入恶意代码:检测到客户端 IP 属于俄罗斯或白俄罗斯时,就擦除文件系统、用爱心表情覆盖文件 (Corrupted open-source software enters the Russian battlefield | ZDNET) (Corrupted open-source software enters the Russian battlefield | ZDNET)。该库被 Vue CLI 等广泛依赖,导致大量用户系统遭破坏,并被赋予 CVE-2022-23812(CVSS 9.8) (Corrupted open-source software enters the Russian battlefield | ZDNET)。
2022年10月LofyGang 大规模投毒活动安全公司发现一个名为“LofyGang”的团伙在 NPM 上分发了将近 200 个恶意包 (LofyGang Distributed ~200 Malicious NPM Packages to Steal Credit Card Data)。这些包通过typosquatting和伪装常用库名称植入木马,窃取开发者的信用卡信息、Discord 账户以及游戏服务登录凭据,累计安装次数达数千次 (LofyGang Distributed ~200 Malicious NPM Packages to Steal Credit Card Data)。这是一起持续一年多的有组织网络犯罪活动。
2022年12月PyTorch-nightly 依赖链攻击知名深度学习框架 PyTorch 披露其夜间版在 12月25–30日间遭遇依赖混淆式供应链攻击 (Malicious PyTorch dependency ‘torchtriton’ on PyPI | Wiz Blog):攻击者在 PyPI 上注册了名为 torchtriton 的恶意包,与 PyTorch 夜ly 版所需的私有依赖同名,导致数千名通过 pip 安装 nightly 版的用户中招 (Malicious PyTorch dependency ‘torchtriton’ on PyPI | Wiz Blog)。恶意 torchtriton 包运行后收集系统上的环境变量和秘钥并上传至攻击者服务器,危及用户的云凭证安全。PyTorch 官方紧急发布警告并替换了该命名空间 (Malicious PyTorch dependency ‘torchtriton’ on PyPI | Wiz Blog)。
2023年3月“W4SP Stealer” 木马泛滥 PyPI安全研究员陆续发现 PyPI 上出现大量携带 W4SP Stealer 信息窃取木马的恶意包 (W4SP Stealer Discovered in Multiple PyPI Packages Under Various Names)。这些木马别名众多(如 ANGEL Stealer、PURE Stealer 等),但本质均为 W4SP 家族,专门窃取用户密码、加密货币钱包和 Discord 令牌等信息 (W4SP Stealer Discovered in Multiple PyPI Packages Under Various Names)。一次报告就揭示了16个此类恶意包(如 modulesecurityeasycordey 等) (W4SP Stealer Discovered in Multiple PyPI Packages Under Various Names)。PyPI 针对此类木马展开清理,并加强了上传检测。
2023年8月Lazarus 组织攻击 PyPIReversingLabs 报告称朝鲜黑客组织 Lazarus 的分支在 PyPI 发布了逾两打(24个以上)伪装热门库的恶意包(代号“VMConnect”行动) (Software Supply Chain Attacks: A (partial) History)。这些包企图针对特定行业(如金融)用户,植入远程访问木马。据称该攻击与此前针对 NuGet 的类似活动相关联,显示出国家级黑客对开源供应链的兴趣。
2024年及以后持续的供应链威胁2024年以来,NPM 与 PyPI 上仍不断爆出新的投毒事件。例如2024年初发现假冒VS Code相关NPM包内含远控间谍软件 (A Timeline of SSC Attacks, Curated by Sonatype)、假冒Solana库窃取加密钱包密钥的PyPI包 (A Timeline of SSC Attacks, Curated by Sonatype)等。这表明供应链攻击已成常态化威胁,需要生态系统持续提高警惕和防御能力。

发 Twitter 吐槽了一下,结果吐槽的时候就看到一个推友遇到了一起供应链攻击事件。

Twitter

所幸 @TBXark 推荐了他的 MCP Proxy 项目,可以很方便的将 MCP Server 运行在 Docker 中。他最初的目的是把 MCP Server 运行在服务器上,减少客户端压力和方便移动端调用。 然而由于 Docker 天然的隔离特性,与我期望有沙箱的诉求不谋而合。

MCP Proxy 会在 Docker 中运行 MCP Servers 并转换为 MCP SSE 的协议,这样用户就可以在 MCP 客户端中全部走 SSE 协议调用,这样可以大大减小 npx 和 uvx 直接运行带来的任意文件读取风险。如果部署在境外服务器, 还可以顺带解决网络的问题

但是当前还是可以读取到 /config/config.json 这个 MCP Proxy 的配置文件, 风险可控。同时也给开发者提了需求, config 文件配置 400 权限, npx 和 uvx 命令使用 nobody 用户运行。如果可以实现,将完美解决任意文件读取的问题。

运行 MCP Proxy

MCP Proxy

如果你自己有 VPS 部署了 Docker, 可以使用下面的命令运行 MCP Proxy。

docker run -d -p 9090:9090 -v /path/to/config.json:/config/config.json ghcr.io/tbxark/mcp-proxy:latest

如果你没有自己的 VPS, 可以使用 claw.cloud 提供的免费容器服务(每个月 $5 额度, GitHub 注册需满 180 天)。

由于 Claw 有容器大小的限制,我们需要使用下面的环境变量,配置 npx 和 uvx 的缓存目录,防止容器崩溃。

UV_CACHE_DIR=/cache/uv
npm_config_cache=/cache/npm

同时在 /cache 路径下挂载 10G 的存储。 配置参考我的配置: 0.5c CPU, 512M 内存, 10G 硬盘。

最终的配置如下:

Claw

配置 MCP Proxy

MCP Proxy 的配置文件需要挂载在 /config/config.json 路径下,完整配置请参考 https://github.com/TBXark/mcp-proxy?tab=readme-ov-file#configurationonfiguration

以下是我的配置,可以参考。

{
    "mcpProxy": {
        "baseURL": "https://mcp.miantiao.me",
        "addr": ":9090",
        "name": "MCP Proxy",
        "version": "1.0.0",
        "options": {
          "panicIfInvalid": false,
          "logEnabled": true,
          "authTokens": [
            "miantiao.me"
          ]
        }
    },
    "mcpServers": {
        "github": {
            "command": "npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-github"
            ],
            "env": {
                "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
            }
        },
        "fetch": {
            "command": "uvx",
            "args": [
                "mcp-server-fetch"
            ]
        },
        "amap": {
            "url": "https://mcp.amap.com/sse?key=<YOUR_TOKEN>"
        }
    }
}

调用 MCP proxy

ChatWise 调用 fetch 为例,直接配置 SSE 协议即可。

fetch

是不是很简单,等 ChatWise 出了移动端这样调用也是完全可用的。

ChatWise

stat

PVE环境折腾之配置DHCP

作者ysicing
2025年4月23日 20:37

感觉最近售卖杜甫的 IDC 有点多,最近凑热闹买了几台杜甫(物理服务器简称),本文不会过多介绍安装 PVE,仅记录配置 dhcp

安装 PVE 环境

推荐先安装 Debian12 环境,在这个的基础上部署 PVE 环境。

  • 物语云暂时只支持系统, 配置 96 核 128G1T 存储 399 元/月,境内服务器
  • 狗云杜甫支持 PVE 环境,配置 48 核 64G1.92T 存储 450 元/月,香港 KC

Debian12 安装 PVE

推荐使用 https://github.com/oneclickvirt/pve 这个项目,基本很稳, 一键虚拟化项目。

细节这里就不多说了,更多可以参考文档

配置 DHCP

默认情况下,是没有配置 DHCP 的,不然每次创建虚拟机后还要手动配置多麻烦,也没 cloud-init

添加 NAT 网桥

如果你是按照上面的文档安装,整个流程走完 NAT 网桥默认已经配置完成了的。

示例配置文件应该如下, 具体网卡具体分析哈

# /etc/network/interfaces 仅摘选相关的部分
auto vmbr1
iface vmbr1 inet static
    address 192.168.23.1
    netmask 255.255.255.0
    bridge_ports none
    bridge_stp off
    bridge_fd 0
    post-up echo 1 > /proc/sys/net/ipv4/ip_forward
    post-up echo 1 > /proc/sys/net/ipv4/conf/vmbr1/proxy_arp
    post-up iptables -t nat -A POSTROUTING -s '192.168.23.0/24' -o vmbr0 -j MASQUERADE
    post-down iptables -t nat -D POSTROUTING -s '192.168.23.0/24' -o vmbr0 -j MASQUERADE

虚拟机网段默认使用 192.168.23.0/24, 网卡是 vmbr1

安装 DHCP 服务

安装完可能会提示报错,这是正常情况需要配置才能启动成功。

apt-get install isc-dhcp-server

配置 DHCP 服务

这里仅涉及 ipv4 部分,所以改动仅涉及 ipv4

编辑 /etc/default/isc-dhcp-server

INTERFACESv4="vmbr1" # 仅修改这里,默认为空,修改为你的NAT网卡vmbr1
INTERFACESv6=""

编辑 /etc/dhcp/dhcpd.conf

:> /etc/dhcp/dhcpd.conf

清空配置文件,重新生成

option domain-name-servers 8.8.8.8;

default-lease-time 600;
max-lease-time 7200;

ddns-update-style none;

authoritative;

log-facility local7;

subnet 192.168.23.0 netmask 255.255.255.0 {
  range 192.168.23.10 192.168.23.50;
  option subnet-mask 255.255.255.0;
  option domain-name-servers 8.8.8.8;
  option routers 192.168.23.1;
  option netbios-name-servers 192.168.23.1;
  option netbios-node-type 8;
  get-lease-hostnames true;
  use-host-decl-names true;
  default-lease-time 600;
  max-lease-time 7200;
  interface vmbr1;
}

默认分配 IP 从 192.168.23.10~192.168.23.50, 切记不要包含网关哈

service isc-dhcp-server restart

重启服务没有报错,即表示配置成功了。如果有错误配置,重启服务会失败。

PVE 导入备份

qmrestore vzdump-qemu-104-2025_04_15-14_56_54.vma.zst 100 -storage local2

其中 local2 为你的存储池

AD 部分

狗云续费、新购充值可以走我的 AFF,如果成功绑定了我的 AFF,返利 50%


欢迎关注、订阅我的微信公众号

快速部署私有微信编辑器

作者ysicing
2025年4月22日 22:05

快速部署私有微信编辑器

之前都是在使用开源作者提供的官方演示站点, 但是最近不知道是不是网络问题,我打开站点比较慢,即使上了手段

WeChat Markdown Editor

一款高度简洁的微信 Markdown 编辑器:支持 Markdown 语法、自定义主题样式、内容管理、多图床、AI 助手等特性

项目地址: doocs/md

功能特性

  • • 支持 Markdown 所有基础语法、数学公式
  • • 提供对 Mermaid 图表的渲染和 GFM 警告块的支持
  • • 丰富的代码块高亮主题,提升代码可读性
  • • 允许自定义主题色和 CSS 样式,灵活定制展示效果
  • • 提供多图上传功能,并可自定义配置图床
  • • 便捷的文件导入、导出功能,提升工作效率
  • • 内置本地内容管理功能,支持草稿自动保存
  • • 集成主流 AI 模型(如 DeekSeek、OpenAI、通义千问),辅助内容创作

那为啥选择它,主要是我的博客系统 Solitudes 也支持它的语法,我只要写一次就可以复用。

PS: 打个广告 Solitudes 是使用 Go 编写的轻量博客引擎, 我的博客是魔改版,一直说要把改动 PR 到上游一直鸽到现在

部署

部署比较简单,官方也提供了镜像

docker 部署

执行完如下命令后,访问你的 80 端口就可以了

docker run -d -p 80:80 doocs/md:latest

k3s+caddy

下面仅供参考

---
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    app: wxmd
  name: wxmd
  namespace: nb-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wxmd
  updateStrategy:
    type: InPlaceIfPossible
  template:
    metadata:
      labels:
        app: wxmd
    spec:
      tolerations:
      - operator: Exists
      nodeSelector:
        node-role.kubernetes.io/china: "true"
      containers:
      - image: doocs/md
        imagePullPolicy: Always
        name: wxmd
        ports:
        - containerPort: 80
          protocol: TCP
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: wxmd
  namespace: nb-system
spec:
  selector:
    app: wxmd
  ports:
  - port: 80
    targetPort: 80

将服务部署到 china 的节点池上,并且创建 svc, 配置 caddy,直接使用 svc 的 ip 更方便

wxmd.nbds.ysicing.net {
reverse_proxy http://10.25.220.235
}

公益服务

wxmd.nbds.ysicing.net, 本文就是使用该服务完成的。本服务应该会长期提供服务,下线会提前通知的。


Go开发实践之Gin框架将前端的dist目录embed到二进制

2025年4月23日 05:51
如要阅读全文,点击标题跳转。通过Go的 embed 包与Gin框架结合,开发者可实现前后端资源的无缝整合,显著提升部署效率和安全性。针对开源项目,非常建议使用这种嵌入方案,对于项目的部署体验步骤,极大简化,能够让人快速拉起体验,抓住初相识的前五分钟,对一个项目的影响力扩展是非常重要的。

Cherry Studio:搭建个人知识库的完美助手!

作者ysicing
2025年4月21日 21:15

Cherry Studio:搭建个人知识库的完美助手!

之前我曾写过一篇关于如何基于 LobeHub 部署个人知识库的文章。有朋友反馈上手难度较高,因此今天我想再推荐一款非常适合我们国人的 AI 助手——Cherry Studio

这款工具非常易于使用,现已基本完成对 LobeHub 的替代。为什么我现在选择使用它呢?我觉得主要有以下几个优点:

  • 客户端支持:兼容 macOS 和 Windows 系统,下载安装后便可直接使用,无需复杂的服务部署。
  • 数据私有化:非 SaaS 服务,数据管理更加安全。
  • 内置丰富的提示词模板(智能体),大大提高使用效率。
  • 便捷的 数据备份,让信息管理更顺畅

相较于 LobeHub,Cherry Studio 唯一不足之处在于多客户端之间的记录同步和合并。但我相信,有了 AI 的加持,这些问题在后续都将迎刃而解(这里先给大家埋个伏笔!)

总之,Cherry Studio 上手简单且功能强大。如果你在寻找一款优秀的 AI 终端应用,我毫不犹豫地推荐它,真的是 YYDS!

安装 Cherry Studio

打开 Cherry Studio 官方下载页面 https://cherry-ai.com/download,根据你的操作系统进行下载。如果是 macOS 系统,M 系列用户请下载 Apple 芯片版本。
当然,如果你有相关技术,还可以通过官方 GitHub 开源项目下载 CherryHQ/cherry-studio

下载完成后,点击进行安装,过程非常简单。安装完成后,启动 Cherry Studio,开始配置。

配置模型服务

Cherry Studio 支持主流大模型服务,配置过程也大同小异,界面上还有相关引导。

配置国产大模型

以深度求索的 DeepSeek 模型为例进行演示

在第五步这里,是验证你的大模型和 Token 配置正常,如果请求成功,则可以设置启用

配置 ollama

整体流程与上述类似,唯一的区别是 Token 地址为空;下图示范的模型是我本地经常进行测试的模型。

需要注意的一点,添加完模型后需要管理,将模型添加上,上面显示的是已经附加了的模型

配置中转大模型

在许多场景下,我们经常使用中转服务商提供的 AI 网关,以解决特殊因素导致的非必要问题。

大部分 AI 网关支持通用的 OpenAI 格式 AI 请求,因此我在这里选择 OpenAI 进行配置。

整体流程与其他模型相似,同样需要在管理页添加所需使用的模型。

设置默认模型

Cherry Studio 允许为不同对话设置不同模型,我们可以在设置中定义默认助手模型。我这里配置的是 GPT-4o-mini。主要这个模型便宜的很,日常使用没有特别大的问题。

在聊天过程中,当然也可以随时切换不同的模型。

网络搜索

强烈推荐使用搜索引擎,可以免费获取实时信息资讯,配置也相对简单。

这里简单给大家实操一下, 查询北京明天天气情况

智能体

虽说是智能体,但更多像单一 Agent 哈,只是内置了相关提词。

生成网页也是轻轻松松, 预览效果如下

图片

默认只能用硅基流动,通常不用它。可以使用 gpt-4o-image 模型生图

个人知识库

Cherry Studio 的个人知识库功能非常实用,下面是配置流程的简要说明:

点击知识库,添加一个知识库

随便添加一些文本

编辑默认助手,勾选知识库

在使用过程中,系统有时能够命中知识库的信息,效果相当不错。

数据备份

Cherry Studio 支持多种数据备份方式,我推荐使用 WebDAV。如果你有飞牛,可以直接开启 WebDAV 服务;如果没有,也可以用 Docker 部署一个 WebDAV 服务。

docker run -d -e USERNAME=test -e PASSWORD=test -v /data/webdav:/var/webdav -p 8888:80 morrisjobke/webdav

顺便提一句,我利用 AI 解决了 WebDAV 的两个问题:

  • 同类型设备备份没发区分备份文件所属机器问题 #5004
  • 备份文件保留设置 #5060,只支持清理当前设备备份数据,历史数据需要手动自行清理了

其他

关于 MCP 部分的内容,下次再分享哈。

Mastodon新通知推送NoneBot

作者老孙
2025年4月21日 16:25
AI摘要:作者受启发开发了一个Python脚本,通过Docker镜像`jkjoy/mastodon2qqbot`实现Mastodon新通知推送至QQ。配置简单,支持自定义检查间隔和接收QQ号,使用NoneBot框架实现消息转发功能。

起因

看了@1900 长毛象新通知推送TGBot

深受启发,于是通过ChatGPT 4.1 写了一个python脚本

定时检测Mastodon消息,有新通知会通过QQbot的API URL 发送消息通知给指定的QQ用户(Nonebot兼容)

使用

构建了一个docker镜像jkjoy/mastodon2qqbot

代码仓库在 https://github.com/jkjoy/dockerfile/blob/main/mastodon2qqbot/main.py

使用docker run 命令启动

docker run -d \
  -e MASTODON_INSTANCE="https://你的mastodon实例" \
  -e MASTODON_TOKEN="你的token" \
  -e QQ_API="https://bot.0tz.top/send_private_msg" \
  -e QQ_ID="你的QQ号码" \
  -e CHECK_INTERVAL="30" \
  jkjoy/mastodon2qqbot

30秒检查一次 Mastodon 消息

通过默认的QQ机器人2280858259 发送消息给我使用的QQ80116747

默认的QQ API 是https://bot.0tz.top/send_private_msg

使用默认QQ API需要添加QQ机器人2280858259为好友

长毛象新通知推送TGBot

2025年4月21日 12:35

其实蜗牛哥之前开发过一个Chrome扩展,可以在浏览器里显示一个图标和通知数量。但是我电脑上现在Chrome一打开十几个进程,我实在不想再新增扩展了。

所以这两天用Deepseek糊了一个搭配Cloudflare推送到TGBot的Worker脚本,效果如截图。设置好长毛象和TGBot的设置后,程序会每5分钟检测一次有没有新通知,有的话才进行推送操作。

准备KV

先添加一个KV,名称 KV_STORE ,备用。

准备长毛象TOken

获取长毛象Access Token,不放心的话在权限部分可以单独只设置通知获取权限

  1. 登录你的 Mastodon 实例(如 https://mastodon.social)。
  2. 进入 "Preferences(偏好设置) > Development(开发)(或直接访问 https:///settings/applications)。
  3. 点击 "New Application(新建应用):
    1. Application Name(应用名称):填写你的应用名称(如 MyBot)。
    2. Website(网站)(可选):填写你的应用网站(如果没有可留空)。
    3. Scopes(权限):选择你需要的 API 权限(如 read、write、follow 等)。
  4. 点击 "Submit(提交),系统会生成:
    1. Client Key(客户端 ID)
    2. Client Secret(客户端密钥)
    3. Access Token(访问令牌)(可直接使用)

准备Cloudflare Worker

再添加一个Worker,代码如下,并修改代码中的 config 部分的配置为你自己的设置,其中长毛象token

// 配置部分
const config = {
  // Mastodon 配置
  mastodon: {
    instance: 'https://your-instance.social', // 替换为你的 Mastodon 实例地址
    accessToken: 'ZTDJN2ZMZMZI5MTU1MZHH',  // 替换为你的 Mastodon 访问令牌
    lastNotificationIdKey: 'last_notification_id' // KV 存储中保存最后通知ID的键名
  },
  
  // Telegram 配置
  telegram: {
    botToken: 'your-bot-token', // 替换为你的 Telegram Bot Token
    chatId: 'your-tg-chart-id'               // 替换为接收消息的聊天ID
  },
  
  // 检查间隔(分钟)
  checkInterval: 5
};

// 主处理函数
export default {
  async scheduled(event, env, ctx) {
    // 执行检查通知任务
    await checkNotifications(env);
  },
  
  async fetch(request, env) {
    // 手动触发检查
    if (new URL(request.url).pathname === '/check') {
      await checkNotifications(env);
      return new Response('Notification check triggered');
    }
    
    return new Response('Not found', { status: 404 });
  }
};

// 检查未读通知
async function checkNotifications(env) {
  try {
    // 获取上次处理的通知ID
    let lastNotificationId = await env.KV_STORE.get(config.mastodon.lastNotificationIdKey);
    lastNotificationId = lastNotificationId || '0';
    
    // 获取新通知
    const notifications = await fetchMastodonNotifications(lastNotificationId);
    
    if (notifications.length > 0) {
      // 有新通知,发送到 Telegram
      await sendToTelegram(notifications, env);
      
      // 更新最后处理的通知ID
      const latestId = notifications[0].id;
      await env.KV_STORE.put(config.mastodon.lastNotificationIdKey, latestId);
      
      console.log(`Sent ${notifications.length} new notifications to Telegram. Latest ID: ${latestId}`);
    } else {
      console.log('No new notifications.');
    }
  } catch (error) {
    console.error('Error checking notifications:', error);
  }
}

// 从 Mastodon 获取通知
async function fetchMastodonNotifications(sinceId) {
  const url = new URL(`${config.mastodon.instance}/api/v1/notifications`);
  url.searchParams.append('exclude_types[]', 'follow');
  url.searchParams.append('exclude_types[]', 'follow_request');
  url.searchParams.append('since_id', sinceId);
  
  const response = await fetch(url.toString(), {
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
      'Authorization': `Bearer ${config.mastodon.accessToken}`
    }
  });
  
  if (!response.ok) {
    throw new Error(`Mastodon API error: ${response.status} ${response.statusText}`);
  }
  
  return await response.json();
}

// 发送通知到 Telegram
async function sendToTelegram(notifications, env) {
  // 按时间倒序排列通知
  notifications.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
  
  let message = `📨 你有 ${notifications.length} 条新通知:\n\n`;
  
  notifications.forEach(notification => {
    const sender = notification.account; // 通知发起者
    const senderName = sender.display_name || sender.username;
    const senderHandle = `@${sender.acct}`;
    const senderUrl = sender.url;
    
    // 根据不同通知类型构建不同消息
    switch(notification.type) {
      case 'mention':
        const mentionContent = stripHTML(notification.status.content);
        message += `💬 <b>${senderName}</b> (${senderHandle}) 提到了你:\n${mentionContent}\n\n`;
        break;
        
      case 'reply':
        const replyContent = stripHTML(notification.status.content);
        message += `↩️ <b>${senderName}</b> (${senderHandle}) 回复了你:\n${replyContent}\n\n`;
        break;
        
      case 'reblog':
        const reblogContent = notification.status 
          ? stripHTML(notification.status.content) 
          : "[内容不可用]";
        message += `🔁 <b>${senderName}</b> (${senderHandle}) 转发了你的嘟文:\n${reblogContent}\n\n`;
        break;
        
      case 'favourite':
        const favContent = notification.status 
          ? stripHTML(notification.status.content) 
          : "[内容不可用]";
        message += `⭐ <b>${senderName}</b> (${senderHandle}) 喜欢了你的嘟文:\n${favContent}\n\n`;
        break;
        
      case 'poll':
        message += `📊 <b>${senderName}</b> (${senderHandle}) 的投票已结束\n`;
        break;
        
      case 'follow':
        message += `👤 <b>${senderName}</b> (${senderHandle}) 关注了你\n`;
        break;
        
      case 'follow_request':
        message += `🫂 <b>${senderName}</b> (${senderHandle}) 请求关注你\n`;
        break;
        
      default:
        message += `ℹ️ 你有一条新通知 (${notification.type}) 来自 <b>${senderName}</b>\n`;
    }
  });
  
  // 添加来源链接
  message += `\n查看所有通知: ${config.mastodon.instance}/notifications`;
  
  // 发送到 Telegram
  const url = `https://api.telegram.org/bot${config.telegram.botToken}/sendMessage`;
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      chat_id: config.telegram.chatId,
      text: message,
      disable_web_page_preview: true,
      parse_mode: 'HTML'
    })
  });
  
  if (!response.ok) {
    throw new Error(`Telegram API error: ${response.status} ${response.statusText}`);
  }
}

// 去除 HTML 标签
function stripHTML(html) {
  return html.replace(/<[^>]*>?/gm, '');
}

配置KV和定时执行

去Cloudflare Worker设置页面绑定KV和设置定时执行。

除了定时执行外,你还可以用 https://wokrerurl.dev/check 手动触发

258、谷雨

2025年4月20日 09:47

null

谷雨时节,春雨润物,万物生长,宜早睡早起,顺应阳气升发。晨起可漫步庭院,吸纳清新之气,活络筋骨,畅通气血。饮食宜甘淡平和,多食芽菜、香椿、荠菜等时令春鲜,少食生冷厚味,以健脾祛湿。
此时肝气渐弱,心气将旺,当调畅情志,戒怒戒躁,保持恬淡安然。衣着需随温增减,尤护腰腹,防湿邪入侵。常饮谷雨新茶,清火明目,助排浊气。运动宜选八段锦、慢步等和缓方式,滋养阳气而不耗散。

使用 Cloudflare Workers 合并音频文件

2025年4月19日 19:09

最近把 Hacker News 中文播客 改成了双人对话的形式,由于目前的语音合成模型还不能很好地处理双人对话,所以需要把每个人的音频文件拼接起来。

由于项目之前运行在 Cloudflare Workflow 的 Worker Runtime, 众所周知 Worker Runtime 缺少不少 Node.JS 特性,无法调用 C++ 扩展。而且 Cloudflare Container 还没有正式上线,所以只能使用 Browser Rendering 来实现。

合并音频文件一般都使用 FFMpeg 来做,现在 FFMpeg 也可以通过 WASM 在浏览器内运行了。所以大体的技术方案是:

  1. 使用 Worker Binding 来启动浏览器实例
  2. 浏览器打开音频合并页面,合成语音文件,返回 Blob
  3. 将 Blob 返回给 Worker 后存入 R2

整体代码量不多,但是由于 Browser Rendering 只能远程调用,调试比较麻烦。

最终实现代码:

浏览器内音频合并代码

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Audio</title>
  </head>
  <body>
    <script>
      const concatAudioFilesOnBrowser = async (audioFiles) => {
        const script = document.createElement('script')
        script.src = 'https://unpkg.com/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js'
        document.head.appendChild(script)
        await new Promise((resolve) => (script.onload = resolve))

        const { createFFmpeg, fetchFile } = FFmpeg
        const ffmpeg = createFFmpeg({ log: true })

        await ffmpeg.load()

        // Download and write each file to FFmpeg's virtual file system
        for (const [index, audioFile] of audioFiles.entries()) {
          const audioData = await fetchFile(audioFile)
          ffmpeg.FS('writeFile', `input${index}.mp3`, audioData)
        }

        // Create a file list for ffmpeg concat
        const fileList = audioFiles.map((_, i) => `file 'input${i}.mp3'`).join('\n')
        ffmpeg.FS('writeFile', 'filelist.txt', fileList)

        // Execute FFmpeg command to concatenate files
        await ffmpeg.run(
          '-f',
          'concat',
          '-safe',
          '0',
          '-i',
          'filelist.txt',
          '-c:a',
          'libmp3lame',
          '-q:a',
          '5',
          'output.mp3',
        )

        // Read the output file
        const data = ffmpeg.FS('readFile', 'output.mp3')

        // Create a downloadable link
        const blob = new Blob([data.buffer], { type: 'audio/mp3' })

        // Clean up
        audioFiles.forEach((_, i) => {
          ffmpeg.FS('unlink', `input${i}.mp3`)
        })
        ffmpeg.FS('unlink', 'filelist.txt')
        ffmpeg.FS('unlink', 'output.mp3')

        return blob
      }
    </script>
  </body>
</html>

Worker 调用代码

export async function concatAudioFiles(audioFiles: string[], BROWSER: Fetcher, { workerUrl }: { workerUrl: string }) {
  const browser = await puppeteer.launch(BROWSER)
  const page = await browser.newPage()
  await page.goto(`${workerUrl}/audio`)

  console.info('start concat audio files', audioFiles)
  const fileUrl = await page.evaluate(async (audioFiles) => {
    // 此处 JS 运行在浏览器中
    // @ts-expect-error 浏览器内的对象
    const blob = await concatAudioFilesOnBrowser(audioFiles)

    const result = new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.onloadend = () => resolve(reader.result)
      reader.onerror = reject
      reader.readAsDataURL(blob)
    })
    return await result
  }, audioFiles) as string

  console.info('concat audio files result', fileUrl.substring(0, 100))

  await browser.close()

  const response = await fetch(fileUrl)
  return await response.blob()
}

const audio = await concatAudioFiles(audioFiles, env.BROWSER, { workerUrl: env.HACKER_NEWS_WORKER_URL })
return new Response(audio)

上面的代码基本是 Cursor 写的,最终效果可以去 Hacker News 代码仓库 看。

stat

walineJson

2025年4月19日 09:03

walineJson

typecho的waline评论插件

下载

教程

按照Waline官方教程引入Waline即可,其中配置js改成如下代码

<script>
    Waline({
        el: '#waline',
        serverURL: '你的域名/index.php/api/',
        path:'<?php $this->cid() ?>',
        dark:'body[class="uk-light"]',
        avatar: 'retro',
        copyright: false,
        math:true,
        highlight: 'github-dark-dimmed',
        login:'disable'

    });
</script>

如果你开了伪静态则可以去掉代码中的index.php

开源地址

https://github.com/zmoyi/WalineJson

字节Trae:小白也可以轻松上手AI编程了。

作者斌仔
2025年4月18日 16:58

前言:AI时代,人人都能成为创造者

十年前,“编程"还是少数人的专利,如今字节跳动推出的 Trae 正在打破这一规则。这个国产 AI 工具让普通人用自然语言就能开发软件、处理数据甚至搭建网站。无论你是程序员、运营人员还是内容创作者,Trae 就像一位 24 小时待命的超级助手,把"想法变现实"的门槛降到了史无前例的低点。本文将带您全面了解 Trae 在不同场景下的应用案例。

Trae
Trae

Trae的八大应用场景与典型网站

  1. 无代码开发平台(trae.com.cn) :作为国内首个 AI 原生 IDE,这里最适合技术小白。只需像聊天一样描述需求,比如"做个图片压缩工具",Trae就能自动生成完整代码。实测中,用户仅用自然语言指令就开发出了支持本地处理、实时预览的图片压缩工具,压缩效果堪比专业软件(原图 1MB → 64KB 画质无损)。该版本搭载 DeepSeek R1 模型,中文理解能力极强。

  2. 编程学习社区(trae.ai):国际版网站汇聚了全球开发者案例,支持 Claude 3.7 和 GPT-4o 双模型。这里不仅能生成代码片段,还能通过" Chat 模式"获得编程指导。例如用户开发贪吃蛇游戏时,Trae 不仅生成代码,还解释了碰撞检测的实现原理。特别适合想进阶学习的开发者。

  3. 爆款内容创作工场 :某写作达人通过 trae.bytedance.com,结合 Claude 3.7 模型,5分钟将语音草稿改写成 VP 点赞的文章。秘诀在于建立风格模板(如卡兹克轻松文体/李尚龙情感文体),Trae 能自动匹配语气词、调整段落节奏。该站点日均产出 3000+ 篇 SEO 优化内容。

  4. 数据处理中心 :面对 612 万行的CSV文件,普通Excel直接卡死。但在 trae.ai的国际版中,用户用自然语言指令:"删除到期日期>2024年的记录,保留指定字段",Trae自动生成Python清洗代码,20分钟完成5G数据清洗。支持Pandas、Numpy等库的智能调用。

  5. 个人博客系统 :通过"搭建支持 Markdown 的响应式博客"指令,Trae 生成 Vue+ElementUI 的网站框架,并与Cloudflare 无缝对接实现免费托管。开发者@掘墓人的小铲子用它搭建了包含满减计算器、红包入口的薅羊毛网站,从开发到上线仅2天。

  6. 企业级应用开发 :国内某电商团队用Builder模式开发了包含商品管理、订单追踪的 ERP 系统。Trae自动处理了JWT鉴权、数据库连接等复杂功能,并生成API文档。该案例证明Trae已具备中大型项目开发能力。

  7. 跨学科研究助手 :科研人员通过"计算资金加权净值"指令,Trae不仅生成Python代码,还自动绘制TW-MW回报率对比图表,并添加了Matplotlib交互控件。这种"需求→代码→可视化"的一站式服务,正在改变传统研究模式。

  8. 教育实践基地 :编程教师利用 trae.com.cn 生成教学案例,比如待办清单应用的代码对比(原始 vs 优化版本)。学生通过修改 Trae 生成的注释完善代码,学习效率提升 3 倍。平台已收录 2000+ 个教学项目。

为什么选择Trae?三大核心优势

  1. 零门槛创造:从图片压缩工具到个人博客,用户全程无需接触代码。Builder模式像"需求翻译器",把自然语言转化为可执行方案。

  2. 全场景覆盖 :支持Web开发、数据处理、内容创作等8大领域,既有面向企业的API开发,也有适合个人的小红书爆文生成。

  3. 国产化优势 :国内版深度优化中文语境,在指令理解上比国外工具准确率提升37%。所有数据处理默认本地运行,杜绝隐私泄露风险。

总结:让创造回归本质

Trae正在重塑技术边界——程序员用它 10 分钟开发复杂应用,作家靠它日更万字长文,学生借助AI理解编程逻辑。这种"想法驱动开发"的模式,让每个人都能专注于创意本身。正如开发者@李海所说:"我们终于不用被困在代码语法里,而是成为真正的问题解决者。"

下载地址

国内版和海外版的界面一致,但在大模型的选择上有所不同。国内版使用字节自己的模型和DeepSeek的版本,而海外版使用ChatGPT和Claude的版本。

游戏体验《生死永相随永相伴》(together we live)

作者2broear
2025年4月17日 20:21

最近在手机上打通一个小众gal,类似纯视觉小说体验,总体下来感觉不错。看到游戏评论有些人没玩懂,其实这个游戏想表达的内容还挺多的,加上内容篇幅也不算太短。在这部游戏中你可以与自己和解并理解如何去爱别人,理解放手所包含的真正意义,以及认知过程与结果之间的关系 并正确面对分合离别。综上所述个人感觉很值得一玩。

网上搜了下,貌似热度很低,好像没啥人玩,我是之前偶然下到手机上的,其实这部作品还可以的,至少传达的思想挺好的。咱就说,陪伴最是长情,人生旅途中会有很多很多的相遇,莫要因为结果不称人意就忽略了最重要的过程,人生中也会有很多悲欢离合,如何正确面对对待也是每个人的必修课。

游戏讲述了在未来的末世,被禁锢行动苏醒的男主与拥有不死之身身陷生死轮回的女主之间的羁绊所发生的一系列故事(在索引界面其实可以发现故事线是由女主视角展开的),表面上看是男主似乎拯救了女主,但从另一个角度来看这其实是一个相互救赎的故事,因为到最后他们都释怀了埋藏在彼此内心的真实情感。

简单回顾一下剧情(含剧透,感兴趣的可以点开看看)

故事开始于未来世界,那时的地球已经破败不堪,这颗水球的水已经枯竭了,这都要归功于人类之间的争端,最终自取灭亡。

彼时苏醒的男主记忆仍然停留在父亲带他走的那晚,后面再没任何记忆。男主苏醒后无法判断自己身处何位,也无法移动身体,就宛如一个全身瘫痪躺在病床的病人一般无助(个人理解这里其实是一处多义隐喻)。就在这时我们女主登场,男主抓住救命稻草一般呼救,结果下一秒谁都没想到女主原地爆炸.. 男主吓惨了,但没想到的是后面的事更让他无法接受,被炸的四散的女主居然在他眼前就这么水灵灵的原地复活了!..

不过,在女主连续几次死亡又复活后,后续男主给女主搭上了话,发现她非常单纯,思维能力就像小孩子,行为同机器人一般。

一开始对男主的恐惧表示自己是人,而男主的思维下人是绝不可能死后复活的,这样的人只能说是怪物。男主迫切想搞清怎么回事,自己身处这荒芜的大地上遇到了这样的事,而眼前怪物一般的场景更是颠覆认知。

意识到当下处境的危险,在荒无人烟的地方 自己完全不能移动,而且还有个反常的现象就是自己居然不觉得饥饿,他发觉必须做点什么。于是在女主死亡复活并离他而去又复返的一次机会,女主回来说到她忘了一件事,因为附近还有一次死亡没有被体验到,于是男主忽悠女主,执行使命的时候带着自己,继而来找寻其他人类存在…

通过对话,男主了解到女主生命的意义就是不断的去体验死亡,一次又一次,这些体验的对象就是地球上已经灭绝的生物,而这一举动被女主诠释为上帝赐予她唯一的使命,她必须完整体验全人类(哺乳类动物)死亡的最后场景,这也被创造女主的上帝称为对人类自己作死的惩罚…

男主起初只是想找到其他人类,直到有次女主为了保护男主主动被杀后,男主并不同意女主通过不断牺牲自我死亡来替人类还债的做法,作为人类的男主想瞒着女主偷偷替她分担痛苦,男主还发现女主因为不断死亡复活,已经导致她出现了一些机能上的损失,此时男主决定帮女主分担痛苦,找回她丢失的幸福和快乐,在此后探索路途,教会了女主很多曾经自己世界中的东西。

后来男主后来发现只要女主带着自己进入死亡体验,自己就可以体验女主所经历的场景。

此后探索路上,男主教会了女主很多东西,因为除了完成使命外,女主没有其他任何行为,也就是说女主此前只有痛苦,没有其他感受。男主为女主找了一条连衣裙,后来女主无意间发现了一块怀表,男主查看后发现是太阳能的,于是等待充满电后男主第一时间查看的当前的日期,这一看不要紧,原来时间已经来到男主记忆中的2000年后了,男主居然到2000年后了!这下完了,男主唯一能想到的就是冰冻休眠,唯一的可能就是有没有和他一样休眠的人。后来这块表被女主一直带在身上。

此后,女主通过死亡体验获取到与男主相同的人类可能存在于东方,他们借助怀表定位方向,来到了东边的一个地下研究所,男主在体验死亡的时候曾经来过此处,而这里死亡的人,是自愿的。就在这里,男主经历了那件让他绝望的事。

抵达研究所后没多久,当女主探索屋内的时候,男主发现这里曾经死亡过的人居然就在最近十几年前,这意味着这里说不定还有活人!紧接着门外传来了一阵脚步声,有人来了。这个人进来就控制住了女主,激动的说着男主无法理解的语言,但好在女主为了体验所有人的死亡而可以理解所有语言。貌似是要求女主做一件女主不愿意的事,男主因为无法移动只能在旁边无能狂怒。最终女主还是做了那件事,把那个人杀了。

当取下那人帽子的时候,男主吓了一跳,这哪里是人,分明是带着人脑的机器人…… 此时男主世界观发生崩塌,回想此前女主给他描述的他的样子,开始严重怀疑自我.. 让女主给他找个镜子,结果不言而喻,男主和刚被女主杀死的是一种生物….

男主始终无法接受自己是机器人这件事,让女主抛下自己走,让女主把他放在家镜子旁边,以后不要再带着他行动了。相比女主,他认为自己才是真正的怪物,这样的自己他完全难以接受。

在男主强烈要求她离开的情况下,女主还是借口各种理由返回研究所找男主,男主只觉得自己已经不是人类了,也没资格要求女主带着自己这一个累赘行动。他不知道的是,女主经过彼此长期的相处之下,早已经对他产生了难以分离的依赖感,这也是女主此前从未有过的体会,哪怕他只是机器人样子,但在女主心中他与人无异。

男主认为自己以这副形态在这种世界下忍受着活着就是上帝自己最大的惩罚,面对这样的自己他甚至想一死了之,可惜这个机器的设定就是无法自杀,不然研究所那个机器人也不会求着女主杀他了。研究所之前有很多一样的机器人,他们在苏醒后,有些和男主一样因为太久没有活动而导致电路传感短路延迟从而忘记如何活动.. 那个人尝试了很多办法都没能解决这个问题,最后他们彼此都最终杀死了对方只剩下他一个,而他在女主协助下死亡的最后一句居然是让他好好活着….

令男主意想不到的是女主居然对他道歉,说都是因为她将另一个人杀死了,才导致男主没办法活动,从而失去希望.. 正是因为如此,明明没有任何错的女主却一个劲给失去希望的男主道歉的原因.. 男主思考后决定重新找回自己在这个世界上存在的意义,与其这样日复一日年复一年不知所谓的活着,他要帮女主完成她未完成的使命,于是在男主的请求下他们再次上路了。

为了让女主尽可能少的执行使命体验死亡的痛苦,男主提出休假策略并成功执行,结果发现女主次日居然病了,并因此了解到女主的前身身世,如果不执行使命女主每次都会因为疾病而亡,虽然可以再度复活,但会承受比体验他人死亡更漫长更痛苦的体验,意识到这样的情况 后面便紧急叫停了。

让男主没想到的是女主在他不经意间已经略微察觉到他瞒着女主承担痛苦的心思了,而这样的事却令女主心理承受了更重的负担,虽然她已经习惯了依赖男主,但她不能忘记她存在的使命,也就是替人类承担罪责,男主这样的行为在某种意义上反而逐渐加深了她内心所承受的负担。

从前的她只需要为自己执行使命而活,但现在因为体验到了幸福快乐,对男主产生了依赖,让她逐渐迷失了方向,内心的矛盾在岁月长河中逐渐变大,直到最后的爆发。

男主一如往常的晚上睡觉,早上给女主打招呼,继续他们完成使命的旅程,迎来的不是一如既往的招呼而是一连串的质问,男主懵了,因为在他的印象中,自两人重新踏上旅途后并没有任何变革,而此时女主的反应则更像一个独立思想的人。在交谈中男主无意间泄露了自己参与体验了女主的死亡痛苦后,女主其实早已发觉,此时再也无法承受幸福快乐带来的代价,明明知道男主在这样做却还是因为自己的私欲默认男主为自己承担痛苦的行为,感觉自己愧对男主,再也无法忍受独自离去,留下男主一人在原地,男主无言以对 因为女主说的都是实话,因为他自己也确实这么做了。

望着女主渐行渐远的背影,男主发现那块留在地上的怀表,上面显示的时间早已超过9999年,男主完全没想到时间已经过去这么久,猛然间,男主突然发觉,自己既然是人形机器人,那这个动能会不会和这块怀表一样是太阳能?男主恍然大悟,想起女主在研究所杀掉的那个机器人,就是从他后脑那块取掉了一块和这个怀表一样的黑色的太阳能板!

这么长的时间里,自己都没发现,女主每天早上都会尝试唤醒他,而他此前装睡时却没发觉女主当时的状态,并不是每次自己都能醒来的!女主日复一日的呼唤,大多时候却只能是自言自语… 男主引导女主幸福快乐给予了痛苦之外的希望,但也是男主自己掐灭了这团希望之火。这全都是因为自己一直都被蒙在鼓里,以为女主不知道,结果却让其背负了更沉重的包袱行走了数千年….

此时男主十分懊悔,但女主已经不会再回来了。这不是对他的惩罚,是女主对自己的惩罚,她无法接受他人为了她而受罪,她本身是来赎罪的,她认为自己不能再因为自己的私欲继续这样下去了。

日复一日,年复一年,男主下定决心要站起来去寻找女主,将遗憾填补。最终,不知道是他的毅力还是感动了女主的造物主,终于他还是站了起来。根据之前承诺女主寻找东方太阳位置的方向踏上了寻找女主的旅途。在这期间,男主发现了女主遗留下来的与他曾经留下的那些点滴,这加剧了男主要找到女主的信念。

终于,皇天不负有心人,他们相遇了。但女主已经把自己内心封闭起来,奈何他们之间的羁绊太深,男主几句话就把女主的心解封了… 两人和好如初,他们决定每到世界一个角落都留下彼此存在过的印记,早上好,彼此两人比之前更加珍惜这段末世之间的关系…

直到最后,女主终于执行完世界所有死亡体验的使命,此时世界已经不知道过了多少年,两人期盼着世界回归正常,可是,一切如故没有任何变化,直到第二天。

女主又生病了,因为此时已经体验完所有生物死亡了,又开始了重复她本身的死亡体验,这种死亡过程既痛苦又漫长,于是女主恳求男主杀了她(此时我担心既然完成使命了,会不会无法复活了),男主经过漫长的心理斗争于女主多次请求下,将女主杀了。而这导致他看到了女主死亡的脸,而且是自己干的。

男主无法跨过心理这道坎,无法原谅自己亲手杀了女主,想到此前女主在研究所时,那个机器人也请求女主杀掉他时,女主又该是抱着什么样的心情呢,想到这里男主心理防线逐渐崩塌。见此情景,复活的女主连忙安慰他说自己好好的没事了,可男主还是无法接受这个事实,恍然间,男主却想到了另一种可能。

他从某种意义上来说也是人,没错,女主需要体验一次他的死亡!女主惊了,连忙表示这种事绝不可能发生,男主表示眼下只有这一种可能,分析一堆后,女主跑了。

男主也不想死,尤其是在两人在末世相互依赖如此之久的情况下,分分合合到最后,还是要忍受别离带来的痛,不仅女主无法接受,男主也一样,相互陪伴的两人都无法做出这样的选择,但是,总是要有人站出来的。

男主找到女主的时候,没有上前,此时女主告诉他她想了很多,尝试找到其他是不是漏掉的死亡体验但是没有,似乎男主提出的办法已经是最后办法了,这些她都能理解,但她就是做不到!男主告诉了她自己一路走来的真实想法,以及自己再也无法忍受要亲手杀掉她的做法,恳求女主让他最后任性一次,这似乎就是他所存在的意义了。

虽然男主没有表情,但此时两人的心情是一样的,眼看女主无法下手,男主握住女主的手放到脑袋后面的太阳能板上,想借此杀掉自己,女主极力反抗但由于力量悬殊无法,男主并不想这样违抗女主意愿来结束自己,他希望女主能理解他真正的心意,恳求女主能成全自己。可是,要亲手杀死陪伴自己一路走过来的唯一的伙伴,这又要承受多大的心理压力才能做到?…

终于,女主回想起遇到男主后与自己之间的点点滴滴,她选择与自己和解,成全了男主最后的选择……为了他,也为了她。

在亲手杀死男主后,女主短暂陷入了患得患失的境地,不敢相信是自己亲手结束了给予自己衣服 名字 幸福 快乐 与陪伴的人,陷入了深深的自责,此时,视角突然调换,女主换到男主临死前的视角,此时男主敞开心扉对遇到女主及后续一系列事件所言之语全部告诉了女主,他非常感谢女主能成全他最后的决定,这不只是为她也是为他自己,为了他们两个人,哪怕不知道这样做的后果,但也比停滞不前更好,虽然男主也想一直陪伴在女主身边,但这样明显行不通的…

在得知男主心意后的女主也是释怀的明白了,两个人能与对方相遇就已经是最大的幸福了,不论结果如何,当你想到对方的时候,有快乐和幸福的记忆,一切都是值得的。

对了,刚刚又想起结尾如梦初醒那段cg时,一个玩游戏时隐约感受到的一点,这部游戏很可能还在暗喻一种极端现实的情况。游戏中很多细节,男女主对话和互动之间貌似都暗示着一个场景..

如果真是这样的话…

开场无法动弹的男主,这是否象征着住院无法生活自理的病人?而女主,则以类似护理的身份出现,为什么这么说?从女主和男主相处后的态度上的转变就能看出来,当男主发现自己真实处境而感到绝望让女主走放弃自己时,是否意味着无法自理的人在医院因为自己处境原因不再想拖累照顾他的人呢?再到通过女主吐露心声后,两人才发现彼此之间早已相互依靠,此时男主重拾活下去的希望。最后结局,男主为了不让陪伴自己这一路走来的人继续感到痛苦,选择牺牲自己成全他人的态度,到这份心意最终也成功传达到女主心中,两人最终释怀…

WIN11更新后,C盘下莫名多出的神秘空文件夹inetpub不是BUG,不要删

2025年4月17日 10:05

微软明确说明 Windows 11用户在安装4月累积更新(KB5055523)后,不能删除系统创建的“inetpub”文件夹!!如果已经删了,需要按步骤恢复这个“inetpub”文件夹,以提高系统安全性。


最近很多人在安装微软发布的 Windows 11 4月累积更新(KB5055523)后,发现系统C盘根目录下神秘出现了一个名为 “inetpub” 的空文件夹。当时大家一致认为这又是伟大的阿三程序员写出新bug了。

“inetpub” 文件夹通常是微软 Internet 信息服务 (IIS) 创建的,IIS 是微软推出的 Web 服务器软件,用于在Windows 11上托管网站或应用程序,一般情况下只有在启用 IIS 后根目录才会出现 “inetpub” 文件夹,但在此次更新中,即使用户未启用 IIS 该文件夹也会自动创建。看起来十分不正常,于是大家都认为这是个 bug。

于是很多科技博主都表示 “这个 “inetpub” 文件夹可以删除,本身这个文件夹也是空的,测试删除后不会对系统造成任何负面影响”,我也受不了C盘有个陌生文件夹,就给删了,结果尴尬了……

但其实这个操作是为了修复漏洞“CVE-2025-21204”的,估计微软没想到大家会如此在意系统根目录出现陌生文件夹,在更新日志中压根没提到会有这个操作,考虑到用户删除“inetpub”后会影响系统安全,微软最近又更新日志添加了这个文件夹的说明

注意:直接手动重建这个文件夹对修复漏洞“CVE-2025-21204”是没有作用的!需要按正确步骤恢复

恢复方式一:
1. 点开开始菜单
2. 在搜索框里搜索开启或关闭 Windows 功能并打开
3. 勾选 IIS 服务 (Internet Information Services) 并点击确定
4. 等待 1、2 分钟 IIS 服务会完成安装,此时会自动创建“inetpub”文件夹
5. 随后关闭 IIS 服务

恢复方式二(微软的推荐做法):
1. 设置 → Windows更新 → 更新历史记录
2. 拉到底,选「卸载更新」
3. 找到 「用于Microsoft Windows的安全更新(KB5055523)」,点击卸载,等待卸载完毕后,重启电脑
4. 重启后,再次检查系统更新,并重新安装「适用于 Windows 11 Version 24H2 的 04 累积更新,适合基于 x64 的系统 (KB5055523)」即可自动恢复“inetpub”文件夹。

The post WIN11更新后,C盘下莫名多出的神秘空文件夹inetpub不是BUG,不要删 appeared first on 秋风于渭水.

Claw Cloud Run 免费容器来了,无需信用卡,Github账号满180天,终身每月送5$

2025年4月16日 15:20

Claw Cloud Run是 Claw Cloud 旗下的轻应用平台,你可以理解成 Vercel、Netlify 之类的东西,既是 PaaS 平台,也有 Serverless 服务,还能跑类 Docker 项目(不是直接用 Docker 跑的,是自动适配,用 Serverless 形式跑的),可以快速部署 Alist、Rsshub、Memos、Uptime Kuma、Chatgpt-next-web等程序。甚至可以用来跑 Minecraft 游戏服务器,注册即送 5 刀额度,如果绑定一个注册超过180天的 Github 账号可以永久享受每月5美元的额度。每个可用区有4H 8G 10G 资源,一共 10 GB 流量。


有白嫖自然是很开心的嘛,虽然Vercel也挺好用的,但是Claw Cloud Run还有免费的数据库可用,跑一些个人项目会更加方便一点,最主要是,新加坡区和日本区到国内的速度相当不错:

注册页链接 (有AFF)

如果不想走我的AFF的话,自己谷歌一下「Claw Cloud Run」就能找到官网了。

使用注册超过180天的github账号登录的话,可以享受每月5刀的赠送,不需要信用卡。
如果没有的话,则只会赠送一次5刀的额度。

注意:每月送的5刀的账户,还是免费计划而不是爱好计划。送你的5刀是让你抵扣使用费用的。
根据页面上的预计费用,Alist 每天0.04刀,WordPress 每天0.06刀,通过适当自行缩小容器配置,算下来每个月免费部署4、5个自用项目还是没问题。

如何缩小容器配置

默认他一个项目最小给1H1G,对于有些小项目,实在太奢侈了,适当改小可以有效降低部署费用。
我有个给贴吧自动签到的脚本,这玩意本地跑才占用几M好不,所以我只给了0.1核64M内存。每天仅需1分钱(其实用不到的,只是因为显示上最小差值是 0.01元)。

  1. 进入应用列表
  2. 点进需要修改的项目
  3. 左上角有个「Update」,点进去
  4. 愉快的把CPU和内存改小就好了。

PS: Claw Cloud(阿爪云,又名阿里云青春版)小道消息称,这是阿里云在新加坡开的马甲(因为他们基础设施都是用的阿里云的)。不过也有一种可能是基于阿里云的二道贩子在蹭热度,这在云服务提供商中也并不少见。

友情提醒
1. 目前 Claw Cloud 的 Janpan 和 Singapore 非常拥挤,随便一个项目开机都要好几分钟,请尽量避开日本和新加坡区。
2. 他家服务稳定性比较,嗯,最近可能人多,有点炸裂,如果项目出问题了,请重启项目解决。

The post Claw Cloud Run 免费容器来了,无需信用卡,Github账号满180天,终身每月送5$ appeared first on 秋风于渭水.

使用 React Native Screens 构建一个 Native Navigation 之内部原理

作者Innei
2025年4月16日 00:04
该渲染由 Shiro API 生成,可能存在排版问题,最佳体验请前往:https://innei.in/posts/tech/building-native-navigation-with-react-native-screens-part-2

上回说到,我们已经初步了解 React Native Screens 的 ScreenStackItem 用法。这节我们探索一下这个组件的原生实现,然后写一个简单的 Navigation 类。

ScreenStackItem 是怎么实现的

源码定位

进入 React Native Screens 的源码,我们找到这个组件的位置。

我们看到这里使用了 Screen 组件。继续查找。

const Screen = React.forwardRef<View, ScreenProps>((props, ref) => {
  const ScreenWrapper = React.useContext(ScreenContext) || InnerScreen

  return <ScreenWrapper {...props} ref={ref} />
})

Screen 组件使用了 InnerScreen 组件。它是对 NativeView 的进一步封装。

用到了 AnimatedScreen,它针对不同场景使用的 Native 组件不同。但是最终都是一个 Screen 基类。

上面的 AnimatedNativeScreenAnimatedNativeModalScreen 都是实实在在的原生组件了。他们分别对应的是 RNSScreenRNSModalScreen 组件。

以 iOS 为例,找到其原生实现。通过 RSScreen.h 头文件看到,这个组件在原生中是一个 ViewController。所以它才会有这些生命周期事件。例如 viewDidLoadviewWillAppearviewDidAppear 等。


@interface RNSScreen : UIViewController <RNSViewControllerDelegate>

- (instancetype)initWithView:(UIView *)view;
- (UIViewController *)findChildVCForConfigAndTrait:(RNSWindowTrait)trait includingModals:(BOOL)includingModals;
- (BOOL)hasNestedStack;
- (void)calculateAndNotifyHeaderHeightChangeIsModal:(BOOL)isModal;
- (void)notifyFinishTransitioning;
- (RNSScreenView *)screenView;
#ifdef RCT_NEW_ARCH_ENABLED
- (void)setViewToSnapshot;
- (CGFloat)calculateHeaderHeightIsModal:(BOOL)isModal;
#endif

@end

RNSModalScreen 则是继承于 RNSScreen 的。

@interface RNSModalScreen : RNSScreenView
@end

这个为什么需要分别定义两个组件?

在 iOS 中,modal 和普通的 view 有所区别,modal 需要脱离 Root Navigation Controller 进入一个新的 Navigation Controller 中。它是一个孤立的视图。

那么,光有 ViewController 肯定是不行的。我们还缺少一个管理 VC 的 Navigation Controller。还记得之前我们在使用 ScreenStackItem 的时候,需要包一个 ScreenStack 吗?

我们找到它,在 iOS 中对应 RNSScreenStack,它是一个 Navigation Controller。

@interface RNSNavigationController : UINavigationController <RNSViewControllerDelegate, UIGestureRecognizerDelegate>

@end

@interface RNSScreenStackView :
#ifdef RCT_NEW_ARCH_ENABLED
    RCTViewComponentView <RNSScreenContainerDelegate>
#else
    UIView <RNSScreenContainerDelegate, RCTInvalidating>
#endif

- (void)markChildUpdated;
- (void)didUpdateChildren;
- (void)startScreenTransition;
- (void)updateScreenTransition:(double)progress;
- (void)finishScreenTransition:(BOOL)canceled;

@property (nonatomic) BOOL customAnimation;
@property (nonatomic) BOOL disableSwipeBack;

#ifdef RCT_NEW_ARCH_ENABLED
#else
@property (nonatomic, copy) RCTDirectEventBlock onFinishTransitioning;
#endif // RCT_NEW_ARCH_ENABLED

@end

@interface RNSScreenStackManager : RCTViewManager <RCTInvalidating>

@end

页面切换的实现原理

现在 Navigation Controller 和 View Controller 都有了,那么 React Native Screens 是如何管理页面之间的切换并做出动画的呢。

我们知道在 iOS 中,使用在 Navigation Controller 上命令式调用 pushViewController 方法,就可以实现页面之间的切换。但是上一篇文章中的 demo 中,我们并没有调用任何原生方法,只是 React 这边的组件状态发生了更新。

还记得吗,回顾一下。

const Demo = () => {
  const [otherRoutes, setOtherRoutes] = useState<
    {
      screenId: string
      route: ReactNode
    }[]
  >([])
  const cnt = useRef(0)
  const pushNewRoute = useEventCallback(() => {
    const screenId = `new-route-${cnt.current}`
    cnt.current++
    setOtherRoutes((prev) => [
      ...prev,
      {
        screenId,
        route: (
          <ScreenStackItem
            style={StyleSheet.absoluteFill}
            key={prev.length}
            screenId={screenId}
            onDismissed={() => {
              setOtherRoutes((prev) => prev.filter((route) => route.screenId !== screenId))
            }}
          >
            <View className="flex-1 items-center justify-center bg-white">
              <Text>New Route</Text>
            </View>
          </ScreenStackItem>
        ),
      },
    ])
  })
  return (
    <ScreenStack style={StyleSheet.absoluteFill}>
      <ScreenStackItem screenId="root" style={StyleSheet.absoluteFill}>
        <View className="flex-1 items-center justify-center bg-white">
          <Text>Root Route</Text>
          <Button title="Push New Route" onPress={pushNewRoute} />
        </View>
      </ScreenStackItem>
      {otherRoutes.map((route) => route.route)}
    </ScreenStack>
  )
}

我们只是通过更新 React Children 对应有几个 ScreenStackItem 组件,就实现了页面之间的切换。

那么,这个过程到底发生了什么呢?

其实都是在 RNSScreenStack 中处理的,通过比较更新前后的 children 数组,来决定是 push 还是 pop。


- (void)didUpdateReactSubviews
{
  // we need to wait until children have their layout set. At this point they don't have the layout
  // set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
  // ui queue will guarantee that the update will run after layout.
  dispatch_async(dispatch_get_main_queue(), ^{
    [self maybeAddToParentAndUpdateContainer];
  });
}


- (void)maybeAddToParentAndUpdateContainer
{
  BOOL wasScreenMounted = _controller.parentViewController != nil;
  if (!self.window && !wasScreenMounted) {
    // We wait with adding to parent controller until the stack is mounted.
    // If we add it when window is not attached, some of the view transitions will be blocked (i.e.
    // modal transitions) and the internal view controler's state will get out of sync with what's
    // on screen without us knowing.
    return;
  }
  [self updateContainer];
  if (!wasScreenMounted) {
    // when stack hasn't been added to parent VC yet we do two things:
    // 1) we run updateContainer (the one above) – we do this because we want push view controllers to
    // be installed before the VC is mounted. If we do that after it is added to parent the push
    // updates operations are going to be blocked by UIKit.
    // 2) we add navigation VS to parent – this is needed for the VC lifecycle events to be dispatched
    // properly
    // 3) we again call updateContainer – this time we do this to open modal controllers. Modals
    // won't open in (1) because they require navigator to be added to parent. We handle that case
    // gracefully in setModalViewControllers and can retry opening at any point.
    [self reactAddControllerToClosestParent:_controller];
    [self updateContainer];
  }
}


- (void)updateContainer
{
  NSMutableArray<UIViewController *> *pushControllers = [NSMutableArray new];
  NSMutableArray<UIViewController *> *modalControllers = [NSMutableArray new];
  for (RNSScreenView *screen in _reactSubviews) {
    if (!screen.dismissed && screen.controller != nil && screen.activityState != RNSActivityStateInactive) {
      if (pushControllers.count == 0) {
        // first screen on the list needs to be places as "push controller"
        [pushControllers addObject:screen.controller];
      } else {
        if (screen.stackPresentation == RNSScreenStackPresentationPush) {
          [pushControllers addObject:screen.controller];
        } else {
          [modalControllers addObject:screen.controller];
        }
      }
    }
  }

  [self setPushViewControllers:pushControllers];
  [self setModalViewControllers:modalControllers];
}

当 React 组件的 children 发生变化会调用 didUpdateReactSubviews 方法。然后最后进入到 updateContainer 方法中。

updateContainer 方法中,会根据 RNSScreenViewstackPresentation 属性,来决定是 push 还是 pop。然后调用 setPushViewControllers 或者 setModalViewControllers 方法,来更新原生的视图。

setPushViewControllers 方法中调用原生的 pushViewController 方法。

所以,在 Native 中,整个 Navigation Controller 都是无状态的,他虽然存储 Controller 数组,但是只会比较前后得出需要过度的页面。

这也导致了,在 React 中如果你没有管理 ScreenStackItem,在触发 dismiss 之后,虽然看到页面返回了,但是再次点击进入之后就会推入比上一次 +1 的页面。

也因为这个原因,在 onDismissed 事件中,Native 无法告诉 React Native 这边被 dismiss 的页面是哪个,而是只能提供 dismiss 的数量。

onDismissed?: (e: NativeSyntheticEvent<{ dismissCount: number }>) => void;

下一步计划

好了,这篇文章就到这里了。篇幅已经有点长了。

那么,下一篇文章,我们再来实现一个简单的 Navigation 类吧。

看完了?说点什么呢

Nanobrowser是什么?一款免费开源的 AI Web 自动化工具

作者斌仔
2025年4月15日 16:55

Nanobrowser是什么

Nanobrowser 是一款在浏览器中运行的开源 AI Web 自动化工具。它是 OpenAI Operator 的免费替代方案,具有灵活的 LLM 选项和多智能体系统。

Nanobrowser
Nanobrowser

为什么选择 Nanobrowser?

您是否正在寻找一款功能强大的 AI 网络代理,但又不想像 OpenAI Operator 那样每月花费 200 美元?Nanobrowser是一款 Chrome 扩展程序,它提供优质的网络自动化功能,同时让您完全掌控:

  • 100% 免费- 无订阅费或隐藏费用。只需安装并使用您自己的 API 密钥,并且只需为使用密钥所需的内容付费。
  • 注重隐私- 一切都在您的本地浏览器中运行。您的凭据始终属于您,绝不会与任何云服务共享。
  • 灵活的 LLM 选项- 连接到您首选的 LLM 提供商,并可以自由地为不同的代理选择不同的模型。
  • 完全开源- 浏览器自动化运行方式完全透明。无黑盒或隐藏进程。

注意:我们目前支持 OpenAI、Anthropic、Gemini、Ollama 和自定义 OpenAI 兼容提供商,未来将支持更多提供商。

主要特点

  • 多代理系统:专门的人工智能代理协作完成复杂的网络工作流程
  • 交互式侧面板:直观的聊天界面,实时更新状态
  • 任务自动化:无缝地跨网站自动执行重复的 Web 自动化任务
  • 后续问题:针对已完成的任务询问上下文后续问题
  • 对话历史记录:轻松访问和管理您的 AI 代理交互历史记录
  • 多个 LLM 支持:连接您首选的 LLM 提供商,并将不同的模型分配给不同的代理

快速入门

  1. 从 Chrome 网上应用店安装

    (稳定版本):

重要提示:要获得最新功能,请从下面的“手动安装最新版本”进行安装,因为 Chrome 网上应用店版本可能会因审核过程而延迟。

  1. 配置代理模型

    • 单击工具栏中的 Nanobrowser 图标打开侧边栏
    • 点击Settings图标(右上角)
    • 添加您的 LLM API 密钥
    • 选择用于不同代理的模型(导航器、规划器、验证器)

体验地址

类似工具

PS邀请函多名字怎么处理?批量制作证书、邀请函、奖状图片

作者张洪Heo
2025年4月15日 15:27
这篇文章介绍了如何利用Photoshop的变量功能高效制作个性化邀请函。第一步是设计邀请函模板,并将替换的文本分别放置在独立的文本图层中。接着,创建一个包含名单的CSV文件,随后在Photoshop中为文本图层定义变量并导入CSV文件作为数据组。通过文件>导出>数据组作为文件功能,可以进行批量生成。最后,使用快速操作或图像处理器将PSD文件转换为图片格式。这种方法不仅节省了大量时间,还特别适合需要批量制作个性化邀请函的情况。

宝塔面板与Bitwarden插件不兼容,登录提示:验证码长度错误 解决方法

作者张洪Heo
2025年4月15日 14:55
这篇文章介绍了宝塔面板与Bitwarden在开启2FA后存在不兼容问题,登录时提示“验证码长度错误”且验证码无法加载,作者经过测试发现是宝塔将2FA验证码误识为登录验证码,建议关闭宝塔的动态验证码并在Bitwarden中删除验证器密钥以解决问题,目前已向社区反馈但是否官方修复仍未知。

Photoshop换电脑如何迁移工作区,将习惯的布局拷贝到其他电脑上

作者张洪Heo
2025年4月15日 11:03
这篇文章介绍了在Adobe Photoshop中导出和导入工作区设置的方法帮助用户在不同电脑之间轻松切换工作环境首先需确保已有自定义工作区接着通过保存工作区设置文件存储于用户特定路径下的WorkSpaces文件夹可将设置备份至U盘或云端在新电脑上通过复制备份文件至相应路径并重启Photoshop即可导入原先的工作区设置实现无缝切换。

Weekly #34:22 岁,我要成为有趣的大人

2025年4月15日 09:54

本期延迟了好几周,因为这几周被迫回学校做了一件很没有意义的事情。结合最近的一些体会,我现在认为填志愿时选择 HNU 是我人生的重大错误决策之一。高考完填志愿时决策根据是「学校 > 专业 > 城市」,如果让我重新选择,我会按照「城市 > 学校 > 专业」的标准。

关于此,已经写了一篇文章,得等这件事结束之后再发布。

其实我也在思考博客周记 Weekly 的组织模式:

  • 以时间为标尺,每周固定发一篇(目前的期望模式):必然存在一些还未完全成熟的想法和文字「赶鸭子上架」的情况,文章内容质量可能不及预期。这可能会导致博客整体文章质量降低。这种文章组织形式也可能会使文章泛泛而谈、缺乏主题和深度。
  • 以内容为标尺,攒够内容再发一篇:容易带来压力和拖延,无法保证定时发布,甚至有可能陷入无限期停更。

目前开始对 Weekly 的暂行调整是:不期望严格每周发布,如遇特别事件可能 gap 一周。期望全年 gap 不超过 5 周。

🎈 22 岁,我要成为有趣的大人

我 22 岁了。22 岁,正是该谈论梦想的年纪。

我认为随着年龄的增长,很多人会逐渐失去青春的热情,沦为平庸,成为无聊的人。因此,我的愿望之一是:我要成为有趣的大人

<iframe allow="autoplay *; encrypted-media *;" frameborder="0" height="150" style="width:100%;max-width:660px;overflow:hidden;background:transparent;" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-storage-access-by-user-activation allow-top-navigation-by-user-activation" src="https://embed.music.apple.com/us/album/22-taylors-version/1595423133?i=1595423261"></iframe>

☁️ There's something in the Air

新购买了 M4 MacBook Air 天蓝色!

今年 Apple 新推出的天蓝色,真的非常漂亮,我在 Apple Store 看到的第一眼就爱不释手。到手后,这个外观效果依然超出预期,在不同光线下会展现出不一样的效果。充分展现了 Apple 工业设计的实力!

我们大概永远无法想象到 2007 年的那场发布会,乔布斯从信封里拿出 MacBook Air 时,给世界带来的震撼。「There's something in the Air」,这场发布会也成了改变世界的一场发布会之一。

如今,乔布斯离开十多年了,现在的 Apple,似乎已经越来越衰落。这次 M4 MacBook Air 以及 iPad Air 产品线的更新,库克在推特上也「碰瓷」了这句标语:「There's something in the Air」。然而由于新 iPad Air 远不及预期,遭受了不少骂声。

虽然 Apple 似乎「日薄西山」,但目前还没有任何一个操作系统,设计与交互能与 Apple 的操作系统比肩。一个朋友认识一些在华为的设计师,他们整个部门都有一种不成文的共识:我们就是在抄 Apple,Apple 出什么我们就抄什么。其实可以有这种感觉:几乎任何国产手机厂商都在抄 Apple 的设计……

有时候会想,我们能用上 Apple 这样的产品,并不是历史的必然。如果世界上没有乔布斯,我们如今能选择的操作系统只有 Windows、Linux、Android 这种了。世界有乔布斯和 Apple,是一种幸运。

《浪潮之巅》里写道,科技行业存在着某种周期,技术的革命总伴随着一批公司的衰落和另一批公司的兴起。Apple 或许在走下坡路了,但目前似乎还没有设计能达到 Apple 水准的下一代人机交互终端出现。希望我们不会沦落到有朝一日只能用 Windows 这样的操作系统。

😷 生病有感

清明节放假三天,在放假前一天晚上开始身体不舒服,结果患了比较严重的感冒,直到假期最后一天晚上才有所恢复。因此,我相当于完美错过了整个假期……

生病的感觉太难受了。这种难受并不仅仅在于身体的难受,因为我理智上知道喉咙疼、头痛、咳嗽都只是暂时的,熬一熬就能过去;更难受的是认识到这一事实:属于我自己的宝贵的时光正在不断溜走,而我只能躺在床上,什么都做不了。本来这段时光应该用于学习、思考、创造,用来做其他更有意义的事情的。生病让自己的时间完全失去了掌控。

生病给人带来的最大折磨是「主观能动性」的丧失

按照上一期 Weekly 的时薪计算方法,本次生病的间接经济损失达到了至少【数据删除】元!😱

正好最近又重读了《献给阿尔吉侬的花束》,结合这次生病的体验,我再次有这种感觉:

精力充沛的身体(不生病),神志清醒的头脑(智力处于正常水平),是属于我们最大的财富

🌟 Bookmarks

Articles:

Resources:

你认知里的婚姻是什么样

作者刘郎
2025年4月13日 21:41

婚姻,从来不是纯粹简单的爱情童话,它是一锅复杂的大杂烩。这里面有利益的纠葛、精神的共鸣、信仰的坚守、安全感的追寻、物质性的陪伴,有彼此的接纳、心灵的抚慰、互动的欢愉、肌肤的亲昵和内心的满足,还有共同抚育下一代的利益捆绑。洞悉了这一点,你就触摸到了婚姻的本质:一场深度合作。

先谈谈爱情吧。爱情,不过是人类分泌的荷尔蒙,是对另一个人强烈的欢喜、关怀与亲近,是有些人眼中的一眼万年、彻底沦陷。30岁前,没经历过轰轰烈烈的爱情,或许会觉得人生缺了一角;可30岁后,还在爱情里纠结打转,就称不上有智慧了。智者并非不向往爱情,只是不会沉溺其中。千万别把爱情和婚姻混为一谈,那是琼瑶剧里的桥段,不是现实生活。

爱情充满不确定性,而婚姻则追求稳定和确定。很多人把爱情当作婚姻的全部,最后就会觉得婚姻是爱情的坟墓。这些“恋爱脑”走进婚姻后,往往痛苦不堪,根源就在于他们不明白,婚姻的本质从来不是单纯的爱情。爱情很简单,你爱我、我爱你就足够;但婚姻,却是“你家、我家、我们的家”三个家庭的碰撞,是各方利益和价值的复杂交换。

为什么很多人恋爱时甜蜜无比,一结婚就矛盾丛生?因为恋爱的本质是感情交换,凭感觉、让彼此开心就行;而婚姻的本质是价值交换,价值可比感情高了一个维度。步入婚姻后,决定婚姻能否长久的,是两个人能否持续为对方提供价值。

婚姻,究竟是出于什么目的的合作关系呢?从男人的角度来看,最底层的婚姻思维就是“传宗接代”。他们找结婚对象时,首先考虑的是对方能否生育,能否延续家族血脉。抱着这种想法去找媳妇,标准就很容易流于表面,比如关注女方长得是否漂亮、身材是否匀称,这是最低级的婚姻思维。

中层的婚姻思维,是找一个事业帮手、贤内助,能打理家庭、照顾孩子,还能支持自己的事业。像两家资产或社会地位互补结合,就是这种思维的体现。

而顶层的婚姻思维,是寻找人生合伙人。这类男人有更高追求,他们需要的是能共同进步、互相成就的伴侣,所以他们更看重灵魂契合,第一考量标准是对方的情绪稳定能力、包容度、人品和三观。

所以说,婚姻的本质是一场合作,底层男人为续香火,中层男人为找帮手,上层男人为找强队友,而渣男,不过是想让你扶贫罢了。回到最初的问题,婚姻是爱情的结合、利益的联姻,还是价值的交换?答案很明确,婚姻是价值的交换,是合作,是共赢。明白了这个道理,你就该思考,到底要找一个什么样的人共度一生?是爱的惊天动地的,还是灵魂契合、相互包容扶持的?

对于女人来说,要嫁的一定是心智成熟的男人,而非仅仅年龄成熟。男人的成熟,和阅历、责任心、思维方式、价值观紧密相关。时间长了你会发现,男人的长相没那么重要,重要的是有没有智慧、有没有担当;男人有多少钱也不重要,重要的是舍不舍得为你花。

很多女人容易在婚姻问题上犯错。第一种,婚前认为好的婚姻就是找到“好人”。这其实是把期待完全寄托在对方身上,期待越高,失望和痛苦就越大。到底什么是“好人”?长得高帅、会关心人、给你患得患失的浪漫感觉,或者在你孤独无助时给点温柔,比如半夜送个外卖,就能算好人吗?

在亲密关系初期,千万别被这些花点小钱就能做到的事轻易打动。很多女人以为找到了避风港,结婚后才发现对方是人生最大的风雨,甚至成了对方的“新娘”(新的娘,照顾对方生活)。

婚前可能觉得爱情至上,一定要嫁给爱情才幸福,可婚后会明白,自我成长在婚姻中更为关键,夫妻共同进步,在小家庭之外有自己的社交圈子,有边界感,有志同道合的朋友,这才是幸福。婚前以为婚姻只是感情交换,“有情人终成眷属”,婚后才发现,其实是价值交换。在男女关系里,异性看重的永远是你的价值,而非毫无底线的付出。只有让自己变得更优秀,才能吸引对方持续爱你。年轻的时候,或许还有容貌、皮肤等外在价值,可年老之后呢?别指望有人会无条件忠诚和付出,除非你一直有对方想要的价值。

所以,女性朋友们,婚前你可能觉得“你负责挣钱养家,我负责貌美如花”很美好,婚后就会明白,旗鼓相当、势均力敌的婚姻才能更长久。

第二种错误,婚前认为好的婚姻是遇到对的人,其实不然。走进婚姻许久的人会发现,好的婚姻是遇到更好的自己。从树立边界的角度讲,无论是爱情还是婚姻,都要保持自我独立,也就是要有边界感。婚姻不只是经济和生理上的结合,也是两个自我的融合。

如果把希望全寄托在另一半身上,永远无法成长为完整的自己。亲密关系的本质是,你希望别人怎么爱你,就先这样爱自己。那些把婚姻当救命稻草、丧失自我的人,连一个人的日子都过不好,又怎么能经营好两个人的生活呢?

实际上,你和伴侣的婚姻关系,不会超过你和自己的关系,这是由基因决定的。和自己关系好了,婚姻关系自然会改善,因为你和自己建立亲密关系的过程,会投射到伴侣身上。从沟通角度看,两个人生活在一起,要相互包容,收敛自己的个性和缺点。比如妻子爱热闹,丈夫爱独处,一开始可能矛盾不断,但时间长了,经过磨合,就会学会欣赏、尊重、理解和包容对方,找到共同兴趣爱好。好的婚姻不是1+1=2,而是0.5+0.5=1,双方都要去掉一半个性,才能组成美满家庭。

灵魂契合的人在一起,能抵消内耗,净化心灵。很多时候,一个眼神、刚开口的半句话,对方就能懂你的想法,接得住你的梗,同频共鸣、相互滋养。在这样的关系里,你会不断寻找、发现、修正和提升自己,身边的人,包括爱人,都是帮助你成长的助力。相反,如果婚后脾气越来越差,不一定是你变了,可能是遇到了不理解、不体谅你的人。

两个灵魂相似的人相逢绝非偶然,都是自己感召来的。每个人都有自己的能量场,你是什么样的人,就会吸引什么样的人,能量对等、频率相似,才能灵魂相依。

觉知是学习的开始,觉察是调整的开始。好的婚姻,是兴趣上的同频共振,是精神上的门当户对,是成长上的势均力敌。人生的另一半若选错了,往后余生步步皆错。选择和谁进入婚姻,差别巨大。有的人走进你的生命,会让你觉得人间值得,成为照亮你的光;而有的人,却会熄灭你心中的光。好的婚姻,是用一个灵魂成全另一个灵魂,是相互渗透、彼此滋养、双向奔赴。

愿每一个正在读这篇文章的你,都能找到灵魂相依的那个人,你懂他的欲言又止,他懂你的言外之意,他尊重你的与众不同,你理解他的山河万里 。

写在最后

感谢大家常来我的个人博客捧场。为了让大家能更便捷地获取文章内容,我新开通了微信公众号[ 刘郎阁 ]。目前,博客上的文章《 你认知里的婚姻是什么样 》已在公众号同步更新,诚邀大家关注我的公众号,关注后不仅能第一时间看到新文章,还能更方便地和我交流互动。

大家可以在微信中搜索公众号名称 [ 刘郎阁 ],或者直接扫描下方的二维码进行关注。期待在微信公众号与大家相遇!

感谢您的关注!


刘郎阁

Links

2025年4月13日 12:28

Links

一款Typecho友情链接管理插件,支持 typecho1.2.0 及以上.

功能描述

本版本的友情链接可以支持以下的功能:
1、自建独立数据表,干净无上限的添加友情链接信息。
2、支持两种输出方式:函数方式,用于主题模板侧边栏等嵌入位置显示;HTML标签方式,用于独立页面等编辑内容显示。
3、三种输出模式:文字友链、图片友链、图文混合友链等。内设三种默认输出规则,支持自定议设定输出规则。
4、管理面板:支持友链的分类,拖拽排序以及友链启用禁用等
5、支持友链邮箱解析头像链接(数字QQ邮箱自动优先解析无QQ号头像链接,其次有QQ号链接。其他邮箱解析Gravatar头像),方便用户添加无图片的友链。
6、支持增加自定义字段,方便用户做一些个性扩展。

下载地址

github地址

https://github.com/Mejituu/Links

宜宾李庄、僰王山一日游

2025年4月12日 23:46

飞雾洞

这些年的清明节几乎都是在小雨中渡过,今年依旧不例外。

好在假日后的第一个工作日天气转晴,川内大部分地区的温度几乎是直线上升,自贡气温也临近了30°,似乎春夏的界限在这几天又开始模糊起来。

起因是平时不怎么会主动想出去玩的母上大人在清明节最后一天提出想出去转转,不过清明节三天,天气都不太好,而且假期中的后两天还漂着毛毛雨。所以和她商量了一下,我们换个班,换到了节后的第一个工作日出去,既能错峰出行,也能有个好天气。

因为只有一天,所以不能走太远,还要有充足的游玩时间,所以车程得压缩到2个小时之内。

之前 内江甜城一日游 去过一次内江,体验还是非常棒的。不过内江我们已经去过了,得换个对我们来说有新鲜感一些的。

目的地

在做攻略的时候想起之前徒步群内有群友策划过去 宜宾僰王山 徒步,宜宾离自贡也就100来公里左右,不到一个小时即可抵达,是十分不错的目的地。

其实宜宾我在很多很多年前来过一次,第一印象是——酒香!

我们当时走的老路去的宜宾,开始进入宜宾地界没多久,忽地就闻到一股淡淡酒香,开窗四处眺望无果,只发现我们正在经过一条长江支流,当时心理想着:真不愧是是五粮液产地、酒都宜宾,这河里都她妈是一股子淡淡酒香味。而后复行一里地后才发现经过一家酒厂时才恍然大悟,所以之后只要一闻到就为就能知道旁边必定有酒厂无疑了。

我们当时是自驾去的蜀南竹海,盛夏的十万亩竹海在微风的吹拂下用竹叶给我奏响了大自然的舒缓乐章我至今难以忘记。但是竹海除了竹子其他能有深刻体验的项目太少了,所以这次被我放弃了,准备还是以群友们之前去过的僰王山为主来做路线规划。

  1. 这次准备先去,因为做攻略的时候发现市区有好几个景点,担心李庄古镇玩起来太单薄所以先去,其次李庄白肉享誉全国,中午饭可以在镇子上解决,而且古镇临江而建,自有一股江南水乡的韵味。
  2. 然后去这次重点目的地 僰王山 ,山里的核心景点 飞雾洞 幽暗诡秘,十分有盗墓笔记的感觉,山中连绵不断的竹林和数个瀑布相信也会有不一样的体验,而且恰逢开春说不定还能挖到竹笋。
  3. 最后晚上再赶去合江门,逛一逛冠英古街,感受一下宜宾人的夜生活。

最后行程规划如下:

  • 8点30出发
  • 10点到李庄古镇,游玩加吃饭下午1点左右出发去僰王山
  • 2点左右到僰王山,开始爬山,来回两个小时,5点左右往回走去合江门
  • 7点左右到合江门,吃晚饭看夜景。
  • 接下来看时间回家。

总结

当时想着李庄离市中心比较远,又只是一个古镇,万一踩坑了就没地儿玩了,所以安排的上午去李庄,晚上去市中心。其实可以白天去晚上也去,听说晚上还有节目表演和白天是不一样的风格。

僰王山只有核心景点飞雾洞最值得一看,其他景点都有点可有可无了。

这种一天来回的旅程真的很不错,强度不会很高,也有非常不错的体验,以后可以常安排。

图记

下了高速不久就能看见长江大桥。

长江大桥

到了李庄停在了月亮田的停车场,在车库里进入了一个古建筑,似乎是一个酒店的大唐,雕梁画柱,气派的很。

李庄建筑

江南水乡味十足的李庄古镇。

水乡

水乡

古镇内

中午吃的李庄白肉,片肉的师傅说每片肉在2mm左右,肥而不腻。

李庄白肉和小煎鸭

吃过饭休息了一会便赶往了僰王山,竹林之内的僰王山特别凉快。

1. 僰王山入口 2. 山内石径

飞雾洞入口

探洞

天坑一景

下山时还在路边偷了春笋一根,哈哈。

第二天炒了腊肉,味道好极了。

春笋

合江门很热闹,三江交汇之处,也是老百姓们晚间活动的地方。

合江门两景

好像国外哪里也有个这个玩意儿,是拉斯维加斯吗?

3D小巨蛋

一大群叔叔阿姨、大爷大妈群魔乱舞,算是感受到「酒都人民」的松弛感了。

酒都人民的夜生活

入夜后的夹镜楼更漂亮了。

夹镜楼

冠英古街内的合江园

近期关于cobra库的一些实践心得总结

2025年4月13日 06:30
如要阅读全文,点击标题跳转。开源社区中,go 语言的 cli 库有很多,有名的也不少,个人觉得不需要都熟练,只需要针对一个用的熟就可以了,其他的稍作了解即可。最近在实际开发工作中,针对 cobra 库又有了一些实践心得,这里做一个汇总记录,以后如果有新的内容,也会更新在这里。

Memos同步至Mastodon(长毛象)

2025年4月12日 18:26

Photo by Chethan / Unsplash

我一直把Memos当作微博来用,偶尔也会手动同步到长毛象,但也只是偶尔。原因是即便在用梯子的情况下长毛象打开也太慢了,毕竟大多数实例都在墙外,且长毛象挺吃服务器资源的。

目前又不太想自建实例,虽然现在弄了NAS,理论上可以搭在上面,但是Ghost似乎在6月份要推出联邦宇宙服务了,到时候可以直接迁移到那个上面去,也就一直没下手。

之前看到过蜗牛哥出过一期长毛象同步到Memos的方案,和我的需求是相反的,因为今天周末恰好有空,所以就捣鼓了一下,弄了一版。

因为Memos可以设置WebHook调用,所以我的思路是:

Memos发布 -> 触发WebHook调用 -> 数据转发给Cloudflare Worker -> Worker里用Mastodon API发布嘟文

实现

先去长毛象上申请Access token ,路径为 :

偏好设置 -> 开发 -> 创建新应用 -> 起个名字,勾选 write:statuses、write:media 权限

然后就是Cloudflare Worker的实现了,我用DeepseekR1跑了一版,稍加改动就能初步使用了。

💡
如果你的Memos服务器在国内,那可能需要给Cloudflare worker绑定一个域名,不然会无法访问。
// cloudflare-worker.js
const MASTODON_INSTANCE = ""; // 实例地址
const ACCESS_TOKEN = ""; // 访问token

async function uploadMediaFromUrl(imageUrl, mimeType) {
  try {
    // 从 URL 获取图片数据
    const imageResponse = await fetch(imageUrl);
    if (!imageResponse.ok) throw new Error(`下载图片失败: ${imageResponse.status}`);
    
    // 转换为可上传的格式
    const blob = await imageResponse.blob();
    const formData = new FormData();
    formData.append("file", blob); // 文件名按需处理
    
    // 上传到 Mastodon
    const uploadRes = await fetch(`${MASTODON_INSTANCE}/api/v2/media`, {
      method: "POST",
      headers: { Authorization: `Bearer ${ACCESS_TOKEN}` },
      body: formData
    });
    
    if (!uploadRes.ok) throw new Error(`上传失败: ${await uploadRes.text()}`);
    return uploadRes.json();
  } catch (error) {
    console.error("媒体上传错误:", error);
    throw new Error(`图片处理失败: ${error.message}`);
  }
}

async function handlePost(request) {
  try {
    const { memo } = await request.json();
    const { content, visibility, resourceList = [] } = memo;
    
    

    // 验证内容
    if (!content) return new Response(JSON.stringify({ error: "内容不能为空" }), { status: 400 });

    // 处理最多 4 张图片
    const validResources = resourceList
      .filter(res => res.type?.startsWith("image/"))
      .slice(0, 4);

    // 并行上传所有图片
    const mediaUploads = validResources.map(async res => {
      const media = await uploadMediaFromUrl(res.externalLink, res.type);
      return media.id;
    });

    const mediaIds = await Promise.all(mediaUploads);

    // 构建嘟文参数
    const params = new URLSearchParams({
      status: content,
      visibility: visibility.toLowerCase() || "public" // 默认公开
    });
    mediaIds.forEach(id => params.append("media_ids[]", id));

    // 发布嘟文
    const postRes = await fetch(`${MASTODON_INSTANCE}/api/v1/statuses`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${ACCESS_TOKEN}`,
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: params
    });

    if (!postRes.ok) throw new Error(await postRes.text());
    return new Response(JSON.stringify(await postRes.json()), { status: 200 });
    
  } catch (error) {
    console.error("处理错误:", error);
    return new Response(JSON.stringify({ error: error.message }), { status: 500 });
  }
}

// Worker 入口
export default { fetch: handlePost };

CloudFlare Worker代码

给Memos启用Webhook

设置 -> 偏好设置 -> Webhook -> 创建 -> 起个名字,填入Cloudflare Worker的地址

一些不足

这种方式同步速度应该会稍快,但是还是有一些限制,比如

  1. 图片太大超过Worker可运行时间会上传失败。
  2. Mastodon API好像最大只能传4张图片。
  3. 向Memos的TG机器人发的消息不会触发Webhook。
  4. 长毛象好像不支持 Markdown。
  5. 我使用的Memos后端版本为 v0.18.1
  6. 等等,目前只发现上面2个,应该不止。

waterMark

2025年4月12日 11:45

Typecho图片水印插件

本插件仅支持 png \ jpg 格式的图片作为水印。
推荐使用 png 格式的图片作为水印,以保证水印的质量。

下载

使用说明

需要自行替换插件内置的水印图片素材

沉迷“吉卜力”中……

作者林木木
2025年4月12日 07:59

苦于官方网络、数量、版权等限制,4o 生图只能眼巴巴看别人分享。

昨天看到 v2ex 上有牛人搞了套壳,免费 3 张,一试,效果极佳,一看定价,立马大会员支持。

邀请链接

当前价位(250412):¥9.9(共20次)、¥19.9(共60次)、¥49.9(共200次)。

注:开发大大说,成本太高,涨价在即。我的邀请链接(你付费后,我会得到 10 张额度。):

https://ghiblio.art/?utm_source=P9ZKx72A8v4z

沉溺其中

生图小体验:有人,多人,神情或动作丰富;照片本身颜色、层次丰富。

小朋友自带奶萌奶萌,满满温情爆棚;小姐姐小哥哥立马帅气、魅力十足;中年嘛,装个可爱也不是不行。老年,有种返老还童之感。

清明台州游记

2025年4月10日 19:17

约了朋友清明节一起去台州旅行,清明当天出发,周一返回,周二上班。去了国清寺,徒步公盂景区,还爬了江南长城,去的时间不短,但是因为景区离得都比较远因此去的地方并不多,下面是详细内容。

Day1 赶路

清明当天开车出发,本想着早上晚点出发下午能早点到,还能看一下国清寺,结果10点从家里出发,过了12点还没出上海,到了1点多才到了嘉绍大桥服务区。于是第一天就直接开到酒店睡觉了,路上吃了饭,冲了电,到了酒店天已经黑的啥都看不见了。 晚上去村子里面充电,发现这边四面被被山包围,主要是括苍山,这个村子也叫括苍村,村里有足球场和篮球场,房子也修建的很整齐,还有个翁森纪念馆,后来查了下是宋代的一个文化人。

Day2 公盂徒步

第二天一早,显示在酒店附近转了转,这是个生态度假酒店,外面有营地,还有种了很多的油菜花,以及一些秋千等设施。

随后,我们就取车前往公盂。公盂景区和神仙居景区是靠在一起的,几年前来过一次,神仙居的风景很棒,但是里面上下都有索道,山上也搞了很多的玻璃栈道,桥啊之类的。因此我决定还是再带着朋友,和小孩一起来徒步公盂。

上一次过来还是我和媳妇两个人,并且走的穿越路线,这次带着娃一起走了个环线。因为是环线所以上次没走的那一半路这次都体验了,那一半更加有趣,路上有溪水相伴,多了更多乐趣。而之前走过的路段,很多地方都增加了台阶,爬行难度有所降低。

徒步到公盂村里面,虽然自上次过来已经过了7,8年了,但是感觉居然没啥变化。房子还是那些房子,旁边的山峰也还是那些山峰,外面每天都在发生着变化,而这里还是多年以前的模样。

因为当天攀爬公盂背的人比较多,在岔路口,我们也决定了放弃攀爬,只希望下次还有机会再来了。

徒步完本打算去仙居县城吃个荣村,结果告知座位已经订满,于是驱车前往临海市区。

Day3 临海一日游

临海最出名的也就是台州府城了,因为是老区,自然不好停车。我们在早饭之后打车来到揽胜门,开启临海一日游。

台州府城墙算是保存比较完整的,也是在山体上,从揽胜门到朝天门这一段都比较陡峭,并且看介绍说明长城的烽火台也是借鉴了这边的地台设计,因此这里又称为江南长城。 我们首先从揽胜门登上198级的台阶,在顾景楼一眺东湖公园。

随后继续登上白云楼,一路上有8个敌台,还有几个不同的城门,并且在朝天门这里有梅园,网上别人发的图片很漂亮,我们来的时候已经没有花了。

最后,我们在城墙上走到兴善门,这里下来就是古街紫阳街了。游客很多,就没去逛,找了个地方吃了个饭。就去了旁边的龙兴寺,这里据说也是日本佛教的发源地,建筑风格都有点像,我觉得寺庙了不好拍照,只拍了个千佛塔,但是居然很多小姐姐在这里摆拍。

逛完龙兴寺,我们便继续去爬了旁边的巾山,山上也有寺庙便没有参观,绕山一圈之后就回酒店了。

家人在酒店休息,我和朋友去充电。等待充电的时候在江边散步,发现墙上被很多人写了不少字,有些字很漂亮,有些写的也很有趣,这里分享一个。

晚上吃了个大排档,本来准备品尝一下荣小馆,然只有4点半的能预约,但是大家中午1点才吃饭,只能作罢。而这个大排档,味道也还不错,卤制的小龙虾加上老板特制的调料,是不一样的风味。来上一些小龙虾,配上一瓶啤酒,晚上回去就睡了个好觉。

Day4 国清寺

今日回程,正好顺路去一下国清寺,这里是隋代的古寺了,据说佛教天台(tai,第一声)宗就是这里发源,这里也是网红景点。周一已经是工作日了,这里人也不算少。跟着前面的车,直接开车进了里面的天台宾馆,这样就少走了不少路。

还没进国清寺,路边的桥和灯,就给人以很好的感觉。

寺庙中大殿,以及五百罗汉堂看的都挺令人震撼的,但是处于对佛祖的尊重,一张照片都没拍,以下是随便拍了一些照片。

既然来到了天台,中午就去当地著名的土灶头吃了一顿,麦饼,鱼头汤,鲜笋味道都挺不错的。

美餐一顿之后,驱车返程,一路上除了到上海之后有点堵,基本都很畅通,三个多小时就到家,这趟旅程就画上句号了。

看完评论一下吧

春至古寺旁,桃花映古韵

作者
2025年4月10日 16:44
加载中
北塔
桃花
法轮寺
法轮寺及北塔

沈阳的春天,总是带着一种独特的韵味在悄然绽放。今天,我来到了沈阳北塔,想一睹它在春季里的别样风采。

当我走近北塔时,那古朴庄重的身影立刻映入眼帘。北塔犹如一位历史的守望者,静静地矗立在那里,见证着岁月的流转。它的塔身线条硬朗而又不失优美,在蓝天白云的映衬下,更显古朴大气。塔身上的雕饰精致入微,仿佛在诉说着往昔的故事,那些历经风雨却依然清晰的纹理,是时间留下的深刻印记。

然而,最让我心动的还是北塔周围的桃花景色。不知何时,桃花已经悄然绽放。沿着小路漫步,满眼都是粉嫩的桃花。桃花花朵不大,簇拥在枝头,像是天边落下的云霞栖在了树枝上。微风轻轻拂过,桃花瓣如雪般飘落,纷纷扬扬,地上很快就铺上了一层薄薄的花瓣地毯。这花瓣落在地上,并不显得狼狈,反而在阳光的照耀下,如同给大地铺上了一层梦幻缎。

琪亚娜小挂件

2025年4月10日 11:31

kiana

博客可以挂起来个萌萌哒萝莉,支持拖拽,拖拽时会变化表情,点击后也会随机说台词。

食用方法:将压缩包解压到typecho的插件目录,将文件夹重命名为kiana,然后后台启用该插件即可,看首页是不是出现了萌萌哒的小萝莉。 如果没有出现,请在设置里打开加载JQ选项。

下载

图片展示

kiana-1.webp

kiana-2.webp

kiana-3.webp

kiana-4.webp

Fimg

2025年4月9日 22:22

Fimg

文章多缩略图调用插件,支持多附件调用

下载

使用代码

<?php Fimg_Plugin::showfimg($this->cid,1);?>
<?php Fimg_Plugin::showfimg($this->cid,2);?>
<?php Fimg_Plugin::showfimg($this->cid,3);?>

XQLocation

2025年4月9日 22:10

XQLocation

Typecho评论IP归属地显示插件,插件支持IPV6和IPV4归属地获取,并且 为了避免卡顿或者获取失败 数据库都是本地 不调用任何第三方API接口。

下载

使用说明

解压后修改文件夹名为 XQLocation,将插件上传至网站目录的 /usr/plugins 下,在 Typecho 后台「插件管理」处启用插件。

在需要显示的地方插入以下代码:

<?php XQLocation_Plugin::render($comments->ip); ?>

(此项也适用于后台)

如果是系统自带的评论框架
找到var/Widget/Comments/Archive.php大概为108行,插入

<?php XQLocation_Plugin::render($this->ip); ?>

出处

https://www.toubiec.cn/1194.html

现代浏览器插件开发指南

2025年4月9日 08:00

最近又写了一本小书。

这本小书定价 29 元,你可以在 这里 购买 PDF 版本。如果你有特殊需求,也可以购买 Markdown + PDF 版本,价格是 49 元(为了增加盗版成本)。

这本书篇幅不长,但因为比较小众,所以定价稍高,请读者自行斟酌购买。

这是一本关于浏览器插件开发的书。这本书面向的是那些希望开发自己的浏览器插件满足自己需求的,也许你是专业的前端程序员,或是略懂前端代码的后端人员,在 AI 的加持下,大概都能从中受益。

之所以想写这本书,是因为我在过去一两年开发过不少浏览器插件(如微信读书笔记同步助手 Notepal, 浏览器 AI 助手 SumBuddy, 专注插件 等等标签 等等),积累了不少经验。这几年越来越多人开始做浏览器插件,尤其是在 AI 时代,浏览器插件作为重要的输入入口,能做的事情越来越多。我想可以把这些经验分享出来,让更多人可以开发满足自己需求的插件。

插件开发其实不是一件很难的事,特别是现在出现了一些成熟的框架,比如 Plasmo, WXT, CRXJS 等。这些插件把代码构建、打包过程都封装得很好,开发者只需要关心插件的逻辑即可。

我自己用过 Plasmo 和 WXT, 这本书我会使用 WXT 进行教学。因为经过自己的长期实践,我认为 WXT 比 Plasmo 的设计更加合理,配套的工具更好用。当然每个框架有每个框架的利弊,即使你想使用其它框架,你还是能从这本书收获很多框架以外的知识,因为框架只是辅助。

另外,本书以 Manifest V3 为背景编写,不会讨论 Manifest V2 的内容。如果你不知道两者具体的区别,你也不需要知道(因为我也不知道)。简单来说 V3 比 V2 在安全性上做了更严格的控制,这一点虽然备受争议,但对于大部分的情况不会有太大的影响。

本书在涉及 UI 开发的部分我会使用 React, 这是我自己日常使用的库,对于使用 Vue, Svelte 等其它库的读者,也不需要担心,用什么框架并不是那么重要。并且 WXT 也支持不同的 UI 库。如果你想用其它库,翻看 WXT 的文档即可。

本书的实战中,我不引入任何样式库,因为会增加额外的和插件开发无关的复杂度。请读者不要计较实例的美观性。

读这本书前,我默认读者了解以下技术:

  • 了解 JSON 这种数据结构
  • 了解基本的 JavaScript/TypeScript, HTML, CSS 知识
  • 有能力搭建 Node.js 开发环境

读完这本书后,读者能学习到开发一个浏览器最重要的几个知识点:

  • 数据存储
  • content script UI 的构建
  • 消息传递

这几个知识点贯穿了所有实际场景必需的元素,我在开发自己的插件时,基本是围绕这三个基本的功能展开。至于其它更多的插件 API, 读者应该自己按照需求查看文档使用。

在讲解消息传递的实例中,我用 AI 消息流作为例子讲解,对于需要开发 AI 相关的浏览器插件的读者应该很有启发。

目录

  • 概述
  • 插件的基本结构
    • manifest
    • popup
    • content script
    • options
    • background script
  • 插件 API
  • 创建插件开发项目
  • 基本数据存储:Todo List 实战
  • 进阶数据存储
    • 监听数据变动
    • 数据迁移 (Migration)
  • Content Script 基本知识
    • 动态注入 content script
    • 在 content script 中渲染 UI
    • 真实环境中的一些经验
  • 消息传递
    • 单次传递
    • 长连接传递
      • 实战: 渲染 AI 消息流
  • 多语言支持
    • 插件原生多语言机制
    • 使用 i18next
  • 附录
    • 最小权限原则
    • 处理用户登录
    • host permissions
    • 定时任务
    • 自动生成不同尺寸的 icon

一些预览

alt text

alt text

show本站主题

2025年4月9日 15:41
show本站自用typecho自适应主题.webp

show

这是本站自用主题,近期考虑换别的主题,所以就把这个放出来吧。

下载

使用

主题设置功能设置里可以设置首页要显示的分类,需要填写分类mid,需要填写4个,比如我站的2,5,1,57,填写后首页就会显示不同样式排版的四个分类了,其他设置看主题设置里的描述自行配置即可。

插件适配了:https://github.com/jrotty/soso 启用插件后弹窗搜索可以支持按分类进行搜索

短代码

详见这个文章:https://store.typecho.work/archives/shortcode.html
支持大部分的短代码(少部分不支持)

UserLog

2025年4月9日 15:19

UserLog

UserLog是一款Typecho的用户登陆日志插件,支持记录常规登录与通过插件接口登录(一般为社交登录)的用户信息,且支持按月归档。

效果图

4183461331.jpeg

下载

插件更新

保留插件目录下history文件夹(里面就是存的登录日志的json文件),替换其余文件即可完成升级。

更新记录

2024年2月17日发布1.0.9版本
支持记录ip,旧版本升级此版本覆盖插件文件夹里面的两个php文件即可,本次升级由 @苏晓晴 和 @泽泽社长 共同完成

2023年11月7日发布1.0.8版本
修正一个链接地址,解决与NewAdmin插件的兼容问题
2023年2月26日发布1.0.7版本
新插入的数据改为插入的数据最前面,月份显示文字调整。
2023年2月23日发布1.0.5版本
支持只显示今天/昨天的登录记录,css调整优化移动端体验效果
2023年2月20日发布1.0.1版本
合计界面下支持统计当前年份下登录的用户人数,同时支持显示平均每人登录的次数。
2023年2月19日发布1.0.0版本
支持记录常规登录与通过插件接口登录的用户信息,且支持按月归档,支持显示最近5年的记录

PasswordProtected

2025年4月9日 15:17

PasswordProtected

Password Protected 插件用密码保护你的整个网站。要查看网站内容,访客必须输入密码。

兼容性

理论支持 Typecho1.2.0 及以上,制作过程中仅在 Typecho1.2.1 版本上进行了实际测试.

功能介绍

启动插件,在插件设置中设置访问密码,设置好后,用户访问网站需要输入正确密码才能访问,正确的密码会存储7天,七天内用户免输入密码。

插件注释清晰,方便二次开发扩展,比如将写死的密码有效期7天,弄成可以自行设置的有效期,亦或是弄个密码提示,比如引导用户加微信或关注xxx公众号来获取密码等。

下载

HTML中解决锚点定位被顶部固定导航栏遮挡的问题

2025年4月9日 14:33

在HTML中解决锚点定位被顶部固定导航栏遮挡的问题,可以通过以下多种方法实现。以下是综合各技术方案的总结和推荐:

一、使用 padding + 负margin 调整布局(推荐)

​​原理​​:通过为目标元素设置 padding-top 将其内容下移,再通过负 margin-top 抵消布局偏移,使锚点定位时实际显示位置避开顶部导航栏。
​​实现代码​​:

.target-element {
  padding-top: 60px;  /* 导航栏高度 */
  margin-top: -60px;  /* 反向抵消 */
}

​​优点​​:无需添加额外元素,纯CSS实现。
​​缺点​​:若目标元素本身有定位(如 relative),可能影响其他元素的交互(如遮挡文本选中)。

二、暗锚点(隐藏锚点元素)

​​原理​​:在目标元素前添加一个不可见的占位锚点元素(如 <span>),通过绝对定位调整其位置,使其成为实际跳转的锚点。
​​实现代码​​:

<!-- HTML -->
<div class="anchor-wrapper">
  <span id="hidden-anchor"></span>
  <h2>实际内容标题</h2>
</div>

<!-- CSS -->
<style>
.anchor-wrapper {
  position: relative;
}
#hidden-anchor {
  position: absolute;
  top: -60px;  /* 导航栏高度 */
}
</style>

​​优点​​:不破坏原有布局,兼容性好。
​​缺点​​:需额外维护隐藏锚点元素。

三、使用 scroll-padding-top 属性(现代浏览器推荐)

​​原理​​:在滚动容器(如 html)上设置 scroll-padding-top,直接预留顶部空间。
​​实现代码​​:

html {
  scroll-padding-top: 60px;  /* 导航栏高度 */
}

​​优点​​:代码简洁,无需修改目标元素。
​​缺点​​:兼容性需注意(不支持IE及部分旧版本浏览器)。

四、伪元素占位法

​​原理​​:通过 :before 伪元素为目标元素创建占位空间,并向上偏移。
​​实现代码​​:

.target-element:before {
  content: "";
  display: block;
  height: 60px;      /* 导航栏高度 */
  margin-top: -60px;  /* 向上偏移 */
}

​​优点​​:不添加额外HTML元素,避免布局污染。
​​缺点​​:伪元素可能影响交互事件的触发区域。

五、JavaScript动态调整(补充方案)

​​原理​​:监听锚点跳转事件,手动修正滚动位置。
​​实现代码​​:

window.addEventListener('hashchange', () => {
  const target = document.querySelector(window.location.hash);
  if (target) {
    window.scrollTo(0, target.offsetTop - 60); // 60为导航栏高度
  }
});

​​优点​​:灵活控制偏移量,适用于复杂场景。
​​缺点​​:依赖JavaScript,可能影响SEO和首屏性能。

扬州清明游记

2025年4月7日 16:58

清明无事驱车去扬州游玩,导航显示抵达东关街的外围,才发现"停车场已满"的红色标牌早已立满路边。车轮缓缓碾过何园、个园外围,车窗外的游客如潮水般漫过斑马线——原来扬州把整座城都过成了清明上河图。

2025-04-07T08:51:58.webp

从小巷进入东关正街,映入眼帘的就是网红”扬大酸奶“,味道像稀释了的青春记忆(3.5元的价格倒很诚实)。转角处突然飘来韭菜饼焦香,金黄酥皮在齿间碎裂的刹那,陇东高原的风倏地穿过三十年时光扑上面颊——这大概就是食物版的"他乡遇故知"。

2025-04-07T08:53:56.webp

古运河码头边,锈迹斑斑的系缆石像被按了暂停键的时钟。没有游船,只有两岸的柳枝低垂,偶尔扫过水面,像是要拂去那些淤积了太久的寂静。

2025-04-07T08:55:07.webp

石塔路绿化带里蹲着个"不合时宜"——五层六角的唐代石塔像个被现代化遗忘的老实人。车流在它旁边呼啸而过时,楠木楼飞檐正悄悄接住一片柳絮,那姿态与《营造法式》里的插图分毫不差。

2025-04-07T08:56:05.webp

当文昌阁的金顶成为自拍背景板,当漆器厂老师傅的凿刀声被电子收款提示音淹没,终于明白"烟花三月下扬州"的终极奥义:38元的家常炒饭,吃的是宋高宗也逃不过的旅游刺客。

2025-04-07T08:57:35.webp

Typecho 批量插入附件 并实现图片预览[2025/04/25更新]

作者老孙
2025年4月7日 10:52
AI摘要:文章介绍了如何在Typecho中添加批量插入附件功能,包括自动识别图片与普通文件并修正Markdown格式。通过修改主题的functions.php文件,添加JavaScript代码实现批量插入按钮和格式修正功能。

在文章的附件选项页加入批量插入所有附件的按钮

并自动识别图片与普通文件,实现图片预览功能

2025-04-07T03:20:33.png

Markdown语法格式自动修正为

![2025-04-07T02:49:18.png](https://img.imsun.org/2025/04/830007048.png)

具体代码实现
在主题的functions.php最后插入

/**
 * Typecho后台附件增强:图片预览、批量插入、保留官方删除按钮与逻辑
 * @author jkjoy
 * @date 2025-04-25
 */
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures');

class AttachmentHelper {
    public static function addEnhancedFeatures() {
        ?>
        <style>
        #file-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:15px;padding:15px;list-style:none;margin:0;}
        #file-list li{position:relative;border:1px solid #e0e0e0;border-radius:4px;padding:10px;background:#fff;transition:all 0.3s ease;list-style:none;margin:0;}
        #file-list li:hover{box-shadow:0 2px 8px rgba(0,0,0,0.1);}
        #file-list li.loading{opacity:0.7;pointer-events:none;}
        .att-enhanced-thumb{position:relative;width:100%;height:150px;margin-bottom:8px;background:#f5f5f5;overflow:hidden;border-radius:3px;display:flex;align-items:center;justify-content:center;}
        .att-enhanced-thumb img{width:100%;height:100%;object-fit:contain;display:block;}
        .att-enhanced-thumb .file-icon{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:40px;color:#999;}
        .att-enhanced-finfo{padding:5px 0;}
        .att-enhanced-fname{font-size:13px;margin-bottom:5px;word-break:break-all;color:#333;}
        .att-enhanced-fsize{font-size:12px;color:#999;}
        .att-enhanced-factions{display:flex;justify-content:space-between;align-items:center;margin-top:8px;gap:8px;}
        .att-enhanced-factions button{flex:1;padding:4px 8px;border:none;border-radius:3px;background:#e0e0e0;color:#333;cursor:pointer;font-size:12px;transition:all 0.2s ease;}
        .att-enhanced-factions button:hover{background:#d0d0d0;}
        .att-enhanced-factions .btn-insert{background:#467B96;color:white;}
        .att-enhanced-factions .btn-insert:hover{background:#3c6a81;}
        .att-enhanced-checkbox{position:absolute;top:5px;right:5px;z-index:2;width:18px;height:18px;cursor:pointer;}
        .batch-actions{margin:15px;display:flex;gap:10px;align-items:center;}
        .btn-batch{padding:8px 15px;border-radius:4px;border:none;cursor:pointer;transition:all 0.3s ease;font-size:10px;display:inline-flex;align-items:center;justify-content:center;}
        .btn-batch.primary{background:#467B96;color:white;}
        .btn-batch.primary:hover{background:#3c6a81;}
        .btn-batch.secondary{background:#e0e0e0;color:#333;}
        .btn-batch.secondary:hover{background:#d0d0d0;}
        .upload-progress{position:absolute;bottom:0;left:0;width:100%;height:2px;background:#467B96;transition:width 0.3s ease;}
        </style>
        <script>
        $(document).ready(function() {
            // 批量操作UI按钮
            var $batchActions = $('<div class="batch-actions"></div>')
                .append('<button type="button" class="btn-batch primary" id="batch-insert">批量插入</button>')
                .append('<button type="button" class="btn-batch secondary" id="select-all">全选</button>')
                .append('<button type="button" class="btn-batch secondary" id="unselect-all">取消全选</button>');
            $('#file-list').before($batchActions);

            // 插入格式
            Typecho.insertFileToEditor = function(title, url, isImage) {
                var textarea = $('#text'), 
                    sel = textarea.getSelection(),
                    insertContent = isImage ? '![' + title + '](' + url + ')' : 
                                            '[' + title + '](' + url + ')';
                textarea.replaceSelection(insertContent + '\n');
                textarea.focus();
            };

            // 批量插入
            $('#batch-insert').on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                var content = '';
                $('#file-list li').each(function() {
                    if ($(this).find('.att-enhanced-checkbox').is(':checked')) {
                        var $li = $(this);
                        var title = $li.find('.att-enhanced-fname').text();
                        var url = $li.data('url');
                        var isImage = $li.data('image') == 1;
                        content += isImage ? '![' + title + '](' + url + ')\n' : '[' + title + '](' + url + ')\n';
                    }
                });
                if (content) {
                    var textarea = $('#text');
                    var pos = textarea.getSelection();
                    var newContent = textarea.val();
                    newContent = newContent.substring(0, pos.start) + content + newContent.substring(pos.end);
                    textarea.val(newContent);
                    textarea.focus();
                }
            });

            $('#select-all').on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                $('#file-list .att-enhanced-checkbox').prop('checked', true);
                return false;
            });
            $('#unselect-all').on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                $('#file-list .att-enhanced-checkbox').prop('checked', false);
                return false;
            });

            // 防止复选框冒泡
            $(document).on('click', '.att-enhanced-checkbox', function(e) {e.stopPropagation();});

            // 增强文件列表样式,但不破坏li原结构和官方按钮
            function enhanceFileList() {
                $('#file-list li').each(function() {
                    var $li = $(this);
                    if ($li.hasClass('att-enhanced')) return;
                    $li.addClass('att-enhanced');
                    // 只增强,不清空li
                    // 增加批量选择框
                    if ($li.find('.att-enhanced-checkbox').length === 0) {
                        $li.prepend('<input type="checkbox" class="att-enhanced-checkbox" />');
                    }
                    // 增加图片预览(如已有则不重复加)
                    if ($li.find('.att-enhanced-thumb').length === 0) {
                        var url = $li.data('url');
                        var isImage = $li.data('image') == 1;
                        var fileName = $li.find('.insert').text();
                        var $thumbContainer = $('<div class="att-enhanced-thumb"></div>');
                        if (isImage) {
                            var $img = $('<img src="' + url + '" alt="' + fileName + '" />');
                            $img.on('error', function() {
                                $(this).replaceWith('<div class="file-icon">🖼️</div>');
                            });
                            $thumbContainer.append($img);
                        } else {
                            $thumbContainer.append('<div class="file-icon">📄</div>');
                        }
                        // 插到插入按钮之前
                        $li.find('.insert').before($thumbContainer);
                    }

                });
            }

            // 插入按钮事件
            $(document).on('click', '.btn-insert', function(e) {
                e.preventDefault();
                e.stopPropagation();
                var $li = $(this).closest('li');
                var title = $li.find('.att-enhanced-fname').text();
                Typecho.insertFileToEditor(title, $li.data('url'), $li.data('image') == 1);
            });

            // 上传完成后增强新项
            var originalUploadComplete = Typecho.uploadComplete;
            Typecho.uploadComplete = function(attachment) {
                setTimeout(function() {
                    enhanceFileList();
                }, 200);
                if (typeof originalUploadComplete === 'function') {
                    originalUploadComplete(attachment);
                }
            };

            // 首次增强
            enhanceFileList();
        });
        </script>
        <?php
    }
}
?>

重磅,Tailscale的Derp服务器原生支持自签发证书

作者ysicing
2025年4月5日 21:32

重磅,Tailscale 的 Derp 服务器原生支持自签发证书

最近比较忙,鸽了很久 😄。如题的功能已经出了比较长的时间了。本篇文章简单讲讲有啥新的调整。

背景

在官方支持之前,大家普遍通过修改源码来支持的,也有不少大佬提供相关镜像。可能大家会有疑问,为啥会有这类需求?受限于部分网络环境下 LetsEncrypt 是被禁用的;还有一些场景,使用域名的方式需要 ICP 许可,跨云 IDC 会比较麻烦。

开源作者讨论 tailscale#issues/11776
相关 PR tailscale#pull/15208

新版本好处

  • • 不需要域名
  • • 不需要 ICP 许可/接入
  • • 不需要证书

新版本部署

编译或下载二进制

如果你有 Go 的环境,可以使用如下命令

go install tailscale.com/cmd/derper@latest

当然也可以使用我编译好的二进制文件

wget https://c.ysicing.net/oss/tiga/linux/amd64/derper

将相关二进制复制到 /usr/bin/derper, 并赋予执行权限

cp -a derper /usr/bin/derper
chmod +x /usr/bin/derper

手动签发 IP 证书

示例是你的 derper 公网 ip

export DERP_IP="1.1.1.1"
mkdir /etc/derper
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout /etc/derper/${DERP_IP}.key -out /etc/derper/${DERP_IP}.crt -subj "/CN=${DERP_IP}" -addext "subjectAltName=IP:${DERP_IP}"

手动启动 derper

derper --hostname="1.1.1.1"  -certmode manual -certdir /etc/derper

启动完成会提示如下信息

2025/04/05 20:00:25 Using self-signed certificate for IP address "1.1.1.1". Configure it in DERPMap using: (https://tailscale.com/s/custom-derp)
  {"Name":"custom","RegionID":900,"HostName":"1.1.1.1","CertName":"sha256-raw:970d3fae5ccd1480ad5e1017597c91f33d4bf0ef5bbeb5f54a2c20c0156ca081"}

然后可以打开浏览器访问这个网址看看,需要放行 804433478, 其中 3478 是 UDP 端口。

配置 ACL

方便显示,格式化如下:

{
    "RegionID": 910,
    "RegionCode": "yxvm",
    "RegionName": "yxvm",
    "Nodes": [
        {
            "Name": "yxvm",
            "RegionID": 910,
            "HostName": "46.x.x.x",
            "IPv4": "46.x.x.x",
            "CertName":"sha256-raw:8a81f6658a7e1830fbaf363b72427f975d1f033d1616bf603cec3defa7e52391",
            "InsecureForTests": true 
        }
    ]
}

保存之后,使用 tailscale netcheck 测试

* DERP latency:
        - yxvm: 118.8ms (yxvm)

总结

如果你之前就使用自签证书的话,更新一下二进制就行

257、清明

2025年4月4日 22:51

晚上出门散步,路边画坛的小蘑菇

清明时节,阳气渐长,宜早起散步,呼吸新鲜空气,舒展筋骨,调和气血。饮食宜清淡,多食新鲜蔬菜,少食油腻辛辣,以养肝护脾。
心境宜平和,避免情绪波动,保持乐观心态,以顺应春生之气。适时添减衣物,防寒保暖,以防春寒侵袭。多饮温水,促进新陈代谢,排毒养颜。适度运动,如太极、瑜伽,以增强体质,提升免疫力。清明养身,顺应自然,方能健康长寿,福寿安康。

今天放假,和S终于能安安心心休息一天了。

一直睡到十一点起床,发现爸还在家,便叫上S出门中午的菜,S做了一个川味的腰花,我做了一个孜然鸡腿肉,都挺成功了。

上午出门的时候下了一部短剧 《家里家外》,下午两口子便我在沙发上看完了,全四川话台词的小短剧,还挺过瘾的,演员演技也还算在线,因为是短剧,所以笑点、爽点都很足。

Rorical可爱主题

2025年4月4日 19:06
Rorical可爱单栏typecho主题

Rorical

“简洁、可爱、功能强大,适合个人博客的理想选择”
Rorical Theme 是一款专为 Typecho 博客系统设计的主题,由 Rorical 开发,版本号为 1.0。它以现代化的卡片式设计和丰富的交互功能为特色,旨在为用户提供美观且实用的博客体验。无论是个人记录、生活分享还是技术笔记,这个主题都能满足你的需求。

下载

依赖插件:https://github.com/SocialSisterYi/Typecho-Plugin-CommentShowIp

主要特点

  • 响应式设计:支持电脑和手机端,自动适配不同设备,提供一致的用户体验。
  • 丰富交互:支持 PowerMode 打字特效和鼠标点击动画。
  • 内置文章目录(TOC),便于长文导航。
  • AJAX 评论提交,提升交互流畅性。
  • 自定义选项:可配置站点 LOGO、头像、背景图片(电脑/手机独立设置)。
  • 支持自定义导航栏图标和样式(下拉式/平铺式可选)。
  • 文章管理:显示阅读次数、字数统计和评论数。
  • 支持自定义文章头图,优先于随机图片显示。
  • 评论系统:支持深层嵌套评论(最大999级),并提供密码保护文章功能。

我的嗜好--嗑瓜子

2025年4月4日 19:07
如要阅读全文,点击标题跳转。在相当多的时候,我是一个无趣之人,孤身一人在杭州打工生活,不上班的时候,大部分时光都在出租屋里度过。我想,也就是在这些时光里,我沾染上了嗑瓜子这个嗜好。从小到大,我不是一个吃零食特别多的人,幼儿至初中前阶段,家里特别贫困,从来不会有给到我自由分配的零花钱,对于零食一直都是渴望但不可及的状态,久而久之,我似乎变成了一个不爱吃零食的人,但与其说不爱吃零食,不如说是已经没有了吃零食的习惯。到后来家里条件慢慢转好,屋子里也常常会有一些零食水果之类的,我则从来不会主动想起去吃,水果甚至因此放坏掉,母亲每每见此场景,总会评判我:你就是跟别人不一样,一点也不爱吃水果零食,不洗好拿到你跟前,你从来不会主动想起来吃。这种时候,我的馋欲才会被勾起来,但因为母亲给我安排了不爱吃零食的人设,我则经常又把馋欲给压制下去。现在,参加了工作,手上的闲钱自然是足够满足自己吃零食的馋欲,孤身在外,也没有了任何外界的管束,我开始沾染上了嗑瓜子的习性。这几年,我的年龄正值壮年,身体也明显感觉到状态饱满异常,再没有小时候稍微风吹草动就感冒发烧的情况,但是这几年里却每年都会发作一两次上火,而这些上火,大部分都与吃瓜子有关。

记大宝参加幼儿园组织的清明烈士陵园扫墓活动

2025年4月4日 05:48
如要阅读全文,点击标题跳转。昨天李欣遇所在的幼儿园组织去了镇上的烈士陵园,晚上开视频的时候有了如下我和女儿的对话。我问她,你们今天去干什么了呀?她回答说去扫墓了。我又问她感觉怎么样?他说不好玩儿,还挺累的。因为幼儿园学校距离烈士陵园有挺远的一个距离,孩子们在老师的组织下步行走过去的。我又问她,你知道什么是烈士吗?她回答说里边儿是爷爷奶奶,叔叔阿姨。接着她又讲,人死了,有的去了天堂,有的去了地狱。我问她,什么人去了天堂?什么人去了地狱呢?她回答说,活着的时候做好事儿的人去了天堂,做坏事儿的人去了地狱。我嘿嘿直乐,嘴里说着你是不是看电视知道的这些?她说,不是,是我自己想的。我又问她,那你以后是想去天堂还是想去地狱呀?她说想去天堂。我问她为什么呀?她说天堂里可以一直玩儿。

2025年10+个免费商用字体和 WebFonts 字体 CDN 服务网站

作者斌仔
2025年4月3日 15:56

前言

设计和开发领域,免费商用字体和 WebFonts 字体 CDN 服务是提升项目视觉效果和加载效率的关键工具。免费商用字体避免了版权风险,而 WebFonts CDN 则通过全球分发加速字体加载。以下整理了当前主流的免费商用字体库及 WebFonts CDN 平台,涵盖中英文资源,帮助大家一站式解决需求。

免费商用字体和 WebFonts 字体 CDN 服务
免费商用字体和 WebFonts 字体 CDN 服务

免费商用字体库

Google Fonts

Google Fonts 全球最大的开源字体库,提供上千种免费商用字体,包括部分中文字体(如Noto系列)。支持直接嵌入代码或自托管,兼容性强,但国内访问需代理优化。

  • 特点:全球最知名的免费字体库,提供上千种开源字体,涵盖衬线体、无衬线体、手写体等,支持多语言(包括部分中文字体)。所有字体遵循开放字体许可证(OFL),可免费商用且无需额外授权。

Font Squirrel

Font Squirrel

  • 特点:专注于免费商用字体,所有资源均经过版权审核,提供TTF、OTF、WOFF等格式下载,适合网页设计和印刷项目。

猫啃网

猫啃网 国内权威中文免费商用字体平台,收录589款字体,授权信息透明,支持批量下载及在线预览,适合品牌设计和宣传物料制作。

  • 特点:国内领先的中文免费商用字体平台,收录589款字体,授权信息透明,支持在线预览和批量下载。

猫啃网
猫啃网

ZeoSeven Fonts(ZSFT)

ZeoSeven Fonts 提供764款免费商用字体,包含编程字体(如JetBrains Maple Mono)及多语言支持。其内置WebFonts CDN服务,支持HTML、CSS、JS等多种嵌入方式,开发者可快速部署字体并享受全球CDN加速,尤其适合技术博客和国际化项目。

  • 特点:提供764款免费商用字体,涵盖编程字体(如JetBrains Mono)、多语言字体等,支持多种嵌入方式,是开发者的优选。

自由字体

  • 网址自由字体
  • 特点:国内权威免费字体平台,整合全网可商用资源,规避版权风险,适合企业宣传和品牌设计。

DaFont

  • 网址DaFont
  • 特点:海量字体库,部分支持免费商用,需仔细阅读授权说明,适合个性化设计需求。

100font

100font 整合多语种免费商用字体(含中、英、日、韩),同时提供免费图片和音效资源,分类清晰,适合设计师一站式获取素材。

  • 特点:分类清晰,支持中、英、日、韩等多语种字体,同时提供免费商用图片和音效资源。

100font
100font

Google Noto Fonts

  • 网址Google Noto Fonts

  • 特点:谷歌推出的开源字体,覆盖全球语言字符,适合国际化项目。

Windfonts

Windfonts 作为国内首家开源免费中文WebFonts平台,Windfonts 提供中文字体切片服务及CDN分发,支持WordPress插件与API对接。其开源工具链优化了字体分包策略,结合自托管或第三方CDN,可显著提升中文字体加载速度,适用于企业官网及多语言应用。

中文网字计划

中文网字计划 基于Google Fonts的分包技术改进,中文网字计划通过优化字符区间和全球CDN分发,实现中文字体全量级按需加载。开发者可通过其开源工具(如@konghayao/cn-font-split)切割字体并托管至CDN,尤其适合动态内容网站。

FontCDN

FontCDNGithub开源地址预览地址) 是一个开源解决方案,允许用户自托管字体文件并通过CDN分发,支持WOFF2等现代格式压缩。其配置简单,可无缝集成React、Vue等框架,适合需要私有化部署的开发者。

Free Font

Free Font预览地址) 收录可商用的免费英文/汉字字体。收录了 927 个可商用免费字体。

Best Free Fonts

Best Free Fonts 最佳免费字体精选了 214 种免费字体。包括衬线字体、无衬线字体、手写字体和等宽字体。

WebFonts CDN 服务

Cloudflare CDN

Cloudflare CDN 通用CDN服务,支持托管自定义字体文件并开启HTTP/2加速。结合中文网字计划的分包策略,可显著提升中文字体加载效率,尤其适合高流量网站。

Adobe Fonts(原Typekit)

Adobe Fonts 需订阅Adobe Creative Cloud,提供专业字体库及CDN分发,适合设计团队在商业项目中快速调用高质量字体。

独立字体

MiSans

MiSans 是小米公司于2021年推出的开源免费商用字体,最初作为MIUI 13系统的默认字体,现已成为全球语言字体项目MiSans Global的核心成员。其设计以简约清晰、人文易读为原则,覆盖20多种书写系统、600+语言、10万+字符,支持简体中文、繁体中文(港台字形)、拉丁文、日文等多语言场景。

CDN链接示例

<link rel="stylesheet" href="https://font.sec.miui.com/font/css?family=MiSans:400,600:Chinese_Simplify,Latin" />  
<style>  
  :root {  
    --font: "MiSans", -apple-system, sans-serif;  
  }  
</style>  

mozilla-type-family

mozilla-type-family 由 Studio DRAMA 为 Mozilla 定制的字体系列。Mozilla 字体系列有两种风格--Mozilla Headline 和 Mozilla Text。

总结

免费商用字体和 WebFonts CDN 的结合,既能满足版权合规需求,又能提升用户体验。对于中文项目,推荐猫啃网自由字体;国际化和开发场景优先选择Google FontsZeoSeven Fonts。若需进一步优化加载速度,可结合CDN服务如ZeoSeven CDNCloudflare。使用时务必仔细阅读授权条款,避免法律风险。

开发者需根据项目类型(静态/动态、中英文比例)选择工具链,并优先采用 WOFF2 格式压缩及 HTTP/2 加速,以最大化用户体验。

二十九

2025年4月2日 19:37

四月二日又至。踏入校园,见园中梧桐新叶初绽,在春风中微微颤动。

幼时的生日总在农忙与上学的夹缝里度过。母亲会在鸡鸣前起身,在灶台前煮一碗卧了荷包蛋的长寿面,面汤上浮着金黄的油星。我趴在方桌上吃面时,父亲已扛着锄头走向田间,背影渐渐被晨雾吞没。那时不懂何为仪式,只道人人都如此过活。

大学时第一次郑重其事地过生日。用做家教攒下的钱请室友下馆子。夜里躺在铁架床上,指腹反复摩挲收银条上的数字,胃里翻腾着红烧肉的油腻与隐隐心痛。月光透过纱窗,在水泥地上划出一道惨白的伤口。

青春像是一本仓促翻过的书,还未及细读,便已翻到了末章。二十几岁的光景,原以为漫长,谁知竟如朝露,太阳一晒便无影无踪。那些曾经以为永远不会忘记的人和事,如今在记忆中已经模糊不清;而那些想要刻意遗忘的,却偏偏如影随形,挥之不去。

昨夜晚饭前,妻子忽然问到:"明天要什么礼物?"我望着她可爱又有些疲惫的身影。"该有的都有了。"话出口才惊觉,这竟是父亲当年常说的话。

街上的梧桐又抽出了新芽。记得去年此时,它们也是这样嫩绿地站在枝头。草木尚能岁岁重生,人却只能一路向前,绝无回头的可能。时光带走了青春,却也沉淀下些许智慧;剥夺了冲动,却赐予了从容。得失之间,孰轻孰重,恐怕连最精明的账房先生也算不清楚。

未来会怎样呢?我不敢妄加揣测。只愿岁月静好,父母安康;只愿在匆匆流逝的时光中,能守住内心的那一点澄明;只愿当白发苍苍之时,回首往事,不至于羞愧难当。

我们高估了智力的重要性

2025年4月1日 08:00

在 AI 越来越强大的这段时间里,我思考得比较多的一个问题是,我们是否高估了智力的重要性。

引起这个思考的原因有很多,一是大语言模型的进化程度让我感受到,语言模型在智能(推理、学习速度、知识广度等)方面已经远远超越人类,而这样的智能是每个人都能运用的。也就是说,人类完全可以把需要智能的行动交给 AI 处理,人类更多地是负责决策层面的工作。

二是因为我自己就是一个智力平平的人,小学当我学到除法的时候,我花了很长时间才理解除法是什么。我在开始学 JavaScript 的时候,也花了很长时间理解什么是 callback, 为什么函数能作为参数被传递和调用。

但我这个智力平平的人还是得到了算是不错的成果。我总是对别人说,我不是一个很聪明的人,我只是 13 岁就开始学编程,笨鸟先飞罢了,很多人大学学一年就能超过我的水平。所以我才更深刻地体会到,正常水平的技术,往往通过时间可以弥补,在这条水平线,不需要很高的智力。

不过,即使通过时间可以弥补智力的不足,但不是很多人能在这段时间里坚持下去。这也引出了我认为智力被高估的同时显现出来了另一个问题 —— 自我效能 (self-efficacy) 的重要性被远远低估了。

所谓的自我效能,是指相信自己有能力成功完成特定任务或应对特定情况的信心。很多复杂的因素决定了一个人自我效能的高低,这并非天生的。成长过程中长辈的态度、通过对他人的观察、个体与环境互动等等都影响一个人的自我效能水平。

在同样面对一个问题,自我效能低的人,看到的往往全是问题,最终放弃。而自我效能高的人,有强大的信念认为可以解决问题。前者也许在智力上比后者更高,但后者可以通过这种信念一直前进,超越前者。

图由 gpt-4o 生成

尤其是在 AI 时代,智力变成了一种更容易弥补的差距。我认为智力是边际效益递减的,除非超过了某个阈值。但这永远是很小一部分的人。像我这样智力平平的人是多数,我们只是站在了巨人的肩膀上。就像 Dijkstra 只有一个,这个世界也需要他这样的天才,但我们还是可以享受他给我们带来的成果。

这不是反智,而是我认为,智力水平有一个临界点,对于临界点以下的人,智力的重要性被高估了,因为智能越来越不稀缺,稀缺的是自我效能,是主动利用智能的人。

自我效能是完全可以通过后天训练的。自我效能是心理学家 Albert Bandura 提出的概念。他总结了影响自我效能的四种因素:

掌控经验 (Mastery Experiences)

个体通过亲身成功完成任务的经验来建立自信。反复的成功会增强自我效能,而失败(尤其是早期或没有应对策略时的失败)则会削弱它。

我觉得这里指的「成功」并非大成功,而是细微的成功。例如在我学习编程的早期,我通过写出各种各样的小程序获得这种成功感,对我建立技术自信有很大的帮助。

替代经验 (Vicarious Experiences / Modeling)

观察与自己相似的人成功完成任务,会提升观察者对自己也能做到的信念。看到别人能行,会觉得“我也许也可以”。

对我来说,小时候读的名人(科技精英)传记就是一种 Modeling, 尤其是李开复的《世界因你不同》,这些「洗脑」式的输入,会让我越来越希望自己能成为这样的人。

乔布斯有一句广为流传的话,他说

Life can be much broader once you discover one simple fact: Everything around you that you call life was made up by people that were no smarter than you.

一旦你认识到一个简单的事实——你周围那些你称之为“生活”的东西,都是由并不比你聪明的人创造出来的——你的人生就会变得更为广阔。

乔布斯从更极端的思路获得高度的自我效能感 —— 通过对比别人不行,觉得自己可以。

社会说服 (Social Persuasion):

受到他人的鼓励和积极评价可以增强自我效能感。而负面评价则会削弱它。

这个条件比较被动,这里不谈。

生理与情绪状态 (Physiological and Emotional States)

个体在面对任务时的生理反应和情绪状态会被解读为自身能力的信号。如果将紧张解读为“我不行”的证据,自我效能会降低;如果解读为“兴奋”或“迎接挑战”,则可能不会降低甚至会提升。

在这个方面,我面对舞台的经验可以充分论证。记得第一次面对众人做技术分享和第一次上台唱歌,我紧张得不行,表现都很糟糕。随着不断地强迫自己上台,我发现自信完全取决于自己的念头,我学会在上台前欺骗自己是一个有很多粉丝的人,台下的人对我非常崇拜,或很喜欢听我唱歌,这样的念头让我在台上的表现有很大很大的改进。

人是事件的反应器,通过刻意训练,是可以掌控自己的反应的。

以上是我对 AI 时代的到来的其中一点思考,希望能鼓励到和我一样智力平平的人。

❌