普通视图

发现新文章,点击刷新页面。
昨天以前只是玩玩

🤣JUSTFUN周刊:第八期

2024年7月14日 00:13
这期的周报又晚了一天,因为最近外甥和外甥女过来玩了,能安静写字的时间变少了。
抱歉,刚落笔时就发布,导致很多错别字,睡醒再来改🙃

一图

特大暴雨

窗外

猫、鱼

流水账

  • 外甥和外甥女过来玩了,这几天白天上班,下午下班后还要送他们去学游泳,晚上则再陪他们看电视、玩小游戏,算是提前体验带娃生活了。

最近几周和几个月,随着欧盟宣布计划对进口中国电动汽车加征高达 37.4% 的临时关税,这些碎片化趋势凸显出来。在此之前,美国于 5 月对中国电动汽车征收 100% 的关税。
在欧盟对中国制造的电动汽车加征关税前,中国对欧电动汽车出口在过去几年猛增。一项研究显示,中国电动车出口额从 2020 年的 16 亿美元跃升至去年的 115 亿美元,占欧盟电动汽车进口总额的 37%。

  • 昨天牙疼了一晚上,我也一晚上没睡。早上第一件事就是去牙科,目前上了药,终于不疼了,但是完全提不起吃饭的兴趣。
  • 用云闪付 + 企业微信日历提醒和管理信用卡还款,基本上很少出现逾期的情况了。
  • #Switch Switch 吃灰很久,而且之前重装系统格机,很多游戏都丢失了。这次因为外甥和外甥女过来了准备下点合家欢游戏晚上一起玩,目前准备的游戏有
    • 大富翁 10
    • 回旋镖大乱斗
    • 双人成行
    • 世界游戏大全 51
    • 马里奥派对
    • 伐木工
    • 胡闹搬家
    • 疯狂兔:传奇派对
    • 疯狂猴子球
  • #知识 部分浏览器已经开始不认可 Let's Encrypt 颁发的证书了,建议更换 Zerossl 的。
  • #观点 在历史的垃圾时间里做什么? – 土木坛子 身边不少有资本的企业即便是在市场低迷的时候依旧在做拓张,这是抢占市场的好机会,当然,这需要资金支持,换我们来估计要折戟沉沙了。
  • #观点 如果对某个观点感兴趣,想写点什么,应该进行深入思考,了解这个观点的一切信息,并结合自己的知识进行总结和整理。
  • #观点 中国人大多是含蓄的,我应该算是个中一员,并在传统教育下的和这些大多数我们一样,都秉承中庸平和这一观念活着,加上成长环境和家庭氛围,塑造出了隐忍含蓄的性格,在工作和家庭里总是体谅他人,这意味着会失去很大一部分自我。

发现

opensftp:漂亮的 SSH 管理工具

#工具

一位大佬自己开发的全平台 SSH 工具,界面漂亮美观,操作方便。

之前用过的类似工具有 fianlshell、WindTerm、Tabby,前两者和这个工具性质上比较类似,不过我现在追求打开迅速,即用即走,所以还是使用的 Tabby,不过对于喜欢用这类工具通过图形界面管理文件的朋友来说还是很棒的一款工具。

工具主页:OpenSFTP

各版本之前的差别:

在线字帖生成器

#工具

外甥过来后我姐给他报了一些培训班,其中就有练字的。但是我这外甥也是个马大哈,把老师才发没几天的练字本给搞丢了,白天看他无所事事,所以在网上找了这个工具给他重新打了几页作业。

工具特性很多,可以设置格子类型,还可以设置笔画顺序等等。

把需要生成字的字粘贴到内容区后,程序会自动按单子做分割的,一行一个字。

Nothing 子品牌产品

#数码 来自 dayu

前几天刷 X,看到 dayu 分享了一个 Nothing 子品牌手机 CFM Phone,模块化的设计,整体的产品语言还挺好看的,我个人挺喜欢的,官网售价 200 美元,当时心动的上咸鱼查了一下,国内到手估计要 2000 左右了,性价比不太高。

官网地址:CMF Phone 1 - CMF Global

之后又看到该品牌的另外一款手表产品,论外观设计来说真的是非常能打动我的,不过不是全功能系统,对标的是华为、小米的长续航定制系统产品,不过因为是国外的定制系统,肯定不会不像国内的品牌会对国产 APP、手机做优化,而且估计很多服务都要🪜才能使用,或者国内使用体验很差,想到这些瞬间就偃旗息鼓了。

产品地址:CMF Watch Pro 2 - CMF Global

不过之后如果价格降下来了会考虑入手一个二手玩玩。

CF 赛博菩萨新工具

#工具 来自 v2ex

在 V 站偶然刷到的,这是一款CF推出的网站扫描工具,可以查询站点信息、站点技术栈、证书信息等。

工具地址:URL Scanner

自建盘搜:aipan-netdisk-search

#工具 来自 x

这是一款可以简单自建的网盘搜索工具,基于 Vue、Nuxt.js 等技术实现,我暂时没有自己搭建,不过体验了一下作者的 Demo 站,效果很棒!

我之前用的是狸猫盘搜,这些个搜索网站虽然也都能达到目的,但是都有一个共同缺点:广告太多了,或者是需要注册、关注公众号等才能拿到链接地址,很是流氓。

有了这个工具可以脱离苦海了。

不过项目使用的是第三方的 API,对 ip 有访问限制,建议自己部署使用。

开源自部署 Figma:penpot

#工具

当代说到做设计自然离不开 Figma 这款工具,虽然我之前体验过,但是用的不是很明白,不过看过一些大佬通过这个工具做的产品设计,只能说是因为我太菜了。

而 PenPot 就是一款可自部署的类 Figma 工具,我注册体验了一下,在基础功能上应该和 Figma 差不太多,不过产品的生态肯定差很远了,具体区别可能需要搞设计的朋友来指出了。

官方网站:Penpot: The Design Tool for Design & Code Collaboration
项目地址:GitHub - penpot/penpot: Penpot: The open-source design tool for design and code collaboration

适用于 XP 和 WIN7 带 Chromium 浏览器:supermium

#工具

看到这个项目的时候我可太惊讶了,想不到居然还有人会为 XP 和 Win7 专门维护一个 Chromium 浏览器分支,这可能会解决很多用户的麻烦。

我其实很喜欢 Win7,漂亮的动效设计、模糊特效,让我至今都认为他是比Win10更好的设计语言,可惜微软跑偏了,不过好在 Win11 有一些改变,其中还能看到一些 Win7 的设计。

项目地址:GitHub - win32ss/supermium: Chromium fork for Windows XP/2003 and up

最近的 CDN 投毒事件

#资讯

之前在椒盐豆豉的博客看到有网友反应他的 adblock 插件会干掉椒盐豆豉的评论窗口,但是还留言说是不是他的网络问题,后来发现不少其他博客的网友也出现类似的问题,其中严重的还有自动弹出广告网站的。

再之后偶然在 V2ex 看到好几个帖子讨论了这个问题才大概知晓了其中缘由 :

大多数关于这次攻击的报道集中于一个星期之前,然而事件的开始却远早于这个时间。一年以前,V2EX 社区就有用户发文表示 BootCSS 的静态资源被投毒 [2]。 通过查阅记录可以发现,BootCSS.com 由王赛于 2012 年底批量注册,建站初期主要提供的是 BootStrap 介绍和交流 [3,4,5]。于此同时进行批量注册的还有 golaravel.com 等一系列技术栈的中文网,猜测是想使用站群方式来进行项目文档的本地化,同时积累受众用户。

来自被进化所抛弃产物的重击

2024年7月12日 20:38

不知道是不是因为昨晚上那一顿烧烤犯的罪,还是水了 晚风、夜色和腼腆的我 这篇文章的缘故,入夜后嘴里仅剩的两颗智齿中的其中一刻忽然开始不安分起来,像是陷入终局的拳击手般重拳出击,殴打了我整整一夜,疼的我死去活来。

我的右半脸几乎在整夜都持续保持这一种刺痛,这种疼痛来自牙根深处,刺痛频率在2-3秒之间,我甚至一度觉得就像摩斯电码那样,或者说就像紫薇被容嬷嬷强迫着做针灸一般,边扎还边靠在我背上问我「崽种,爽不爽?」,我只能像个小娘们一样哭着喊着「不要,不要,啊!容嬷嬷,我错了」。

一两点的时候疼的实在有点熬不住了,人都有些恍惚了,便起床去找药。

哪知道我家似乎没人有牙痛这个毛病,根本没有准备任何牙痛相关的药物,无奈之下只能拿了几粒花椒咬在病牙处,虽然有所环缓解,但依旧一晚上都没办法入睡。

其实我这两年来刷牙的习惯保持的挺好的,每天早晚都会刷牙,而且晚上刷牙后就不会再吃东西。而且除了电动牙刷外还给我这一口宝贝们配了冲澡器(冲牙器), 不过人类的进化后被抛弃的产物就是这么的不安分,总会在不经意间给你个惊喜。

其实发作的这颗智齿在之前就有发作的征兆,当时还专门腾了一上午去医院做检查,医生建议是直接拔除,而且一次只能拔一颗,不过当时因为需要照X光,辐射会影响备孕,而且当时只疼了一会便好了,想着问题应该不会很严重便搁置了,这次算是切身体会了「牙痛不是病,疼起来要人命」这句谚语。

今天一到公司便顶着一对黑圆圈去了旁边的牙科诊所,虽然这颗是个无用的智齿,但是目前这种状况只能先做根管治疗之后再临时先给他补上,避免后续再出现发炎的症状。

因为还在发炎,今天只能先把炎症处理一下,把里面积压的发炎物质排除,明天不疼了之后再去做填补处理。

在一切完事之后我一定要第一时间和这两颗「宝贝」说再见,不回头的那种...。

晚风、夜色和腼腆的我

2024年7月12日 00:37

这几周一直都在坚持更新「JUSTFUN周刊」,截止目前也有7期了,而且此间「节气」的图片也没有落下,但是也有段日子没有写正儿八经的博文了。

倒不是我没有东西想写,反而心中积酝着很多东西。

而是不太想将这些东西吐露出来,即便是在博客这个面向少数网友的「文字载体」里,因为这里毕竟还算是个公共区域,而我平常也算是以半实名状态活动在博客圈内,抒发这些敏感情绪总是有点不太好意思。

不过为什么又在这个点写下这篇酸溜溜的文章呢?

因为总有憋不住的时候把...。

想着那就擦着边,就着夜色和晚风写点不着边际的废话权当抒发了。

写完再删了

...

以下写了不少,写完后于我而言倒也算痛快。
为了不让大家看笑话,完笔时全删掉了。
大家看个乐呵。

🤣JUSTFUN周刊:第七期

2024年7月7日 11:58

一图

我的座驾

夕阳刚落下,华灯初上

骑行随拍

给旧楼穿衣服

流水账

  • #知识 不吃早点是非常容易得胆结石的,原因是不按时吃饭,胆没有规律性工作,胆汁停留时间过长。
  • #知识 乌龙球一词源于英语的“Own goal”,直译成中文为“自进本方球门的球”。由于粤语“乌龙”有“搞错、乌里巴涂”之意,所以香港记者便在报道中以“乌龙”或“乌龙球”来翻译“own goal”。中国大陆记者也借用了香港的这种译法。而中国成语的“自摆乌龙”、“乌龙大礼”也成为乌龙球的另一种戏称。
  • #资讯 在TG上看到 OpenSSH出现高危远程代码执行漏洞 一文,遂用 ssh -v 检查了一下自己的SSH版本,似乎在受影响范围,赶紧升级一下,整理了一下升级流程 升级SSH版本到openssh-9.8p1
  • #流水 今天有人帮朋友在博客群里征婚,是位大概30岁左右的男士,深圳户籍。看他的征婚简历,个人条件其实还算不错的。
  • #流水 浏览器又从 Chromium 换回了 百分浏览器 ,换了后Instagram死活打不开,不知道是我梯子的问题还是换浏览器导致的。
  • #技巧 企业邮箱其实可以设置客户端专用密码让第三方客户端登录且是长时间有效,避免密码只有3个月有效期的问题,功能路径为 设置 》 邮箱绑定 》 客户端专用密码
  • #技巧 以前使用 Trilium 的时候日志都是按 年/月/日 以文件夹形式进行存放的,后转入Obsidian用了两年多,一直都是直接存放在Diary文件夹下,虽然不影响使用,但是所有日志都在同一个文件夹下始终觉得不太习惯,今天在查了一下资料整理了一篇知识库文章 Obsidian新建日记使用年月文件夹格式储存
  • #资讯 根据Follow官推发文,Mac版马上要发布了,乐观估计有可能这个月就能用上了。不过根据我找一些资料,该工具大概率可能是基于云端的收费服务。

发现

读Vol.83 社交媒体上的两种价值取向

#观点

在读完 Vol.83 社交媒体上的两种价值取向 后关于 Leslie 提到到的笔记软件选择的问题有了些想法。

我也曾经在不同的笔记软件中反复横跳,体验过的软件有:Trilium、Notion、Obsidian、appFlower、FlowUs、Affine等等,也在持续去发现新的笔记软件,如Docmost、Octarine等。

但是静下心来思考,用什么软件真的不重要,大多数人都是图个新鲜,真的在认真输出的人有几个?

当然,因为每个人的做笔记方法、习惯不同,笔记软件对每个人的体验都是有好有坏的,不过一款好用的笔记软件能大幅度提高做笔记的效率,而且一些独特的功能搭配用户的特殊方法可以迸发出新的火花。

但是笔记软件的内核始终是笔记。

自驱性的、持续性的去做笔记才是我们应该做的,在这个大前提之下去选择自己喜欢的软件也没什么问题,而 Leslie 提到的那几个点是选择笔记软件很好的出发点。

Bitwarden上保管的密码安全吗?

#知识

我目前是本地记录了一份,在Bitwarden上记录了一份,我始终认为用户量越多的服务越容易被黑客盯上,因为这种产出比太高了,比如WordPress。

不知道Bitwarden的自部署服务有没有出现过被黑的问题?V2ex上这个 bitwarden 备份的数据能直接解析出密码吗? - V2EX 帖子讨论过这个问题。

结论:只要主密码不泄露基本上没办法破解。

V2EX Polish:V2ex增强插件

#Chrome插件

发现一个更好用的V2ex增强插件,V2EX Polish ,在这之前我都是是用油猴脚本加一些Stylus自定义样式实现的,用了这个插件后才后知后觉的发现我以前那套搭配的简陋...。

插件地址:V2EX Polish
项目地址:GitHub - coolpace/V2EX_Polish: 专业的 V2EX 浏览器扩展,集合了众多实用功能,重塑你的浏览体验!

![[99-Rsources/Assets/Pasted image 20240707104758.png]]

htmlx:一个只有14k且很有意思的javascript库

#Github GitHub - bigskysoftware/htmx: </> htmx - high power tools for HTML 一个只有14k且很有意思的javascript库,可以通过拓展HTML中的属性实现直接在html元素上完成一些需要js、ajax请求才能做到的事情。

通过下方的示例,可以很简单的无其他代码的情况下以 ajax 的方式发起一个post请求。

  <script src="https://unpkg.com/htmx.org@2.0.0"></script>
  <!-- have a button POST a click via AJAX -->
  <button hx-post="/clicked" hx-swap="outerHTML">
    Click Me
  </button>

当然,他能做的事情不止这些,感兴趣的可以看看项目库。(现在的前端真是越玩越花了)

项目地址:GitHub - bigskysoftware/htmx: </> htmx - high power tools for HTML
文档地址:</> htmx - high power tools for html

用小游戏的形式学前端

#知识 来自 bilibili

以前关注了渡一教育这个UP主,他经常会发一些有意思的前端知识,偶尔刷到都能对以往学的一些知识有更深的了解。最近刷到他一个视频,整理了一些可以通过小游戏方式去学习前端知识的一些网站。

猫星人

#云撸猫 来自 bilibili

自己没精力照顾小动物,所以只能云一云了,而在B站看可爱的小动物是我日常云的方式之一,分享一个这周看到最萌的。

咸鱼PC端即将上线

#资讯

以前咸鱼是有浏览器版本的,可以在PC上方便的淘东西,不过不知道从什么时候开始他们忽然砍掉了PC端,其中的缘由我没有深究,以前在浏览器版本的时候还可以通过脚本进行价格排序等等操作,现在没有了之后是真的不方便了。

不过前些看到淘江湖上说PC端可能要重新上线了,期待中。

原文地址:虽迟但到,你需要的图片搜索和闲鱼也回来了 - 淘江湖

![[99-Rsources/Assets/Pasted image 20240707110337.png]]

赛博旅游:Google Map 扫街

#分享

在b站上偶然刷到一个视屏 玩电子扫街,享赛博人生 ,UP主展示了他如何通过Google Map完成扫街,在这个炎炎夏日对于那些不想出门,又爱好摄影的人来说可谓是打开了新世界的大门。

不过看着UP操作似乎很简单,但是实际确是非常需要经验的,我在日本走了半个小时,没发现什么适合拍照的点。

看来赛博扫街需要打开思维,脱离平常的拍摄手法了,而且因为手机拍摄屏幕的关系,拍摄的照片会出现摩尔纹,UP主另外一个教程视频倒是有提到如何处理摩尔纹等问题,不过是订阅会员才能看,可惜。

OpenSSH高危漏洞和升级教程

2024年7月2日 12:32

在TG上看到 OpenSSH出现高危远程代码执行漏洞-影响超过1400万台暴露在公网上的服务器 一文,遂用 ssh -v 检查了一下自己的SSH版本,似乎在受影响范围,遂打算赶紧升级一下。

已知受影响的版本有:

  • 低于 4.4p1 版 (不含此版本):受影响
  • 高于 4.4p1 但低于 8.5p1 (不含此版本):不受影响
  • 8.5p1 及后续版本到 9.8p1 (不含此版本):受影响

问了一下GPT,给的方案是 sudo apt update ,但是不管用,后找了一篇教程 ssh爆漏洞了,手动更新OpenSSH教程,整理了一下相关资讯,教程和注意事项如下。

💡
提示
升级后你的 sshd_config 设置会被覆盖,再重启服务器前请重新设置配置文件。
Ubuntu 的软件源更新了 https://ubuntu.com/security/notices/USN-6859-1
Debian 11 12 已经修复了 https://security-tracker.debian.org/tracker/CVE-2024-6387

尽量使用包管理更新openssh
make install安装会导致后续无法使用包管理更新
我按手动方式操作完后出现了问题,后重新使用 apt-get 安装解决
sudo apt-get purge openssh-server
sudo apt-get install openssh-server

自动升级

如果你是Debian12的话可以使用apt包管理工具一键升级

  1. 更新包列表:sudo apt update
  2. 升级 OpenSSH: sudo apt upgrade openssh-server
  3. 检查版本: ssh -V
  4. 重启服务: sudo systemctl restart ssh

手动编译安装

查看版本

ssh -v
sudo apt update
sudo apt upgrade

更新安装必须的包

sudo apt-get  -y update
sudo apt-get install build-essential zlib1g-dev libssl-dev

下载最新的包:

wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gz

解压:

tar -xzf openssh-9.8p1.tar.gz
cd openssh-9.8p1

编译:

./configure
make

安装

sudo make install

重启服务&查看版本

sudo systemctl restart ssh
ssh -V

如果版本没显示openssh-9.8p1,就是需要添加环境变量:

echo 'export PATH=/usr/local/bin:/usr/local/sbin:$PATH' >> ~/.bashrc

验证生效

source ~/.bashrc

查看版本(出现openssh-9.8p1就OK)

ssh -V

修改 ssh.service 文件中的 ExecStartPre、ExecStart 和 ExecReload 行,
将 /usr/sbin/sshd 改为 /usr/local/sbin/sshd

sudo sed -i 's|ExecStartPre=/usr/sbin/sshd|ExecStartPre=/usr/local/sbin/sshd|; s|ExecStart=/usr/sbin/sshd|ExecStart=/usr/local/sbin/sshd|; s|ExecReload=/usr/sbin/sshd|ExecReload=/usr/local/sbin/sshd|' /lib/systemd/system/ssh.service

确认修改内容

grep -E 'ExecStartPre|ExecStart|ExecReload' /lib/systemd/system/ssh.service

注意:以上操作ssh的配置文件就变成了 /usr/local/etc/sshd_config,需要更新配置文件内容,把新配置文件删掉,创建链接到 /etc/ssh/sshd_config ,并加载服务

sudo rm /usr/local/etc/sshd_config  
sudo ln -s /etc/ssh/sshd_config /usr/local/etc/sshd_config  
sudo systemctl daemon-reload  
sudo systemctl restart sshd

列出正在运行的SSH进程(确认是否是 /usr/local/sbin/sshd)

ps -ef | grep sshd

🤣JUSTFUN周刊:第六期

2024年6月30日 14:40

一图

骑行随拍

捉蛞蝓

周末逛彩灯公园,发现了儿时记忆里的游乐园

路边芒果摊
小区夕阳一景

流水账

  • 这种没有建设性的评论我已经习惯选择性不看了。 毕竟说废话,发表幸灾乐祸情绪并没有成本,而且这种人在地球很常见。
  • 💡Ghost 5.86.2 更新了新功能,现在可以在文章内通过输入 @ 符号快速插入站内链接,在撰写文章时通过 @ 符号唤起查询窗口,如果在行内的话是插入链接 🤣JUSTFUN周刊:第五期 。如果不在行内,如另外起一行则是插入bookmark
  • 执业药师考试可以开始报名了,总共4科,之前每年都报名,但是都没有认真备考,今年怎么也要过个两科把!加油加油!
  • 发现Ghost的API中有一个可以获取文章修改历史的接口,也许可以用他在文章底部加上一个修改记录?
  • 在x上看到dayu分享了一杯凉虾图片,但是发现他们那儿叫槐花粉,经查资料还是有略微不同的,虽然都是大米粉制作,但是广西的这个东西里面加了槐花研磨而成的粉末
  • 发现Octarine的OG图挺好看的,也许可以试着抄抄。

发现

Git命令速查图:稻米鼠整理的Git命令

#Git 来自 稻米鼠

我其实觉得Git的命令不太好学,因为普通用户没有系统性的使用过Git参与项目的整个开发过程,所以自然有很多不常用的命令无法用到,像我目前熟悉的就只有pull、commit、push等,其他的如切换分支、清除提交等都需要临时通过Google或者询问AI查找,总的来说还是不太方便。

不过最近发现 TG 上 Obsidian群内的 稻米鼠 大佬整理了一份 Git 命令速查图,非常有用。

OPML在线编辑器

#工具 来自 Github

大多数RSS工具都可以导出OPML格式的订阅文件,如果你需要快速对OPML进行修改、合并,这个工具也许可以帮到你,目前可以有以下几种特性:

  • 快捷新增、删除订阅:点击 Outiner view 切换编辑视图,可以通过UI管理所有订阅。
  • 合并多个文件:上传文件后可以点击 add file 添加多个 OPML文件
  • 去重:点击 Remove Rduplicate 可以一键去重

项目地址:GitHub - imdj/opml-editor: Online OPML editor tailored for managing subscription lists (RSS & Atom feed lists)
在线体验:OPML Online Editor

Notion发布网站

#新闻 来自 odin

知识库软件 Notion 推出网站发布功能,可以直接绑定自己的域名构建公开网站,即在 Notion 里编辑页面然后可以直接将其作为网站发布。

这个功能极大地降低了网站构建门槛,不过也让一些提供类似功能的 SaaS 开发者痛苦了,现在这些 SaaS 服务的需求量可能会消失。

不过我看了一下官方的模板还是更偏向以前的notion页面分享出来的结果,和传统意义上的网站还是有很大的差别。

[!NOTE] Title
发布免费,不过自定义域名要 10$ 一个月。

官方更新日志:The easiest way to publish a website

Obsidian-Daily-Notes-Editor:让Obsidian日记像Logseq那样

#Obsidian插件 来自 memos top

我以前使用过很长一段时间Logseq,他的日记页面可以在一个页面内以滚轴形式展示所有日记,在翻阅时会比较直观、方便。

而Obsidian-Daily-Notes-Editor就是可以让Obsidian实现类似效果的一款插件。

项目地址:GitHub - Quorafind/Obsidian-Daily-Notes-Editor: A plugin for you to edit a bunch of daily notes in one page(inline), which works similar to Roam Research's default daily note view.

弓鱼术

#知识 #视频

偶然间在B站刷到这么一个视频,其中提到了一种我第一次听闻的技术: 弓鱼术 ,这是福建地区的一种对鱼的特殊处理方法,通过将鱼弯曲,可以让鱼在陆地无水的环境至多存活1-2天,并且2009年“弓鱼术”已经被列入福建省第三批非物质文化遗产名录 。

弓鱼的存活率比较长是因为弓鱼因绑扎而强迫开口,而鱼鳃也一样强行张开,因此吸氧多。紧绑也使鱼身减少活动,防止伤害,不会消耗体能。没有弓鱼处理的鱼已经无力张开口和鳃,在离水的环境下,很快因缺氧机能衰退而死。但是弓鱼这种技术,只能适用于空气湿度高,温度适中的地区,干旱、温度极端的地区则弓鱼便不能生效。而且并不是所有鱼类都适合弓鱼,鳝鱼、泥鳅等长形而柔软的鱼类,也不适合。

和父母、长辈更好的沟通方式

#分享 #视频

在父母年纪日益增长之后他们也面临着需要我们的照顾、关心,我们作为子女们时刻担心着他们的身体、担心他们上当受骗、担心他们的生活习惯,但是这些长辈们缺越老越顽固,沟通起来自然会有很多问题,老顽童这个说法真是恰到好处的形容。

不过看了这个视频后我对和他们的相处似乎又多了一些可行的选项。

miniflux-injector:让你的订阅出现在google等搜索引擎结果内

#工具

偶然间在miniflux的官网上发现了这款插件,它可以在进行google等搜索时同时搜索你的flux订阅,并展示在google等搜索页面内,这会让你和你的订阅产生一些奇妙的关联。

比如前些天下完雨后我和我妈在捉蛞蝓,当时不知道这东西的学名,只知道我们的传统叫法是 鼻涕虫 ,后来查了一些资料后才知道叫 蛞蝓 ,然后查询过程中偶然在 Flux 的搜索结果中发现 李大毛 老师曾经也被这玩意儿困扰过 蛞蝓战争

当真妙不可言。

猛踩油门 VS 慢慢开车,哪种驾驶习惯更伤车?

#知识 来自 知乎-王大富

作者是一位资深的发动机工程师,从原理上详细的分析了标题所述的问题,以前只知道梦踩油门伤车,急刹车伤车等等,越来很多常识其实并不一定对,其中的弯弯绕绕在看完之后有种霍然开悟的感觉,对于车辆的保养和使用的理解又上了一层楼。

因为知乎现必须登录才能看原文,不过通过订阅访问没有这个限制,所以这里分享的是我的flux分享页面。

文章地址: 猛踩油门 VS 慢慢开车,哪种驾驶习惯更伤车? - Miniflux

Loop:一个很酷的Mac分屏工具

#工具

虽然我不用Mac,但是经常能刷到有网友分享这类工具,想来在Mac上做窗口分屏应该是没有Windows方便的。

偶然间看到这款工具,从作者发布的视频来看UI、操作逻辑都设计的挺好的,也许能让部分用Mac的朋友喜欢,遂分享一下。

项目地址:GitHub - MrKai77/Loop: Window management made elegant.
下载地址:Releases · MrKai77/Loop

99-Rsources/Assets/Pasted image 20240630141438.png

🤣JUSTFUN周刊:第五期

2024年6月23日 04:01

一图

夜骑

1. 路灯 2. 下班回家的工人夫妻,妻子载着丈夫

晴转多云

流水账

  • 💡 给博客新增了 莫比乌斯 的主题配色,可以在左侧配色部分点击启用,希望大家喜欢。如果有看到什么样式BUG希望能及时告诉我。
  • 这周真累呀,还好气温怡人,心情不至于又跌落谷底。
  • fly.io 也要开始收费了,有些免费的午餐的确不长久。
  • 在B站刷到了上次遇难兄弟的相关视频,总算了解到事故原因了:货车刹车失灵撞到他了,路过的骑友说没看到他有外伤,哎。

发现

cobalt:简单好用的流媒体下载器

#工具 来自

cobalt is a media downloader that doesn't piss you off. it's fast, friendly, and doesn't have any bullshit that modern web is filled with: no ads, trackers, or invasive analytics.

Cobalt 是一个不会惹你生气的媒体下载器。 它快速、友好,而且没有任何现代网络充斥着的废话:没有广告、跟踪器或侵入性分析

试用了一下,基本上稍微大一些的流媒体网站都支持下载,而且可以单独下载音频文件,一些做营销号的小伙伴可能用的上。

这里 可以查看支持那些网站,

项目地址:GitHub - imputnet/cobalt: save what you love
在线体验:cobalt

nullboard:一个纯html的看板工具

#工具 来自 奇趣周刊 - 第 55 期 - 子舒的博客

Nullboard is a minimalist take on a kanban board / a task list manager, designed to be compact, readable and quick in use.

Nullboard 是看板/任务列表管理器的极简主义版本,设计紧凑、可读且使用快速。

体验了一下,非常棒的一款看板工具!虽然是纯HTML的,但是作者实现完整的导出导入功能,并且开发了一些配套的桌面端和服务器端程序,可以十分 方便将程序数据备份到电脑或者云端,对于需要多地点办公、有看板需求的朋友来说应该是非常方便的。

不过目前没有预设的中文字体,想要用的舒服的朋友可以用Stylus或者油猴处理。

项目地址:GitHub - apankrat/nullboard: Nullboard is a minimalist kanban board, focused on compactness and readability.
在线体验:nullboard.io/preview

VSCode插件推荐

#工具 来自 我最爱的 VSCode 扩展插件推荐_哔哩哔哩_bilibili

偶然间刷到的一个视频,该视频推荐了一些有意思的插件

  • 0:00 Introduction
  • 0:17 Console Ninja :可以在编码时显示变量结果
  • 2:58 Best VS Code Themes :多和一主题插件
  • 3:36 VsCode Pets
  • 4:25 VsCode Random Data Generator :随机生成数据
  • 5:18 Indent Colorizer
  • 5:53 VsCode Snippet Creator
  • 6:31 VsCode Image Previewer
  • 7:19 Toggle Quotes :快速切换引号类型
  • 8:06 Outro

其中 Console Ninja 有点意思,不过不知道为什么我的11ty项目并不支持。其次是 Toggle Quotes ,这个插件可以通过快捷键快速切换引号类型,相当好用。

FPSMaster-v3:一个开源的Minecraft PvP 客户端

#Minecraft 来自 三年开发,我们最终选择开源!_我的世界

FPSMaster 是一个免费、强大的 Minecraft PvP 客户端。

虽然我不怎么玩 Minecraft ,但是以前着迷过一些同类型的游戏,如:传送门骑士、Cube World等,这两款游戏都有职业系统,也可以联机,后者有点类似方块版的暗黑破坏神,可以刷刷刷,十分有趣。现在国内的MC玩家基数应该挺大的,尤其是一些小学生,我经常看见一些小学生围在一起建房子。

项目地址:GitHub - FPSMasterTeam/FPSMaster-v3: FPSMaster Client Open Source

你的名字猫鼠版

#视频 来自 《你的死期》_哔哩哔哩_bilibili

挺好玩的一个视频,还真有当时 你的名字 时的感觉。

你的情绪指南

#知识 来自 Memos

在木木大佬的Memos中看到的一张图片,可以和上次分享的 各种激素高低的影响 一图搭配使用。

Minipresso NS2:便携咖啡机

#好物 来自 商品详情

在B站刷到的一个便携咖啡机的视频,这是一款面向户外露营用户的手动按压式的咖啡机,大概有8bar的压力。

有胶囊版和咖啡粉的版本,颜值、便携性的确挺不错的,价格也还算能接受。

不过个人感觉咖啡粉版本不太实用,弄一次就要清理一下,在户外来说太麻烦,既然是户外,自然要追求极致的方便。

正在考虑是不是要剁手。

🤣JUSTFUN周刊:第四期

2024年6月16日 14:37

一图

1. 下午的阳光从通风口射进仓库 2. 龙虾、烤肉这两样已经很少去外面吃了

流水账

#流水 dockerhub最近好像又被制裁了,不过我记得似乎一直都不能访问的把?实在不行就用镜像服务,有手有脚饿不死。
#流水 今天发现可以查成都马拉松的报名结果了,不出意外的没有中签,只能看看能不能候补上了。

发现

Coolify:Netlify等平台的自部署替代工具

#工具

Coolify是一个开源的Heroku、Netlify、Vercel替代自部署方案,他可以在你的服务器上完整实现上述平台的大部分功能,官方推荐的VPS配置是2c2g30g,另外官方也提供托管服务,差不多4刀一个月。

一键安装脚本: curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

我之前也想过部署一个CI工具,不过最后还是放弃了,一是这类工具对系统占用都挺高的,我小鸡负担不起,二是其实我的用量是比较少的,性价比不高,所以最后还是用了Github Action。

项目地址:GitHub - coollabsio/coolify: An open-source & self-hostable Heroku / Netlify / Vercel alternative.
官网地址:Coolify

hammal:CFWorker代理docker hub

#工具 来自 蜗牛

利用 Cloudflare Workers 自建 Docker 镜像

最近dockerhub无法访问的问题激起了很大的讨论热度,不过我记得似乎好像一直都无法正常访问,我都是通过镜像服务来下载的,上期推荐的chrs好像也能做到一键更新镜像源。

不过如果不想使用第三方镜像服务,也可以自己弄一个,这篇文章就是介绍了如何利用Cloudflare的服务搭建一个自己的镜像代理,不过感觉这样玩Cloudflare早晚会被彻底封掉呀...。

我已经安装操作搭建成功,不过直接访问部署好的worker会提示404,需要在命令行中使用你部署的域名+docker image名字测试。

文章地址: 利用 Cloudflare Workers 自建 Docker 镜像 - 知识库 - Bryan 的知识库
项目地址: GitHub - ImSingee/hammal: docker-registry proxy run in cloudflare workers

1000小时:用你的注意力填满 1000 小时就能练成任何你所需要的技能……

#知识 来自 randy

原本只需要一句话就可以彻底说明白:
用你的注意力填满 1000 小时就能练成任何你所需要的技能……

大意和「反复练习」一书的思想应该是一致的,但是在细节处略有一些不同,不过真要去执行这么一个长时间的任务的确是对人性的一项巨大考验。

在线阅读:简要说明 | 1000 小时

通用头像服务:unavatar

#工具

Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.
从社交网络获取统一的用户头像,包括Instagram,SoundCloud,Telegram,Twitter,YouTube等。

我一直在使用Gravatar服务作为我的头像服务,不过这项服务在国内已经无法访问了,但是好在有许多大佬都提供了国内的镜像服务,但是这些服务良莠不齐,而Unavatar这个项目就可以让你自己搭建一个类似的服务,而且他不单单只能获取Gravatar头像,还可以获取你的 Ins、Youtube、X、Github头像等等。

项目地址:GitHub - microlinkhq/unavatar: Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.

使用方式:

AwesomeCloudflare:极致的薅CF羊毛

#工具

⛅️ 精选的 Cloudflare 工具、开源项目、指南、博客和其他资源列表。/ ⛅️ A curated list of Cloudflare tools, open source projects, guides, blogs and other resources.

Cloudflare被许多开发者们称为赛博活菩萨,原因是该公司提供的一些各类开发者们需要用到的服务,而且大多数都是免费,且免费额度远超同类服务商,我在前几期周刊中分享的 faviconSink 和本期提到的 hammal 都是基于他们提供的免费服务实现。

而这个项目则汇总了众多基于Cloudflare开发的工具项目。

项目地址:GitHub - zhuima/awesome-cloudflare: ⛅️ 精选的 Cloudflare 工具、开源项目、指南、博客和其他资源列表。/ ⛅️ A curated list of Cloudflare tools, open source projects, guides, blogs and other resources.

TalkWithGemini:通过悬浮窗使用Gemini

#AI

Deploy your private Gemini application for free with one click, supporting Gemini 1.5 Pro, Gemini 1.5 Flash, Gemini Pro and Gemini Pro Vision models. 一键免费部署您的私人 Gemini 应用, 支持 Gemini 1.5 Pro、Gemini 1.5 Flash、Gemini Pro 和 Gemini Pro Vision 模型。

该工具是一个跨平台的Gemini工具,安装好后,打开程序,将窗口调整到手机画面大小后通过点击任务栏图标即可切换隐藏、显示状态,而且还支持通过语音进行对话。

项目地址: GitHub - Amery2010/TalkWithGemini

uiverse.io:使用的前端样式站

#工具

之前分享过一个 navnav 前端效果站,而unverse.io同样拥有丰富的样式、动画效果。

网站地址: Explore 3000+ Free UI Elements: CSS & Tailwind

obsidian-border:Obsidian主题

#Obsidian

A clean and highly customisable theme for obsidian.

特色:

  • 自动隐藏侧边栏、Tab、状态栏等
  • 卡片样式布局
  • 悬浮行提示
  • 极高的自定义性
  • 拓展了Checkbox样式
  • 拓展了Callout样式
  • 拓展了代码段样式
  • 对平板、手机支持良好
  • 自定义新标签

Border是一款简单、干净、高定制性的Obsidian主题,搭配  style settings 插件可以丰富的自定义效果。

而且可以实现卡片布局的效果,可以让整个页面看起来分布得体,之前这个效果我参考了 咬猪娃 的代码段实现,用了几个月了,十分满意,现在有主题可以直接实现这个效果,所以果断切换过来了。

该主题目前已经收录进Obsidian的主题商店,直接搜Border即可下载安装,同时如果要使用自定义功能必须安装 Style Settings 插件。

目前该主题配置项已经有了完整的汉化,设置不苦手。

主题地址: GitHub - Akifyss/obsidian-border: A theme for obsidian.md

🤣JUSTFUN周刊:第三期

2024年6月10日 10:17

恰逢节假日,昨天回家已经很晚,所以这期的更新晚了一天。

一图

哎,昨天我们这里也有人跳桥了,人没救起来

流水账

#观点 以前我有个朋友跟我说过一句特牛逼的话:爱情就像两个拉着同一根橡皮筋,只有先松手的人才不会受伤。

#观点 今天一大早就收到一个不好的消息。之前一位经常一起骑行的骑友在骑行318途中,刚出昌都不远出了车祸过世了,据闻是被大货车碾压了。他是5月中旬出发重骑318,我还时不时给他留言询问进度,想不到世事无常,人已离世,现在心子很不舒服...。

#观点 这世界没有什么东西是一成不变的,也正是因为这样,这世界才多姿多彩。心有郁结的人该走出去,不该停留在不好的回忆里,不好的思绪里,该看看这世界的美好,这是活着的意义,而他们走偏了。

#观点 最近高考,和大家聊了聊关于是否一定要读一个好大学一事。我的个人观点是:灵活的思维模式比读书更重要。很多以前读书时期的混子能挣着钱,而一些读死书的高材生最后可能连工作都不好找,这就是比较好的例子。另外就是执行力,有了思维如何实践出真知就是另外一个重要环节了。最后就是运气了,不过称之为运气,我总觉得有点不妥,因为都是别人在做出某些选择之后成功了才被称为运气。总的来说读书是一个非常好开拓思维的方式之一,但不是唯一的选择。

工具

gifski:非常屌的GIF生成工具

#工具 来自 OdinSay

基于 pngquant 的高质量 GIF 编码器。
gifski 使用 pngquant 的奇特功能将视频帧转换为 GIF 动画,以实现高效的跨帧调色板和时间抖动。它生成每帧使用数千种颜色的动画 GIF。

看了一下官方生成后的效果图,太厉害了!

我之前也有用过一些其他工具生成GIF动图,如utool的截图插件、ShareX和现在用的 PixPin,但是这些工具生成的GIF图要不帧率太低,要不就是体积过大,亦或者是颜色不行,总不能完美,相信这个库以后会很完美的解决这个问题。

项目地址:GitHub - ImageOptim/gifski: GIF encoder based on libimagequant (pngquant). Squeezes maximum possible quality from the awful GIF format.

阿里云618活动

#VPS 来自 蓝点网

上一期推荐了腾讯云的服务器99元续费,今天发现阿里云也出了类似的活动,我猜测应该这几家国内头部云服务厂家正在打价格战。

而且根据 网友小宋 反映,腾讯云又推出了618的年中大惠活动,也有99元续费活动,有想升级、续费VPS的朋友这段时间可以关注一下了,刚刚去看了一下,我好像又可以参加了,不过目前VPS有3年时效了,暂时不打算再续。感觉以后VPS的价格应该会越来越便宜...。

阿里云活动地址:618创新加速季_新迁入云享5亿算力补贴-阿里云
腾讯云活动地址:腾讯云618年中盛惠来啦!

favicon:搭建你自己的网站icon获取服务

#github #工具

以前实现过一个 文章内链接自动增加网站 icon 的功能,就是将需要获取icon的网站传给一个API,该API会将icon地址返回给你,此时再根据需求进行使用即可,不过这类API经常失效,有一些稳定的却会出现被墙的问题,比如我上面这篇文章内提到的API网站就已经失效。

Favicon这个项目是利用cfwork搭建的服务,fork项目后修改 wrangler.toml 配置即可部署出一个你自己的icon服务,至少在你使用期间他是绝对不会过期的。

项目地址:GitHub - splitbee/favicon-resolver: Get the favicon from an URL utilizing cloudflare workers

关于如何在公网开端口

#知识 来自 V2ex

在V站看到一篇关于如何在公网开端口的讨论帖,文中有个做法很有意思,也就是访问前先通过一些api服务把ip加进白名单,这样当前的机器就有临时访问权限了。

第三层是 IP 白名单,要求你的防火墙或者路由器支持 API 或者 ssh/telnet 等交互方式,这样便于实现脚本自动修改白名单。我的思路是先通过 ip 接口(例如 https://myip.ipip.nethttps://ddns.oray.com/checkiphttps://ip.3322.net ,也可以自己在云服务器跑一个获取 IP 的 docker: https://github.com/yshtcn/GetIP_Service ),然后用脚本把 IP 加入白名单(我的用例是自动注册 IP 到阿里云安全组: https://github.com/yshtcn/alicloud-ip-updater ),并定期删除和清理。只要能交互,用 gpt 类实现脚本可以减轻不少工作量。

WebRTC 泄露IP处理

#工具 来自 OdinSay

之前在使用 MyIP 做泄露检测时发现WebRTC这一项是泄露状态,我尝试查询相关资料和询问ChatGPT对这一问题进行处理,但是始终不得其法。今天无意间看到Odin大佬推荐了一个Chrome插件,安装后进行对应的设置后再使用MyIP进行测试会发现这一项的结果变成了 待检测或链接错误

插件地址:WebRTC Leak Prevent
MyIP检测工具:IPCheck.ing - Check My IP Address and Geolocation - Check WebRTC Connection IP - DNS Leak Test - Speed Test - Ping/MTR Test - Jason Ng Open Source

插件安装后会弹出设置窗口,将 IP handling policy 选项修改为 Disable non-proxied UDP (force proxy) 即可。

四象限工具

#工具 来自 V2ex

复制下面链接到浏览器地址栏可直接打开,不需要网络将打开的网页保存本地 html 后再打开,页面内容就能支持本地保存,刷新不丢失

一个形式上很有意思的四象限工具,将网页内容转换为base64格式展示,虽然逻辑上就是一个html网页,看了一下,数据是用 localStorage 来保存的。

data:text/html;base64,CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJlbiI+Cgo8aGVhZD4KICAgIDxtZXRhIGNoYXJzZXQ9IlVURi04Ij4KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wIj4KICAgIDx0aXRsZT7lm5vosaHpmZA8L3RpdGxlPgogICAgPHN0eWxlPgogICAgICAgIGJvZHkgewogICAgICAgICAgICBtYXJnaW46IDA7CiAgICAgICAgICAgIGhlaWdodDogMTAwdmg7CiAgICAgICAgICAgIGRpc3BsYXk6IGdyaWQ7CiAgICAgICAgICAgIGdyaWQtdGVtcGxhdGUtY29sdW1uczogNTAlIDUwJTsKICAgICAgICAgICAgZ3JpZC10ZW1wbGF0ZS1yb3dzOiA1MCUgNTAlOwogICAgICAgICAgICBmb250LWZhbWlseTogQXJpYWwsIHNhbnMtc2VyaWY7CiAgICAgICAgfQoKICAgICAgICAucXVhZHJhbnQgewogICAgICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogICAgICAgICAgICBhbGlnbi1pdGVtczogc3RyZXRjaDsKICAgICAgICAgICAgcGFkZGluZzogMTBweDsKICAgICAgICB9CgogICAgICAgIC5xdWFkcmFudCBpbnB1dFt0eXBlPSd0ZXh0J10gewogICAgICAgICAgICBib3JkZXI6IG5vbmU7CiAgICAgICAgICAgIHBhZGRpbmc6IDBweDsKICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICAgICAgZm9udC1zaXplOiAxNnB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDsKCiAgICAgICAgfQoKICAgICAgICAucXVhZHJhbnQgdGV4dGFyZWEgewogICAgICAgICAgICBmbGV4LWdyb3c6IDE7CiAgICAgICAgICAgIG1hcmdpbi10b3A6IDEwcHg7CiAgICAgICAgICAgIHBhZGRpbmc6IDE1cHg7CiAgICAgICAgICAgIGJvcmRlcjogbm9uZTsKICAgICAgICAgICAgcmVzaXplOiBub25lOwogICAgICAgICAgICBib3JkZXItcmFkaXVzOiAxMHB4OwoKICAgICAgICB9CgogICAgICAgICNxdWFkcmFudDEgewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhiM2QwOwogICAgICAgIH0KCiAgICAgICAgI3F1YWRyYW50MiB7CiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNiM2U1ZmM7CiAgICAgICAgfQoKICAgICAgICAjcXVhZHJhbnQzIHsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmY2NiYzsKICAgICAgICB9CgogICAgICAgICNxdWFkcmFudDQgewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZDdjM2ZhOwogICAgICAgIH0KCiAgICAgICAgLnF1YWRyYW50IHRleHRhcmVhIHsKICAgICAgICAgICAgYm94LXNoYWRvdzogaW5zZXQgMCAwIDEwcHggcmdiYSgwLCAwLCAwLCAwLjEpOwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KCjxib2R5PgoKICAgIDxkaXYgaWQ9InF1YWRyYW50MSIgY2xhc3M9InF1YWRyYW50Ij4KICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgdmFsdWU9IumHjeimgee0p+aApSIga2V5PSJpbnB1dDEiIG9uaW5wdXQ9InNhdmVEYXRhKHRoaXMpIiAvPgogICAgICAgIDx0ZXh0YXJlYSBvbmlucHV0PSJzYXZlRGF0YSh0aGlzKSIga2V5PSJ0ZXh0YXJlYTEiIHBsYWNlaG9sZGVyPSLmoIfpopjlkoznqpflj6Ppg73lj6/nvJbovpEs5L+d5a2Y5Li6aHRtbOaWh+S7tui/kOihjOaUr+aMgeWunuaXtuacrOWcsOS/neWtmOeKtuaAgSI+PC90ZXh0YXJlYT4KICAgIDwvZGl2PgoKICAgIDxkaXYgaWQ9InF1YWRyYW50MiIgY2xhc3M9InF1YWRyYW50Ij4KICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgdmFsdWU9IumHjeimgeS4jee0p+aApSIga2V5PSJpbnB1dDIiIG9uaW5wdXQ9InNhdmVEYXRhKHRoaXMpIiAvPgogICAgICAgIDx0ZXh0YXJlYSBvbmlucHV0PSJzYXZlRGF0YSh0aGlzKSIga2V5PSJ0ZXh0YXJlYTIiPuS7u+WKoTEK5Lu75YqhMgovL+S7u+WKoTMo5bey5a6M5oiQKTwvdGV4dGFyZWE+CiAgICA8L2Rpdj4KCiAgICA8ZGl2IGlkPSJxdWFkcmFudDMiIGNsYXNzPSJxdWFkcmFudCI+CiAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIHZhbHVlPSLkuI3ph43opoHntKfmgKUiIGtleT0iaW5wdXQzIiBvbmlucHV0PSJzYXZlRGF0YSh0aGlzKSIgLz4KICAgICAgICA8dGV4dGFyZWEgb25pbnB1dD0ic2F2ZURhdGEodGhpcykiIGtleT0idGV4dGFyZWEzIj48L3RleHRhcmVhPgogICAgPC9kaXY+CgogICAgPGRpdiBpZD0icXVhZHJhbnQ0IiBjbGFzcz0icXVhZHJhbnQiPgogICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiB2YWx1ZT0i5LiN6YeN6KaB5LiN57Sn5oClIiBrZXk9ImlucHV0NCIgb25pbnB1dD0ic2F2ZURhdGEodGhpcykiIC8+CiAgICAgICAgPHRleHRhcmVhIG9uaW5wdXQ9InNhdmVEYXRhKHRoaXMpIiBrZXk9InRleHRhcmVhNCI+PC90ZXh0YXJlYT4KICAgIDwvZGl2PgoKICAgIDxzY3JpcHQ+CiAgICAgICAgY29uc3QgdGV4dGFyZWFzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgndGV4dGFyZWEnKTsKICAgICAgICBjb25zdCBpbnB1dHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCdpbnB1dCcpOwogICAgICAgIGNvbnN0IG5zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyh3aW5kb3cubG9jYXRpb24uc2VhcmNoKS5nZXQoIm5zIikKCiAgICAgICAgd2luZG93Lm9ubG9hZCA9IGZ1bmN0aW9uICgpIHsKCiAgICAgICAgICAgIHRleHRhcmVhcy5mb3JFYWNoKCh0ZXh0YXJlYSwgaW5kZXgpID0+IHsKICAgICAgICAgICAgICAgIGxldCBrZXkgPSAiNHF1YWRyYW50LSIgKyBucyArICItIiArIHRleHRhcmVhLmdldEF0dHJpYnV0ZSgia2V5IikKCiAgICAgICAgICAgICAgICBjb25zdCBzYXZlZFZhbHVlID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0oa2V5KTsKICAgICAgICAgICAgICAgIGlmIChzYXZlZFZhbHVlKSB7CiAgICAgICAgICAgICAgICAgICAgdGV4dGFyZWEudmFsdWUgPSBzYXZlZFZhbHVlOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9KTsKICAgICAgICAgICAgaW5wdXRzLmZvckVhY2goKGlucHV0LCBpbmRleCkgPT4gewogICAgICAgICAgICAgICAgbGV0IGtleSA9ICI0cXVhZHJhbnQtIiArIG5zICsgIi0iICsgaW5wdXQuZ2V0QXR0cmlidXRlKCJrZXkiKQoKICAgICAgICAgICAgICAgIGNvbnN0IHNhdmVkVmFsdWUgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShrZXkpOwogICAgICAgICAgICAgICAgaWYgKHNhdmVkVmFsdWUpIHsKICAgICAgICAgICAgICAgICAgICBpbnB1dC52YWx1ZSA9IHNhdmVkVmFsdWU7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH07CiAgICAgICAgZnVuY3Rpb24gc2F2ZURhdGEoZWxlbWVudCkgewogICAgICAgICAgICBsZXQga2V5ID0gIjRxdWFkcmFudC0iICsgbnMgKyAiLSIgKyBlbGVtZW50LmdldEF0dHJpYnV0ZSgia2V5IikKICAgICAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oa2V5LCBlbGVtZW50LnZhbHVlKTsKICAgICAgICB9CgogICAgPC9zY3JpcHQ+Cgo8L2JvZHk+Cgo8L2h0bWw+

请保护好你的Cookie

#新闻 #Chrome插件 来自 X

在X上看到一位玩币安的大佬被黑客盗取了100w美元的货币。看了下推文,大佬推测是一个行业Chrome插件劫持了cookie,绕过2FA和密码验证,通过「对敲交易」转移了他账户内的所有财产。

不过对于推文关于指责币安是同伙的部分币安也在第一时间做了回应 x.com ,不过真假对错不得而知了。

该款恶意插件的具体运行原理是:如果你安装并使用了恶意插件,那么黑客就可以收集你的Cookies,并将其转发到黑客的服务器。黑客能够利用收集的Cookies,劫持活跃用户会话(伪装为用户本人),这样黑客不再需要密码或2FA,能够控制你的帐户。
...
在我的实际情况中,因为我的资料保存在1password之中,黑客没有办法绕过2FA提走我的资产。但可以利用我的Cookies,通过挟持我的账户,对敲获取收益。
...
于是我找到推广KOL,我要确定他是否是黑客的同谋,如果不是,那他要立刻通知他的所有用户,马上停用这个插件,避免更大的损失,但在和他去的联系后,更加让我震惊的故事来了。
...
原来币安早就知道这个插件的存在,甚至鼓励这名KOL与黑客进一步获得更多的信息,而我就是在该插件被进一步推广之时被盗的。币安至少在3、4周前就追查到黑客的地址了,也从该KOL处获取到插件的名字和链接。但即便如此,币安很可能是为了继续追查这个黑客,避免打草惊蛇,而没有及时通知暂停这个产品,我也就此成为了牺牲品。

有大佬第二天就撸了一个Chrome插件用于检测有那些插件使用了Cookie权限。

当然,你也可以直接通过 chrome://extensions-internals/ 查看所有插件信息,在信息页面搜索 cookie 也可以定位有那些插件使用了该权限。

推文地址:x.com

插件地址: Release WhoUsesCookies v0.0.1 · sshallow/WhoUsesCookies · GitHub

chrs:一款全平台换源工具

#工具 来自 大大的小蜗牛

这是一款可以根据你的网速设置速度最快镜像源的工具,全平台支持。

  • 支持十数种编程语言的包源管理,如node、go、python、ruby等等,
  • 支持十数种操作系统的软件源管理,如 ubuntu、debian、arch等
  • 支持部分软件的镜像源管理,如 brew、guix、conda等。

我前些日子重装VPS时就遇到过软件源的问题,网上的一些镜像源信息良莠不齐,而且大部分是有问题的,而这个工具可以根据当前的VPS网络环境去测试已知镜像源,设置最优的那个,完美解决这类问题,以后可以告别去网上找镜像源信息的麻烦了。

在测试的时候conda似乎需要根据提示手动替换,感觉提示信息有点含糊不清,提了个issue,希望改改。
作者已回复,会优化信息提示,很棒。

项目地址:GitHub - RubyMetric/chsrc: chsrc 全平台命令行换源工具. Change Source for every software on every platform from the command line.

🤣JUSTFUN周刊:第二期

2024年6月2日 09:59

这是第二期,希望我能50周如1周般的更新。

  • 「发现」部分新增了当前信息的获取来源。
  • 目前你还可以通过 TG 频道订阅本周刊: https://t.me/justfuning1900

一图

想起小时候和院子里小伙伴玩的场景,祝大家六一快乐。

1. 忽然下起了暴雨,车子被砸的哐啷直响 2. 农夫山泉的东北虎彩蛋

流水账

#讨论

因为肥猫的影响,最近重庆一些大桥效仿桥的人多了很多,地方政府无奈之下增加了很多守桥人员,已经是十步一岗的状态了。

是人们的心理承受能力变弱了还是这个社会确实让人寸步难行,这种现象是值得我们反思的。

#讨论

华为手机销量时经 13 个季度,重回排行第一名。

#讨论

群里今天讨论baidu搜索。

我发现自从有了ChatGPT等ACGI工具后,我已经很少通过搜索引擎获取内容了,至少代码这块已经很少。有疑问都是直接问AI获取答案,以前还有牛头对不上马嘴的时候,随着模型的不断完善,这个问题已经越来越少了。

下一个时代搜索引擎是不是要被淘汰了?

#讨论

今天在x上和看到球大佬在指责「中文互联网正在加速崩塌」一文制造焦虑,他的原推 x.com

其实我的观点和他是差不多的,我不否认可能有大部分文字确实消失了。但是,首先互联网是碎片化的,从一个入口或几个入口去考量「崩塌」这个感觉是有点片面的,你所知道的途径这些资料不在了,不代表他完全在互联网上消失了。

而且当今爆炸式的互联网,大部分文字其实没必要留存下去,如何保留有用的信息才是当代人要考虑的。

不要有悲观,持续输出就好了,有用的东西自然会流传下去,就像 「中文互联网正在加速崩塌」 这篇文章一样,已经有无数副本存在于互联网之上,这不正是互联网的魅力之一吗?

发现

Sink:用 CF 部署的短链生成器

#github 来自 大大的小蜗牛

一个简单、方便,具有统计功能的短链生成器,可以免费部署在 CF 上,利用 KV、Cloudflare 的智能统计实现统计效果,部署流程也十分简单,感兴趣的小伙伴可以试一试吗,官方的路线清单

  • Browser Extension
  • Raycast Extension
  • Apple Shortcuts
  • Enhanced Link Management (with Cloudflare D1)
  • Analytics Enhancements (Support for merging filter conditions)
  • Dashboard Performance Optimization (Infinite loading)
  • Units Test
  • Support for Other Deployment Platforms

只目前刚发布第一个版本,只有一些基础功能,API 之类的还没上线,目前只能在页面上手动添加短链,不过感觉未来可期,可以加个 Star。

目前可以通过官方搭建的示例:Sink.Coo 体验,登录的 Site TokenSinkCool

项目地址:GitHub - ccbikai/Sink: A Simple / Speedy / Secrue Link Shortener with Analytics, 100% run on Cloudflare.

IP-API:利用 CF 搭建的 IP 工具

#工具

利用 Cloudflare Workers / Vercel Edge / Netlify Edge 快速搭一个获取 IP 地址和地理位置信息的接口。

和上面的 Snik 是同一个作者写的,CF 是真的能干很多事情,开发人员的巧思总能利用这些免费资源做很多意料之外的事情,当然,也感谢这些公司贡献的公共资源,正因为有他们,这个互联网才变的更加美好。

项目地址:GitHub - ccbikai/ip-api: 利用 Cloudflare Workers / Vercel Edge / Netlify Edge 快速搭一个获取 IP 地址和地理位置信息的接口。

腾讯云 99 元续费

#VPS

根据博友 近况(8) | Pampo's Blog 的文章发现腾讯云最近出了一个 224 配置 99 元续费的活动,亲测有效,不过时效截止时间为 2024 年 5 月 31 日,截止本文发出时可能已经过期,只能通知一下相关的博客群,为不能通知到更多人而惋惜。

活动地址:精选特惠 用云无忧_腾讯云优惠活动

各种激素高低的影响

#知识 来自 随望淡思

阿均哥分享的两张关于各种激素对人的影响,对其中内啡肽的产生有很深刻的体验,前段时间因为要跑马所以坚持跑步,那段时间每天感觉人没什么负担,很通透。

其实这完全可以作为一份「生活快乐指南图」来看,既然我们知道了如何科学的让自己不再 EMO,那么要做的就是按着科学的方法来做就是了。

知乎开始强制要求登录

#新闻

2024 年 5 月 26 日蓝点网发现知乎现在开始已经无法在不登录的情况下查看回答,根据他们的分析可能是在担心数据被 AI 抓去训练模型。

不过现在才反应过来是不是慢了一些?底裤已经早就被看光了把?

而且现在知乎上的回答几乎全是 AI 生成,之前看产品推荐类的问答全是淘宝客洗版,知乎已经没有刚推出时那种专业劲了,现在找资料不如直接去看小红书,更能找到有用的答案。

原文链接:知乎开始强制要求用户登录账号 否则不允许查看完整回答和专栏内容 – 蓝点网

国外 Google 地图无法看到店铺评价数据解决办法

#知识

谷歌地图看不到评价的,原来是得把蜂窝数据和默认号码都设置成非大陆的手机卡。

推文地址: x.com

PicSmaller:图片压缩工具

#工具 来自 大大的小蜗牛

国人大佬开源的一款高质量在线压缩工具,支持简单、快速以个性化参数压缩 JPEG、PNG、WEBP、AVIF、SVG 和 GIF 图像。

我平时经常要发照片,对这类工具还是挺有需求的。以前使用的是在 V 站发现的 Photofun,不过最近经常抽风打不开了,今天体验了一下 PicSmaller,速度快,选项直观,压缩速度也挺不错的,已经准备作为主要压缩工具使用了。

项目地址:GitHub - joye61/js-controller: Handle http request with controller
网站地址:Pic Smaller – Compress JPEG, PNG, WEBP, AVIF, SVG and GIF images intelligently

NavNav:前端效果库

#前端

无意间发现的一个前端效果库,提供了各种酷炫前端效果,而且复制粘贴即用,提供了 codepen 预览,可以在线调试。

我浏览了一下,很多效果非常不错,准备之后做相册页面时来找找灵感。

网站地址:NavNav+

Git 远程代码执行漏洞

#github

在公众号 编程奇点 中收到一篇关于 Git严重漏洞,远程执行代码,Mac和Windows通杀! 的文章,文内提到了一个新爆出来的 Git 严重漏洞,编号为:CVE-2024-32002 ,只要 Clone 了攻击者的代码库到本地就会受到攻击。

原理是利用 Git 的 hook 机制和符号链接机制执行攻击者想要执行的代码或者程序。

本次漏洞受影响的版本有:

  • v2.45.0
  • v2.44.0
  • <=v2.43.3
  • <=v2.42.1
  • v2.41.0
  • <=v2.40.1
  • <=v2.39.3

原文地址:mp.weixin.qq.com/s/H8cYqIUB0ir4_YZbtl247A

PI时钟:PI包含了很多数字...

#好玩 来自 小胡来杯咖啡

这是一个从PI的小数位中获取一段数字显示小时、分秒的时间网站,还挺有意思的。

另外,你还可以试试第二个网站,这个网站可以查询一段数据是否在PI的小数中出现过,部分人应该可以搜到自己的手机号码,我的手机号还差3位数,明年再来查查看,hhh。

PI时钟:Pi Clock

PI小数搜索:Irrational Numbers Search Engine (可以搜索20亿位)

梦幻联动,Innei加入Follow阅读器开发

#新闻 来自

DIYgod 是Rsshub和xLog的作者,前者是现在几乎所有开发者都在用的订阅工具,后者是一款基于区块链的博客平台。

Innei则是我之前一文的 PC状态同步到博客 这个点子的源发大佬,而且还出过好几款漂亮的主题和博客程序 GitHub - Innei/Shiro: 📜 A minimalist personal website embodying the purity of paper and freshness of snow.,但是这位大佬最近情绪有点低落、焦虑。

DIYGod最近在做一款新的RSS阅读器 GitHub - RSSNext/follow,今天官宣把 Innei 拉到开发团队,希望 Innei大佬走出低谷,两位大佬合力整出一款有意思的、好用的订阅工具。

项目地址:GitHub - RSSNext/follow: [WIP] Next generation information browser

ChatTTS:一款很牛逼的基于AI的开源语言生成器

#AI #工具 来自 bilibili

ChatTTS是专门为对话场景设计的文本转语音模型,例如LLM助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。在HuggingFace中开源的版本为4万小时训练且未SFT的版本.

我在B站偶然间看到关于这个工具的介绍视频,该工具生成的语音停顿、气口简直和真人的一模一样,我的第一想法是以后的有声书、配音员市场是不是要把播音员淘汰了?其次是这种一模一样的声音在以后应该是完全是无法靠人耳来区分的,如何避免出现安全问题呢?

项目地址:GitHub - 2noise/ChatTTS: ChatTTS is a generative speech model for daily dialogue.

用workflow转换Google地图kmz数据为geojson数据

2024年5月30日 13:26

前言

最近终于把大发哥的地图功能算是移植过来了,可以在 https://1900.live/map/ 页面中查看初步效果,不过在后续如何方便的维护这些数据的方式上产生了一些思考。

想直接看代码可以通过右侧TOC跳转到最后。

方案

数据持久化无非是写在文件里或者数据库,所以我想了一下几种方案:

  • 直接手动维护一个json文件,坐标数据等信息去google地图里复制。
  • 之前做了API服务,也可以考虑写进lokijs的数据库里。
  • 直接用一些第三方的API服务。

第一个方案

不过一种方案有些麻烦,大概只在我脑子里停留了2.5秒后就被我踢出局了。

第二个方案

第二个方案我倒是认真想了很久,甚至做了一下原型,验证结果是做虽然可以做,但是很麻烦。

因为相当于我得重新写一套包含前后端的,基于mapbox的交互,如添加标点,删除标点、更新标点,如果要全部弄完很是挺麻烦的,我也没这个信心做好,所以在第二天放弃了。

第三个方案

emmm,好像没有现成的API服务。

我查了一下相关的自制地图服务,好像基本上都没有提供这个功能。

倒是我之前用的google地图的 mymap 服务倒是提供了一个导出功能,不过也没有提供现成的API读取数据。

本来是打算放弃了,采用第二种方案。

但是无意间发现 mymap 的导出链接似乎是固定的,我通过开启无痕浏览,在其他设备上使用链接下载,等方式验证,都是通过的,应该是不需要cookie或者密钥之类的东西,有链接就可以下载。

不过下载下来的格式是.kmz的,无法直接使用,我尝试通过google到的一些在线工具转换后发现可以转换成一种geojson格式,所以我灵光一闪「能不能用workflow去下载mymap的数据再转换成geojson数据存在仓库里以供调用呢?」

所以我 用ChatGPT40写了个脚本测试,果然行得通。

只不过似乎定位坐标的转换不是很准确,会有一些偏差,可能是mapbox和google map之间的标准不一样。

不过这些之后可以慢慢处理。

数据结构

目前虽然数据能正常渲染了,但是地点相关的游记这个功能暂时还没做,不过我大概有一些想法了。

首先数据结构可能要重新做下调整,大发哥那边不知道是通过什么工具维护数据的,他的 description 属性是以个数组对象,而且还包含有 permalink 属性和 image 属性,google map目前还不能这样做,所以我目前只能先把地点图片、游记标题和链接直接拼成字符串,在python脚本中再进分割处理。

其次,我想通过博客集成的 fuse.js 去搜索全站文章中和当前地点名字相关的博文进行展示,这部分可以在地图初始化的时候处理。

大概就是这样,之后再慢慢进行完善把。

代码实现

首先你需要一个库,我这边用的我转换douban数据的库。

新增一个新的wordflow配置文件 .github/workflows/mapsync.yml ,这个action可以手动触发或者每天凌晨2点执行一次。

先通过链接下载kmz文件,再通过仓库的pyhon脚本转换数据为json格式存在 data 目录下。

name: Google To GeoJSON

on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch:

jobs:
  download_and_convert:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.8'
    # 安装python环境
    - name: Install required Python packages
      run: |
        python -m pip install --upgrade pip
        pip install shapely requests
    # 下载kmz文件
    - name: Download KMZ file
      run: |
        URL="https://www.google.com/maps/d/kml?mid=103ngIYVR56ypc1eJp_4FntEU0Nc&cid=mp&cv=qNGi_t2KHPo.zh_CN"
        OUTPUT_FILE="downloaded_file.kmz"
        curl -o $OUTPUT_FILE $URL
      shell: bash
    # 调用python转换kmz文件为json文件,存在指定目录
    - name: Convert KMZ to GeoJSON
      run: |
        mkdir -p data
        python convert_kmz_to_geojson.py downloaded_file.kmz data/geojson.json
      shell: bash
    # 提交到代码库里。
    - name: Commit and push converted GeoJSON
      run: |
        git config --global user.name 'github-actions'
        git config --global user.email 'github-actions@github.com'
        git add data/geojson.json
        git commit -m 'Automated commit of converted GeoJSON'
        git push
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

workflow配置

然后在仓库根目录新增一个python脚本 convert_kmz_to_geojson.py ,内容如下:

import os
import sys
import json
import zipfile
import xml.etree.ElementTree as ET
from shapely.geometry import shape, Point, LineString, Polygon, mapping

def convert_kmz_to_geojson(kmz_file_path, geojson_file_path):
    with zipfile.ZipFile(kmz_file_path, 'r') as kmz:
        kml_content = kmz.read('doc.kml')
        
        root = ET.fromstring(kml_content)
        geojson_content = kml_to_geojson(root)
        
        with open(geojson_file_path, 'w') as geojson_file:
            json.dump(geojson_content, geojson_file, indent=4)

def kml_to_geojson(element):
    geojson = {
        "type": "FeatureCollection",
        "features": []
    }

    for placemark in element.iterfind('.//{http://www.opengis.net/kml/2.2}Placemark'):
        feature = {
            "type": "Feature",
            "properties": {},
            "geometry": None
        }

        for name in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}name'):
            feature["properties"]["name"] = name.text
        
        for description in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}description'):
            feature["properties"]["description"] = description.text

        for point in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Point'):
            coords = point.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
            lon, lat, _ = map(float, coords.split(','))
            feature["geometry"] = mapping(Point(lon, lat))
        
        for linestring in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}LineString'):
            coords = linestring.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
            points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
            feature["geometry"] = mapping(LineString(points))
        
        for polygon in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Polygon'):
            outer_boundary = polygon.find('.//{http://www.opengis.net/kml/2.2}outerBoundaryIs')
            if outer_boundary is not None:
                coords = outer_boundary.find('.//{http://www.opengis.net/kml/2.2}coordinates').text.strip()
                points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
                feature["geometry"] = mapping(Polygon([points]))
        
        if feature["geometry"] is not None:
            geojson["features"].append(feature)
    
    return geojson

if __name__ == "__main__":
    kmz_file_path = sys.argv[1]
    geojson_file_path = sys.argv[2]
    convert_kmz_to_geojson(kmz_file_path, geojson_file_path)

然后前端使用这个数据文件即可。

🤣JUSTFUN周刊:第一期

2024年5月26日 10:40

开刊语

受各位大佬的影响,也想开个分享类的周刊,看看自己能坚持多久。

至于刊物名字把之前的博客名字拿来用了,「JUSTFUN」

初步打算将本刊分为三个方面:

  • 一图:每周保底发一张自己拍的照片
  • 流水账:本周的思考、工作记录等等琐碎的事
  • 发现:本周遇到的所有有意思的东西,可以是项目、观点、见闻等等。

一图

  1. 上个星期的燕子还肉嘟嘟的,
  2. 公司后面的洗车房,工人们忙了了一天终于能下班了
  3. 这个星期已经翅膀硬了,开始自己飞了。
  4. 昨天刮了一阵妖风,伴随着风下了一夜的雷雨,今天又放晴了。

流水账

  • #工作:开始使用 Done List 记录工作
  • #战争:在 b 站看了这个视频,感慨战争真的很残酷,最近伊朗又在出问题,这个世界越来越混乱了。
    【一直压着没说的话,拍摄于 4 月 20,(人比战争更残酷),你可以说战争就是生命收割机,正常战斗他们或许不会牺牲,官僚的利益游戏)),-哔哩哔哩】 https://b23.tv/IS7tRJ9
  • #知识:刚刚无意间做了个尝试,发现在Windows11中,选中多个文件后按 F2 可以对这多个文件批量重命名,有用的支持增加了。
  • #讨论 关于最近的「美国登月成功」是假的,这一论点在互联网上讨论的热火朝天,博客群里自然也没错过这波风潮,我认为,怀疑难道不是应该要有理论基础吗,没理由的冒出一句「美国登月是假的」不是耍流氓吗,而且有时候很多道理都是讲不通,近代后没有再执行过登月任务难道不是因为成本考量吗?人上去能做什么,能待多久?而且我其实不纠结真假,自那以后月球的相关资料越来越多才是对于人类这个大集体友好的事。
  • #讨论:kvins最近一篇博文中 略显古老的邮件营销,在当下还有用么? - Kevin's提到了关于现在的纸质信件的营销是否有用。
    其实我因为行业原因也偶会收到一些医药公司的递送的产品资料,个人感觉这种形式已经没什么实际意义了,而且据我了解前些日子成都的药交会热闹程度也已经大不如前了,主要这些线下的推广成本太高了。而且在互联网高速发展的当代已经有了更多渠道可以获得更优质且对口的资源,因为企业信息泄露,如今成本更便宜、效果更好的电话营销才是他们主要选择手段了。
  • #知识:这两抽空帮her.blue做了服务器重装,安全配置等工作,也重新梳理了一下相关要用到的命令,昨天写了一篇 服务器重装设置指南

发现

开源 AI 眼镜:Brilliant Labs

#AI

一个很酷的 AI 眼镜开源项目,支持 Perplexity、OpenAI 的 ChatGPT、Whisper 等 AI 模型,就是价格不太便宜 $349 刀,有三色可选。语言目前只支持 English, French, Italian, German ,中文和日语的支持好像正在开发了。

他们之前还出过一个单镜片原型,似乎是将组件嵌入到镜片之中,后来发布的版本更趋向于华为智能眼镜那种样式,就是不知道 AI 的加持会不会更加实用,唯一的问题就是国内要想用好的话可能需要一些手段,而且响应速度应该也是个问题,不过最近体验 ChatGPT-O4,感觉响应速度已经比以前快很多了,可以看下后续的市场反馈。

Frame is designed to be worn as a pair of glasses with a suite of AI capabilities out of the box. Whether your daily pair of specs or workbench prototyping tool, Frame is ready for the journey.

仓库地址:Brilliant Labs · GitHub

1.产品结构图 2. 效果图

轻笔记软件:Slippod

#笔记软件

我个人认为这是一款类似 Memos 、Flomo 的小而美卡片笔记软件,应该使用的是 electron+SqLite 的结构,数据存在本地,官方列出的特性中有笔记知识库发布、笔记回顾等功能。

不过目前就我下载下来的体验,几乎和上述提到的两款软件一模一样,目前 UI 暂时没有中文,教程笔记倒是做了中文,应该还在早期的快速开发、迭代阶段。

目前免费使用 14 天,试用期到了给他们发邮件会免费送一份给你,不过目前个人感觉还不太完善,但是对于这种喜欢这种小而美笔记软件的朋友后续可以在再期待一下。

官网地址: Slippod - Shape up Creative Ideas with Ease

GitHub-Hosts

#Github

在国内访问 Github 一直是个问题,GFW 的态度也是比较模糊的,反正是时而灵时而不灵,而 Github-hosts 就是一个通过修改系统 hosts 达到访问 GitHub 目的的工具,不过这个项目需要手动修改,记得之前有个工具是可以自动修改、更新最新的 hosts 的,之后想起了再分享把。

项目地址:GitHub - maxiaof/github-hosts: 通过修改 Hosts 解决国内 Github 经常抽风访问不到,每日更新

2024 年 5 月 20 日更新

#Github Hosts Start
#Update Time: 2024-05-20
#Project Address: https://github.com/maxiaof/github-hosts
#Update URL: https://raw.githubusercontent.com/maxiaof/github-hosts/master/hosts
140.82.113.26 alive.github.com
140.82.112.26 live.github.com
185.199.110.154 github.githubassets.com
140.82.114.22 central.github.com
185.199.108.133 desktop.githubusercontent.com
185.199.108.153 assets-cdn.github.com
185.199.108.133 camo.githubusercontent.com
185.199.111.133 github.map.fastly.net
146.75.121.194 github.global.ssl.fastly.net
140.82.121.3 gist.github.com
185.199.108.153 github.io
140.82.121.4 github.com
192.0.66.2 github.blog
140.82.121.6 api.github.com
185.199.110.133 raw.githubusercontent.com
185.199.108.133 user-images.githubusercontent.com
185.199.108.133 favicons.githubusercontent.com
185.199.109.133 avatars5.githubusercontent.com
185.199.108.133 avatars4.githubusercontent.com
185.199.108.133 avatars3.githubusercontent.com
185.199.109.133 avatars2.githubusercontent.com
185.199.111.133 avatars1.githubusercontent.com
185.199.109.133 avatars0.githubusercontent.com
185.199.108.133 avatars.githubusercontent.com
140.82.121.10 codeload.github.com
54.231.130.177 github-cloud.s3.amazonaws.com
16.182.65.49 github-com.s3.amazonaws.com
54.231.224.73 github-production-release-asset-2e65be.s3.amazonaws.com
16.182.68.121 github-production-user-asset-6210df.s3.amazonaws.com
52.217.165.49 github-production-repository-file-5c1aeb.s3.amazonaws.com
185.199.109.153 githubstatus.com
140.82.112.17 github.community
51.137.3.17 github.dev
140.82.112.21 collector.github.com
13.107.42.16 pipelines.actions.githubusercontent.com
185.199.108.133 media.githubusercontent.com
185.199.111.133 cloud.githubusercontent.com
185.199.111.133 objects.githubusercontent.com
#Github Hosts End

Github 个人情况汇总工具:GitGlance

#github

这是一款使用 Next.js 搭建通过你的 Github 信息生成一个Github信息汇总情况说明的网站,在用作展示、应聘时的简历应该都是可以的,你可以直接通过他们搭建在 vercel 上的网站使用,如 https://gitglance.vercel.app/rebron1900 ,将我的 rebron1900 换成你的 Github 用户名就行,当然你也可以 Frok 后自己搭建。

项目地址: GitHub Profile Visualizer

Obsidian 剪藏插件:Slurp

#obsidian 插件

一个可以将网站 「吸溜」 一下保存为一个干净的 Markdown 格式笔记的插件。目前支持通过在 Obsidian 之内输入 URL 添加或者通过标签栏的 Javascript 超链接剪藏。

不过目前没有转存网站的图片部分,如果目标网站有防盗链的话可能会缺图,而且在开发者的 Roadmap 中没有发现有这方面的开发意向。

项目地址: GitHub - inhumantsar/slurp: Slurps webpages and saves them as clean, uncluttered Markdown. Think Pocket, but better.

<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=205738091&bvid=BV1Sh411v7XQ&cid=340315860&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>

Hugo新特性

#hugo

hugo最近在社交平台发布了即将推出的新特性:可以实现从google表格中实时更新数据到静态页面上。

这是不是意味着以后可以用xls表格管理博客文章?数据如果是动态的就可以有更多的创新性玩法,虽然我用的是11ty,不过还是蛮期待这个特性发布后开发者们都能玩出什么样的花样。

感觉以后的网站都会趋向于边缘计算多一些,多是动静分离一类的架构。

嘟文地址: gohugoio@fosstodon.org

FreeReName:批量重命名工具

#工具箱

一款基于Tauri写的开源跨平台重命名小工具,特性如下:

  • 支持拖拽添加文件和文件夹
  • 支持创建多个配置
  • 单个配置内支持多个规则
  • 支持 js 脚本,内置 Monaco Editor 。

项目地址: GitHub - cyhuajuan/FreeReNamer: 功能强大又易用的文件批量重命名软件

不过目前要实现类似功能的工具还挺多的,比如我目前用的到是uTools的重命名插件,要轻量化一些,不过论定制性肯定不如这个工具,尤其是对那些玩PT、NAS,需要复杂规则重命名的玩家来说。

说唱梦工厂:拉斐尔轮船——红手指

#music

在说唱梦工厂听到的一个20岁的说唱歌手写的歌,我个人觉得完成度挺好的,尤其是歌词,有好强的既视感。不过评委说风格太像王以太了,不过我平时没怎么听王以太,没有很强的不适感。

告诉你为什么Chrome这么慢——Linus谈科技

#chrome

今天在B站刷到Linus讨论Chrome为什么变慢原因的视频,总结大致意思就是大量的拓展拖慢了浏览器运行速度,想快就少开点插件(刚刚检查了一下,我大概有20来个插件,重审查了一遍,关到剩下16个。

另外Linus提到了一个浏览器性能测试工具 Speedometer 3 用于测试接近用户体验的评分,我的电脑在不开启无痕模式和开启无痕模式的评分分别为8分和12分...。

服务器重装设置指南

2024年5月25日 17:38

前言

其实也不算指南把,因为最近要帮 her.blue 的服务器重新装系统和全线使用 docker 安装应用,在操作过程做顺便做了个记录,希望能帮到你。

数据备份

  • 在操作之前最好做各个服务器快照,以防万一
  • 如果没有快照就对所有程序、数据库等单独自己做数据备份
本次操作过程就装完系统后就立马触发这个问题需要重新装系统,还好数据都有备份。

服务安全加固

添加非root用户

  • sudo adduser username 添加用户

添加用户到 sudo 用户组

  • sudo usermod -aG sudo username
  • id username 这个命令可以查看用户有什么权限

生成安全密钥

切换到新增加的用户,生成该用户的安全密钥

  • ssh-keygen -t rsa -b 4096
  • cat id_rsa.pub >> authorized_keys

设置ssh,启用密钥登录

编辑 /etc/ssh/sshd_config 文件,进行如下设置:

RSAAuthentication yes
PubkeyAuthentication yes

你也可以将root用户禁用登录(禁用后root用户就不能登录了,一定要先把普通用户的登录测试好后再禁用

PermitRootLogin yes

最后设置完后禁用密码登录

PasswordAuthentication no

重启SSH服务

sudo service sshd restart

安装Nginx

在debain8.9安装nginx失败,按下方的操作更换源也不行,后来无奈把系统重新做到11.8后才可以正常安装

先更新apt

sudo apt-get update

安装

sudo apt-get install nginx

换源

可能会因为网络问题更新失败,需要换源 没用,最后换了系统版本。

# 备份
cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo vim /etc/apt/sources.list

# 阿里镜像
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse 
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse 
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse 
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse 
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse 
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
# 中科大
deb http://mirrors.ustc.edu.cn/ubuntu/ precise-updates main restricted
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise-updates main restricted
deb http://mirrors.ustc.edu.cn/ubuntu/ precise universe
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise universe
deb http://mirrors.ustc.edu.cn/ubuntu/ precise-updates universe
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise-updates universe
deb http://mirrors.ustc.edu.cn/ubuntu/ precise multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ precise-updates multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise-updates multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ precise-backports main restricted universe multiverse
# 搜狐源
deb http://mirrors.sohu.com/ubuntu/ precise-updates main restricted
deb-src http://mirrors.sohu.com/ubuntu/ precise-updates main restricted
deb http://mirrors.sohu.com/ubuntu/ precise universe
deb-src http://mirrors.sohu.com/ubuntu/ precise universe
deb http://mirrors.sohu.com/ubuntu/ precise-updates universe
deb-src http://mirrors.sohu.com/ubuntu/ precise-updates universe
deb http://mirrors.sohu.com/ubuntu/ precise multiverse
deb-src http://mirrors.sohu.com/ubuntu/ precise multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-updates multiverse
deb-src http://mirrors.sohu.com/ubuntu/ precise-updates multiverse
deb http://mirrors.sohu.com/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://mirrors.sohu.com/ubuntu/ precise-backports main restricted universe multiverse
# 网易源
deb http://mirrors.163.com/ubuntu/ precise-updates main restricted
deb-src http://mirrors.163.com/ubuntu/ precise-updates main restricted
deb http://mirrors.163.com/ubuntu/ precise universe
deb-src http://mirrors.163.com/ubuntu/ precise universe
deb http://mirrors.163.com/ubuntu/ precise-updates universe
deb-src http://mirrors.163.com/ubuntu/ precise-updates universe
deb http://mirrors.163.com/ubuntu/ precise multiverse
deb-src http://mirrors.163.com/ubuntu/ precise multiverse
deb http://mirrors.163.com/ubuntu/ precise-updates multiverse
deb-src http://mirrors.163.com/ubuntu/ precise-updates multiverse
deb http://mirrors.163.com/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://mirrors.163.com/ubuntu/ precise-backports main restricted universe multiverse

在更新过程中出现 E: Could not get lock /var/lib/apt/lists/lock - open (11: Resource temporari 错误,通过以下命令解锁

sudo rm /var/lib/apt/lists/lock

安装Docker

 curl -fsSL https://get.docker.com -o get-docker.sh
 sudo sh get-docker.sh

安装Docker-compose

一键安装

sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

添加执行权限

sudo chmod +x /usr/local/bin/docker-compose

添加链接到bin目录

sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

如果docker-compose没有设置user设置,可能会以root等高权限组的用户创建,此时需要修改文件夹、文件权限,其中1001是你的目标用户、用户组

chown -R 1001:1001 文件夹、目录名称

在使用docker-compose创建容器时如果不是非要用到root权限,建议加上 user: 1001:1001,文件夹权限使用普通用户,1001 可以是你的非root用户id。

Docker-compose安装MySQL

version: '3.1'

services:
  mysql:
    container_name: mysql
    image: mysql:8.0
    restart: always
    user: 1001:1001
    ports: 
      - 10001:3306
    environment:
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - /home/herblue/data/mysql-data/data:/var/lib/mysql
      - /home/herblue/data/mysql-data/config:/etc/mysql/conf.d

进入mysql中添加用户、创建数据

sudo docker exec -it mysql bash

mysql -u root -p

CREATE DATABASE ghost_test;

CREATE USER 'ghost_test'@'localhost' IDENTIFIED BY 'your_password';

GRANT ALL PRIVILEGES ON ghost_test.* TO 'ghost_test'@'localhost';

FLUSH PRIVILEGES;
登录域最好给任意,我设置为localhost似乎不能登录

DockerCompose安装ghost

Ghost中环境变量的server__port会直接开启外部端口。

version: '3.1'

services:
  ghost:
    container_name: ghost
    image: ghost:latest
    restart: always
    user: 1001:1001
    ports:
      - "10002:8080"
    environment:
      server__host: "127.0.0.1"
      server__port: 10002
      
      portal__url: "https://npm.webcache.cn/@tryghost/portal@~{version}/umd/portal.min.js"
      sodoSearch__url: "https://npm.webcache.cn/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js"
      sodoSearch__styles: "https://npm.webcache.cn/@tryghost/sodo-search@~{version}/umd/main.css"
      comments__url: "https://npm.webcache.cn/@tryghost/comments-ui@~{version}/umd/comments-ui.min.js"
      comments__styles: "https://npm.webcache.cn/@tryghost/comments-ui@~{version}/umd/main.css"
      
      logging__level: "error"
            

      #database__client: sqlite3
      #database__connection__filename: "content/data/ghost-sqlite.db"
      database__client: mysql
      database__connection__host: "127.0.0.1"
      database__connection__port: 10001
      database__connection__user: "ghost-test"
      database__connection__password: "test"
      database__connection__database: "ghost_test"

      url: https://yourdomain

    volumes:
      - /home/herblue/data/ghost-data:/var/lib/ghost/content
    network_mode: "host"

备份

建议对所有容器数据进行容灾备份,避免出现被黑、脑残操作等等意外导致数据丢失。

我其实之前写过一个备份脚本,不过在上次搬家的时候忘记备份了,还挺可惜的。

不过现在有ChatGPT辅助,重新生成一个完善点的也不是什么难题。

mysql的备份脚本内用到了一个my.cnf文件,请自省提前copy到mysql容器内

#!/bin/bash
set -e  # 遇到错误停止执行
set -x  # 打开调试模式

# 设置参数
BACKUP_DIR="/home/username/backup"
TARGET_DIR="/home/username/data"
DATABASES=("ghost_prod" "test" "test2")  # 数据库列表
ZIP_PASSWORD="your_zip_password"
DATE=$(date +"%Y%m%d%H%M%S")
MYSQL_CONTAINER_NAME="mysql"
USER_HOME="/home/username"  # 请根据实际用户主目录路径修改
TEMP_BACKUP_DIR="$USER_HOME/backup_temp_$DATE"

# 检查并创建所需的目录
mkdir -p "$BACKUP_DIR"
mkdir -p "$TEMP_BACKUP_DIR"

# 1. 自动将指定目录的数据复制到临时备份目录
if [ -d "$TARGET_DIR" ]; then
    cp -r "$TARGET_DIR"/* "$TEMP_BACKUP_DIR"
else
    echo "Target directory $TARGET_DIR does not exist"
    exit 1
fi

# 2. 自动将指定的 MySQL 数据库导出成 SQL 文本并放到临时备份目录中
for MYSQL_DATABASE in "${DATABASES[@]}"; do
    SQL_FILE="$TEMP_BACKUP_DIR/${MYSQL_DATABASE}_backup_${DATE}.sql"
    
    # 从Docker容器中导出数据库
    docker exec "$MYSQL_CONTAINER_NAME" sh -c "mysqldump --defaults-extra-file=/root/.my.cnf $MYSQL_DATABASE" > "$SQL_FILE"
done

# 3. 将临时备份目录打包成带密码的压缩包
ARCHIVE_NAME="backup_${DATE}.zip"
if ! zip -r -P "$ZIP_PASSWORD" "$BACKUP_DIR/$ARCHIVE_NAME" "$TEMP_BACKUP_DIR"; then
    echo "Failed to create backup archive"
    exit 1
fi

# 删除临时备份目录
rm -rf "$TEMP_BACKUP_DIR"

# 4. 备份文件保留规则
find "$BACKUP_DIR" -type f -name "*.zip" | while read backup_file; do
    backup_date=$(basename "$backup_file" | grep -o -E '[0-9]{14}')
    backup_epoch=$(date -d "$backup_date" +%s)
    current_epoch=$(date +%s)
    diff_days=$(( (current_epoch - backup_epoch) / 86400 ))

    if [ $diff_days -ge 365 ]; then
        # 保留最近365天的一个备份
        if [ $diff_days -gt 365 ]; then
            rm "$backup_file"
        fi
    elif [ $diff_days -ge 150 ]; then
        # 保留最近150天的一个备份
        if [ $(( diff_days % 150 )) -ne 0 ]; then
            rm "$backup_file"
        fi
    elif [ $diff_days -ge 90 ]; then
        # 保留最近90天的一个备份
        if [ $(( diff_days % 90 )) -ne 0 ]; then
            rm "$backup_file"
        fi
    elif [ $diff_days -ge 30 ]; then
        # 保留前30天的一个备份
        if [ $(( diff_days % 30 )) -ne 0 ]; then
            rm "$backup_file"
        fi
    elif [ $diff_days -ge 7 ]; then
        # 保留最近一个星期每天的备份
        if [ $(( diff_days % 7 )) -ne 0 ]; then
            rm "$backup_file"
        fi
    fi
done

echo "Backup completed and expired backups cleaned"

rclone备份

然后再利用rclone将备份文件夹同步到OneDrive就好了,以下是安装命令

sudo -v ; curl https://rclone.org/install.sh | sudo bash

安装完后通过 rclone config 配置远端储存服务,具体的教程可以自己搜一下。

文章点赞和浏览数统计实现

2024年5月19日 11:08

前段时间大发哥TG频道说要发一个用CFWorker实现文章点赞和浏览数统计的功能,很快啊,星期二教程就出来了: Hugo Cloudflare Worker

整体方案是使用CfWorker 实现api请求,再用D1做数据持久化,效果还是很赞的。

本来我是想照着教程完全一步步来的,但是想到我前文 将博主 PC 上使用的应用信息实时显示到博客 在折腾时搭了一个API服务,就不想再去用CFWorker了,打算直接在自己服务器上实现这个功能,顺便再实践一下用Express搭配数据库工具。

所以打算稍改一下,结构如下:

  • 前端照搬
  • API结构实现部分照搬
  • 数据库方面结构、读写操作照搬
  • 数据库持久化用 LokiJS

前端

前端我这里用的Alpinejs做驱动,前文 用 Alpinejs 完成主题切换功能 介绍过这个库,在不使用Vue等工具下普通HTML的一个替代方案,好用。

  • 在模板中渲染的时候设置文章 ID (这里我因为用的Ghost做数据源,所以自带一个MD5的ID,用一个固定唯一值就好)
  • 设置 x-data 为后面js中初始化的 post_action 对象
  • 通过 x-show 调用 initViewsinitLike 实现组件数据初始化
  • 通过 @click 绑定 like 函数实现点赞

HTML

<!-- 点赞按钮 -->
<div x-data="post_action" class="post_action flex flex-wrap justify-center">
    <div class="post_like" x-show="initLike('{{ post.id  }}')" data-postid="{{ post.id  }}" @click="like('{{ post.id  }}')">
        {% svg 'like', '0 0 26 26' %}
        <div class="post_like_desc">
            「 还好我们还有文字... 」
        </div>
    </div>
</div>

<!-- 文章页脚数据显示 -->
<div>
  <a class="flex align-center" href="#" title="Last modified by" rel="noopener">
    {% svg 'like' %}
    <span class="post_likes" ></span>
  </a>
</div>


<div>
  <a class="flex align-center" href="#" title="Last modified by" rel="noopener">
    {% svg 'views' %}
    <span class="post_views" x-data="post_action" x-show="initViews('{{ post.id  }}')"></span>
  </a>
</div>

页面结构

CSS部分

之前做那个PC上APP状态显示的时候使用了一下CSS动画,效果还不错,这里再次借鉴一下那个思路,点赞后实现了一个心脏跳动的感觉。

.post_action {
    .post_like {
        align-items: center;
        cursor: pointer;
        display: flex;
        height: 6rem;
        position: relative;
        text-align: center;
        flex-direction: column;
        justify-content: center;

        svg {
            fill: none;
            stroke: var(--body-font-color);
            height: 1.5em;
            margin-inline-end: 00;
            width: 1.5em;
        }
        /** 点赞按钮的激活效果 **/
        &.active svg{
            fill: var(--hint-color-danger);
            stroke: var(--hint-color-danger);

            animation: growAndFade 1s 1;
            transform-origin: center;
        }
    }
    .post_like_desc{
        font-style: oblique;
        padding: 8px 0px;
        color: var(--gray-500);
        font-size: 14px;
    }

    
}

/** 点赞动画 **/
@keyframes growAndFade {
    0% {
      transform: scale(1);
      opacity: 1;
    }
    50% {
      transform: scale(2);
      opacity: 0.3; /* 这里调整透明度,以符合动画结束立即恢复的需求 */
    }
    100% {
      transform: scale(1);
      opacity: 1;
    }
  }

JS中Alpinejs的设置部分

💡
我的代码写的应该不是很规范,因为没有系统性学过,感觉很多语法、使用方式都存在问题,希望有看到不合理、错误的地方的大佬能指点一下。
var apiUrl = "https://yourapidomain.com";
Alpine.data("post_action", () => ({
    apiUrl: apiUrl,
    // 点赞函数
    like: (post_id) => {
        // 初始化一些元素变量
        var likelist = localStorage.getItem("lieklist") || "";
        var likeButton = document.querySelector(".post_like");
        var likeText = document.querySelector(".post_likes");
        
        // 每次点赞都去本地数据中匹配一下
        // 我这里将用户浏览的数据拼成字符串处理的,以后应该会有性能问题
        if (likelist.indexOf(post_id + ",") != -1) {
            console.log("你已经点过赞了");
            likeButton.classList.add("active");
        } else {
            // 如果没有就发起POST请求更新数据
            fetch(`${apiUrl}/post/${post_id}/like`, { method: "post" })
                .then((res) => {
                    return res.json();
                })
                .then((data) => {
                    // 成功了就做一些页面数据同步的工作
                    console.log("点赞成功" + JSON.stringify(data));
                    localStorage.setItem("lieklist", likelist + post_id + ",");
                    likeButton.classList.add("active");
                    likeText.innerText = data.likes
                })
                .catch((error) => {
                    console.error(
                        "There was a problem with the fetch operation:",
                        error
                    );
                });
        }
    },
    // 后续的代码逻辑上都差不多,我就不一一写备注了。
    initLike: (post_id) => {
        var likelist = localStorage.getItem("lieklist") || "";
        var likeButton = document.querySelector(".post_like");
        var likeText = document.querySelector(".post_likes");
        if (likelist.indexOf(post_id + ",") != -1) {
            likeButton.classList.add("active");
        }
        fetch(`${apiUrl}/post/${post_id}/like`)
            .then((res) => {
                return res.json();
            })
            .then((data) => {
                if (data.likes) {
                    likeButton.dataset.like = data.likes;
                    likeText.innerText = data.likes;
                }else{
                    likeButton.dataset.like = 0;
                    likeText.innerText = 0; 
                }
            }).catch((error) => {
                likeButton.dataset.like = 0;
                likeText.innerText = 0;
                console.error(
                    "There was a problem with the fetch operation:",
                    error
                );
            });
        return true;
    },
    initViews: (post_id) => {
        var viewlist = localStorage.getItem("viewlist") || "";
        var viewText = document.querySelector(".post_views");

        if (viewlist.indexOf(post_id + ",") != -1) {
            fetch(`${apiUrl}/post/${post_id}/views`)
                .then((res) => {
                    return res.json();
                })
                .then((data) => {
                    if (data.Views) {
                        viewText.innerText = data.Views;
                    }else{
                        viewText.innerText = 0; 
                    }

                });
        } else {
            fetch(`${apiUrl}/post/${post_id}/views`, { method: "post" })
                .then((res) => {
                    return res.json();
                })
                .then((data) => {
                    console.log("浏览量" + JSON.stringify(data));
                    localStorage.setItem("viewlist", viewlist + post_id + ",");
                    viewText.innerText = data.Views;
                })
                .catch((error) => {
                    viewText.innerText = 0;
                    console.error(
                        "There was a problem with the fetch operation:",
                        error
                    );
                });
        }

        return true;
    },
}));

后端

后端的实现其实还是和之前一样,只不过这次多了一个数据库的持久化操作。

数据持久化

因为是个轻量型的服务,我也不想搞什么MySQL之类的服务,甚至连SqLite也不想用,所以想到之前在使用 Twikoo 的时候,发现它用的一个数据服务是一个以 JSON 为结构的数据库:LokiJS,特性如下,应付这个功能应该绰绰有余了。

  1. Fast performance NoSQL in-memory database, collections with unique index (1.1M ops/s) and binary-index (500k ops/s)
  2. Runs in multiple environments (browser, node, nativescript)
  3. Dynamic Views for fast access of data subsets
  4. Built-in persistence adapters, and the ability to support user-defined ones
  5. Changes API
  6. Joins
yarn add -D lokijs

安装LokiJS依赖

LokiJS基础操作

Loki的数据操作很简单、方便,会用JavaScript就可以,对于我来说上手难度更低。

// LojiJS基础操作
// 新建数据库
var db = new loki("blog.db");
// 添加一个「表」(集合)
db.addCollection("posts");
//通过id查找
var post = posts.findOne({ post_id: post_id });
// 插入数据
posts.insert({
    post_id: post_id,
    likes: 1,
    Views: 0,
});
// 操作更新到数据库中
posts.update(post);

另外,

Loki中 addCollection 这个操作是会覆盖你原有的数据的,我之前用的时候以为打开自动加载、自动保存就好了,没有判断表存不存在,导致了每次服务器重启都会新添加一个表进去,导致原表数据被覆写。

另外Express的接口代码和前端的JS代码一样没有做结构整理,图方便全部写了一遍,后期是有很大优化空间的。

// 创建数据库
// 开启自动加载,自动保存
var db = new loki("blog.db", {
    autoload: true,
    // 数据库加载完毕后回调
    autoloadCallback: loadHandler,
    autosave: true,
});

let posts;

function loadHandler() {
    posts = db.getCollection("posts");
    // 如果posts不存在才创建
    if (posts === null) {
        db.addCollection("posts");
    }
}

app.get("/post/:key/like", (req, res) => {
    const post_id = req.params.key;
    try {
        let post = posts.findOne({post_id:post_id})
        if(post){
            res.status(200).json(post)
        }
    } catch (e) {
        res.status(500).json({ error: e });
    }
});

app.post("/post/:key/like", (req, res) => {
    const post_id = req.params.key;
    try {
        let post = posts.findOne({ post_id: post_id });
        if (!post) {
            posts.insert({
                post_id: post_id,
                likes: 1,
                Views: 0,
            });
            post = posts.findOne({ post_id: post_id });
        } else {
            post.likes += 1;
        }
        posts.update(post);
        res.status(200).json(post);
    } catch (e) {
        res.status(500).json({ error: e });
    }
});

app.post("/post/:key/views", (req, res) => {
    const post_id = req.params.key;
    try {
        let post = posts.findOne({ post_id: post_id });
        if (!post) {
            posts.insert({
                post_id: post_id,
                likes: 0,
                Views: 1,
            });
            post = posts.findOne({ post_id: post_id });
        } else {
            post.Views += 1;
        }
        posts.update(post);
        res.status(200).json( post );
    } catch (e) {
        res.status(500).json({ error: e });
    }
});

app.get("/post/:key/views", (req, res) => {
    const post_id = req.params.key;
    try {
        let post = posts.findOne({post_id:post_id})
        if(post){
            res.status(200).json(post)
        }
    } catch (e) {
        res.status(500).json({ error: e });
    }
});


Enjoy~

弄完这些也算是对NodeJS发布一个拥有完整功能的网站有了些基础了解,不过这种了解暂时还很片面,在架构、性能、安全性方面肯定还有很多需要学习的地方,之后如果碰到了以上问题了再做学习、优化。

在折腾中学习、进步,这种感觉真棒按。

我也出一版纯CSS+JS热力图

2024年5月17日 19:23

前言

之前根据蜗牛哥的教程弄了一版使用Cal-heatmap库生成的热力图 博客更新热力图,不过这个方式需要加载四五个js文件还有一些css文件,比较拖慢整个网页的加载速度,所以蜗牛哥后来又出了一个CSS 和 JS 实现博客热力图 方案,不过当时我闲麻烦一直没跟着做。

不过我也一直在思考该怎么用纯CSS和JS更为简便的实现这个功能,今天灵光一闪,有了些思路,所以趁着这股热乎劲把这个功能实现了,撰文分享一下我的思路。

思路

我的思路其实很简单,因为博客上显示的热力图其实就是一个很多小方块组成的方阵,所以其实不用管什么年份、月份之类的,我们只需生成固定个数的小方块,再将匹配的数据填充进去不就好了吗?

我的构思如下:

  • 生成一个 7 * 9 列的格子方阵,一共是是63个
  • 每个格子要匹配往后63天中对应一天的文章
    • 如果开始当天不是星期天,则需要找到这个星期的星期天作为开始时间
  • 在生成格子的时候将文章数据附加进小格子内。
  • 完成

实现

💡
有了ChatGPT后折腾博客真的好方便呀,你只需要有一些基础编程知识和思维就能通过它构建出自己想要的代码。

不得不感慨一句:
以后的编码工作真的是看创意和思想,以及如何通过ChatGPT更快速、准确的生成自己想要的代码。

定义html结构

  • 共9列 grid-colum ,每列7个 grid-tem
  • 每个 grid-item 中有一个 item-info 用于展示信息
  • item-info 中根据需要设置选项
    • 背景颜色
    • 鼠标提示信息
    • 日期信息
    • ...
<strong>热力图</strong>
<div class="relitu-container" id="relitu-container">
  <div class="grid-column">
    <div class="grid-item">
      <div style="background-color:rgba(77, 208, 90,0.8674)" class="item-info item-tippy" data-tippy-content="共 1 篇,共 3337 字<br />- <a href='https://1900.live/i-also-run-a-half-marathon-finish-the-race/'>我也跑个半程马拉松:完赛</a></br>" data-date="2024-04-08" aria-expanded="false"></div>
    </div>
  </div>
</div>

html结构

设置CSS

这里通过 grid 布局实现了Heatmap的效果,我为了省事直接通过 after 搭配 content 在最后几个方块添加了星期标注。

.relitu-container {
    display: grid;
    grid-template-columns: repeat(9, 18px);
    grid-template-rows: repeat(7, 18px);
    gap: 5px;
    margin: 1em 0;
    position: relative;

    .grid-column {
        display: grid;
        gap: 5px;
    }

    .grid-item {
        align-items: center;
        display: flex;
        justify-content: center;
        min-height: 18px;
        position: relative;

        a{
            color:var(--hint-color-info)
        }

    }

    .item-info {
        font-size: 10px;
        height: 100%;
        width: 100%;
        background-color: var(--gray-200);
        border-radius: .25rem;

        font-size: 10px;
        height: 100%;
        width: 100%;
    }

    div:nth-child(9) > div:after{
        position: absolute;
        right: -20px;
        color: var(--gray-500);
    }

    div:nth-child(9) > div:nth-child(1):after{
        content: "一";

    }
    
    div:nth-child(9) > div:nth-child(3):after{
        content: "三";
    }
    
    div:nth-child(9) > div:nth-child(5):after{
        content: "五";
    }
    
    
    div:nth-child(9) > div:nth-child(7):after{
        content: "日";
    }

}

scss样式

JSON数据部分

这边可以根据你的需求生成这部分数据

[
  {
    "id": 1,
    "href": "https://1900.live/pcshi-yong-de-appxin-xi-tong-bu-geng-xin-dao-bo-ke-shang/",
    "date": "2024-05-13T23:00:16.000+08:00",
    "title": "将博主PC上使用的应用信息实时显示到博客",
    "section": "编码,工具箱,分享",
    "published": "2024-05-13T23:00:16.000+08:00",
    "word_count": 13037
  }
  //...Items
]

json数据格式

JS部分

几乎所有代码都是通过ChatGPT生成,相关说明我直接写在代码中。

// 字符串转换为时间格式
function parseDate(str) {
    return new Date(str);
}

// 获取本周的星期天作为开始时间
function getThisSunday(date) {
    const dayOfWeek = date.getDay();
    const daysToSunday = dayOfWeek === 0 ? 0 : 7 - dayOfWeek;
    const thisSunday = new Date(date);
    thisSunday.setDate(thisSunday.getDate() + daysToSunday);
    return thisSunday;
}

const today = new Date();
const sunday = getThisSunday(today);
let startDate = new Date(sunday);
// 往后推63天
startDate.setDate(sunday.getDate() - 62);

// 构建基础数据
function dateBuild(data) {
    const dateCounts = {};
    // 标准化json中的时间格式
    data.forEach((item) => {
        const dateStr = parseDate(item.date).toISOString().split("T")[0];
        dateCounts[dateStr] = (dateCounts[dateStr] || 0) + 1;
    });

    const result = [];
    // 生成63天的数据数组
    for (
        let currentDate = sunday;
        currentDate >= startDate;
        currentDate.setDate(currentDate.getDate() - 1)
    ) {
        const dateStr = currentDate.toISOString().split("T")[0];
        const count = dateCounts[dateStr] || 0;
        // 通过时间去json数据获取当天的文章
        const dataContent = data.filter(
            (item) =>
                parseDate(item.date).toISOString().split("T")[0] === dateStr
        );

        // 放进数组中
        result.push({
            date: dateStr,
            count: count,
            data: dataContent,
        });
    }

    // 统计文章字符总数(好像可以整合进上面的循环中,下次再优化把。
    result.forEach((item) => {
        var sumOfWordcounts = item.data.reduce((accumulator, currentItem) => {
            return (accumulator = currentItem.word_count || 0);
        }, 0);
        item.wordcount = sumOfWordcounts;
    });

    return result;
}

// 填充数据
export default function fillGrid(data) {
    // 先将构建用于渲染的数据
    let articles = dateBuild(data);
    // 获取热力图元素
    const gridContainer = document.getElementById("relitu-container");
    // 构建grid-item元素
    const gridItemTemplate = document.createElement("div");
    gridItemTemplate.className = "grid-item";

    // 倒序遍历文章数据
    articles
        .slice()
        .reverse()
        .forEach((article, index) => {
            const gridItem = gridItemTemplate.cloneNode(false);
            // 构建提示字符串
            const tooltipStr = article.data
                .map(
                    (item, i) =>
                        `- <a href='${item.href}'>${item.title}</a></br>`
                )
                .join(" ");
            // 构建grid-info中的信息
            // 关于小方框颜色部分,我这里直接用的百分比透明度,起步0.2,5000字100%
            gridItem.innerHTML = `<div style="${ article.wordcount != 0 ? `background-color:rgba(77, 208, 90,${article.wordcount / 5000 + 0.2})`:""}" 
            class="item-info ${ article.count != 0 ? `item-tippy" data-tippy-content="共 ${article.count} 篇,共 ${article.wordcount} 字<br />${tooltipStr}"`:'"' } data-date="${article.date}"></div>`;

            // 计算排列顺序
            const colIndex = Math.floor(index / 7);
            const rowIndex = index % 7;

            if (rowIndex === 0) {
                const gridColumn = document.createElement("div");
                gridColumn.className = "grid-column";
                gridContainer.appendChild(gridColumn);
            }
            // 根据顺序构建
            const gridColumns = document.getElementsByClassName("grid-column");
            if (gridColumns[colIndex]) {
                gridColumns[colIndex].append(gridItem);
            } else {
                // 如果列索引超出了当前已有的列,需要创建新的列
                const newColumn = document.createElement("div");
                newColumn.className = "grid-column";
                gridContainer.appendChild(newColumn);
                newColumn.append(gridItem);
            }
        });
}

我这里用的fetch加载的json文件,刚刚测试了一下速度还是没有直接将json生成在html中速度快,下一版做一下改动。

document.addEventListener("DOMContentLoaded", function () {
    fetch("/assets/relitu-data.json")
        .then((respone) => respone.json())
        .then((posts) => {
            // 现在使用dateBuild函数处理数据,并将结果传递给fillGrid函数
            fillGrid(posts);
            tippy(".item-tippy", { allowHTML: true, interactive: true ,maxWidth: 'none',appendTo: () => document.body,});
        });
});

入口函数调用

Enjoy~

❌
❌