阅读视图

发现新文章,点击刷新页面。

我VPS上的Selfhost服务

以前刚玩博客的时候用的 Wordpress 作为博客系统,基本上都搭建在一些互联网的公益服务上,如今还记得就是 wpoak 这个平台,不过这些服务难抵时间漫长,大多因为成本问题关门大吉了。

所以之后开始自己买 VPS 进行博客程序部署,也开始慢慢重视起数据的重要性和自我保有。

刚开始 VPS 还是玩的非常简单,实例化完成后便用 lnmp 等程序一键装好 PHP 环境,然后下载上传 WordPress 安装程序便算完事了,后来接触的多了才知道 VPS 能做的事情元远不止于此。

后来我开始接触到 Docker,然后研究各种 Selfhost 的应用、服务。

昨天在夜未央的「浪费」一文中评论到:「对于很多普通用户来说基本上没有什么选择了,我们是喜欢折腾和了解,知道有什么渠道去找平替」。而后又在 Dayu 的「目前使用的自托管服务」一文中看到了他分享的 Selfhost 清单,所以这里索性也做一次分享,希望也能让更多的人用到这些开源、可控、好用的服务,利用起自己 VPS 上多余的算力资源。

我目前的 VPS 只有一个国内的腾讯轻量云。

配置为 2C2G4M,一年费用大概在 100 元左右,目前购买了 3 年,自托管的服务如下:

Ghost

这是我目前使用的博客后端,Ghost 在国内算是非常小众的博客程序了。

我大概在 16 年开始使用 Ghost 作为博客系统,当时服务部署在百度的 BAE 平台上,「在百度开放云BAE上部署GHOST博客并配置七牛云」,后因服务不能在继续免费白嫖几经搬家,后落到现在的 VPS 上。

可以使用 Docker 或者 Ghost 官方的 CLI 程序安装。在以前的老版本时,数据库支持 SQLite,后来几经更新,现在在正式环境中只能使用 MySQL。

Artalk-Go

在早期时 Ghost 并没有提供评论功能,虽然在后续的迭代中增加了评论功能,但是在我看来不太友好,而且因为评论需要用户登录,其他使用 Ghost 评论功能的小伙伴反应,在迁移过程中需要额外进行会员数据、评论数据处理,所以我一直没有使用。

所以我最开始使用的自部署评论程序是 Twikoo ,但是因为这个项目更新在很长一段时间内停滞,且管理面板、功能过于简陋,几经尝试后换到了 Artalk。

Artalk 是国内大学生大佬开发的一款 Disqus 的第三方评论平替程序,开源,自部署,且功能十分丰富,更新也非常勤快,个人认为是一款非常完善的评论程序。

可以用 Docker 部署。

MySQL

这个不用多说,声名远播的开源数据库,因为 Ghost 和后面其他的一些程序需要用到。

可 Docker 部署。

  • 推荐程度:🌟🌟🌟🌟
  • 部署方式:Docker
  • 是否开源:是
  • 项目地址:MySQL

Vaultwarden

密码管理器我以前使用的是 LastPass 这个免费服务,但是在使用过程中频繁出现网络问题无法使用,而且管理面板没有中文,且某一次还出现了安全漏洞的问题,自那后我就切换到了 Bitwarden,在 PC 上通过浏览器拓展可以快速进行当前网站的密码填充。

但是因为想用密码泄露检测等服务,不过这些功能在官方的 Bitwarden 上都是需要付费使用,所以我开始使用了国外大佬基于 Bitwarden API 重新实现的开源密码管理程序 Vaultwarden,Bitwarden 中一些需要付费使用的服务在 Vaultwarden 上都能免费使用,搭配第三方的安卓端 Keyguard ,可以说颜值与实力并存。

密码管理器我是非常推荐每一个没用过的人使用的。

因为一些第三方网站管理员自身水平良莠不齐,可能会因为服务漏洞导致数据库泄露,此时如果你将常用密码用在注册这些网站上,届时你的其他重要服务被撞库暴力破解的风险将大大提升。

但是如果你使用密码管理器的随机高强度密码,只要在主密码没有泄露的情况下密码的情况下,即便数据库泄露了也不用担心被破解的风险,且因为在注册时使用的随机密码,即便第三方服务的数据库被泄露也不用担心被撞库。

Alist

Alist 是一个非常好用的网盘管理程序,因为太好用、用的人太多了,导致国内一些网盘实在承受不住,甚至不顾脸面将以前承诺的一些服务重新二次收费——没错,说的就是你「阿里网盘」。

Alist 可以在一个程序内管理你的大部分网盘服务,将他们集成在一起统一管理,并且通过 Alist 的一些拓展实现诸如公开分享、WebDav 等等,但是因为用的人实在太多了,而且很多影视程序也跟着上来薅羊毛,把网盘做成资源服务开放给其他人使用,导致这些情况的发生。

但是,即便这样,Alist 也依旧是一款非常好用的网盘管理程序。

Memos

Memos 是个非常好的 Flomo 平替工具,可以方便的进行卡片笔记记录,甚至你可以拿他当作微博、微博客来用也没什么问题,而且作者更新也非常勤快。

但是开发者时常进行 Break Change,我碰到的就有不下三次,导致我实在不敢继续再升级了,在某一次回退后彻底停留在目前使用的 v0.18.1 版本,好在那时的功能已经非常完善,即便不升级也不影响软件的后续使用。

Umami

google 统计的平替服务,之前部署在 Raliway 上,但是因为平台收费政策调整,只给 5 刀的免费额度,导致有一次超出额度,多交了 30 多元的费用后转移到 VPS 上部署。

GoToSocial

一款用 Go 写的联邦宇宙实例工具,长毛象的超小型替代,但是我没有玩的太明白,使用过程中出现很多莫名奇妙的问题,暂时放弃。

miniFlux

一款开源的 RSS 阅读器,简单好用,没有多余的花哨功能。

lx-music-sync

洛雪音乐的同步服务,可以在手机、PC 端之间同步历史记录、收藏等等。

wewe-rss

国内大佬开发的一款微信公众号转 RSS 订阅工具。

Express

我用 Docker 部署了一个 Express 服务,用于博客上一些需要后端的小功能,如点赞、PC 状态同步等。

WatchTower

Docker 镜像更新服务,通过维护一个需要自动更新的 Docker 容器名称列表,实现对对应容器镜像的自动更新。

独立博客自省问卷15题

雅余哥发起的调查,回答一波 

1、你的博客更新频率是多少?

A.每周更新

B.一周数篇:基本上每周都有把。

C.一月1-2篇

D.几个月一篇

 

2、你的博客上次更新是什么时候?

A.本周:有节气这个系列在,基本上很稳定。

B.上周

C.上个月

D.上季度

 

3、你的博客文章是原创的吗?

A.坚持原创:都是自己写的,可能有部分引用的块。

B.部分借鉴

C.AI 帮我写的

D.搬运别人的,而且不署名

 

4、你觉得自己的文章对他人有帮助吗?

A.旨在对他人有启示

B.多少有点意义:技术文章偏多一点,多少应该会有点帮助把。

C.每日每周流水账

D.自我陶醉就好,管他呢

 

5、你上次换博客主题/程序是什么时候?

A.上周

B.上个月:最近才从11ty换到Astro

C.去年

D.凭良心说,我多年都是一个主题

 

6、你上一次捣腾博客主题代码是什么时候?

A.昨天,撸代码到凌晨

B.每周必捣腾:经常会折腾,就像狂热的钓鱼佬一样。

C.每月有那么一次

D.一年有那么一次

 

7、你会对博客主题进行二次开发?

A.直接配置使用,省心不折腾

B.时不时自己改改,搞点新花样,换图片,换字体,爽:自然是这个选项。

C.删除主题作者版权信息,改改样式,然后自我感觉良好

D.改得面目全非,但保留原作者版权信息或注明

 

8、你多久打开自己博客自我陶醉一次?

A.每天数次:基本上每天都会打开,看看评论之类

B.每周一次

C.看心情

D.一般都是照镜子,不看博客

 

9、你近期对自己博客域名什么感受?

A.想搞到一个 .COM 的域名

B.如果域名能再短几个字符就更好了

C.今年才换双拼域名了,明年再看看

D.目前挺好,没想法:没啥想法。

 

10、你每天都会看网站的流量统计吗?

A.每天看几次,今天又多了100PV

B.每周回顾,看看流量趋势

C.记得就看看:偶尔看看。

D.没有搞流量统计,都是浮云

 

11、你通过博客的广告赚到钱了吗?

A.有,能覆盖建站费用

B.有,但付出大于收入

C.没考虑通过博客流量赚钱:暂时没考虑

D.拒绝广告,保证阅读体验

 

12、你去浏览别人的博客/网站主要为什么?

A.学习别人分享的知识:友链多是生活型博客,看看别人活的咋样。

B.搬运别人的内容

C.看看别人怎么装修博客,自己也抄一下,感觉都比自己的好

D.不爱看别人博客,自己爱写啥写啥

 

13、看到别人分享了一篇文章,你打开第一反应是什么?

A.哇,这域名真不错,怎么我没想到

B.哇,这网站速度真快,图片延迟加载丝滑

C.哇,这程序/主题不错,我也要抄一抄/留言问问哪里搞的

D.看看文章内容:因为打开就会看到设计,所以一般都是先看到设计再看到内容。但是我认为内容更重要。

 

14、你觉得博客哪方面更重要?

A.域名

B.服务器

C.主题

D.内容:上一条回答了。

 

15、近期通过写博客有哪些新收获?

A.知识面有拓展

B.认识了新朋友

C.写作水平提升

D.通过知识变现

基本上都有。

我的miniFlux主题

最近Follow大火,虽然也在使用,不过我还是更喜欢miniFlux这种简单的阅读器。

之前从 yarr 切换到 miniflux 是因为 木木 大佬推荐的 miniflux 主题 Miniflux-Theme-Reeder,不过当时用了一段时间后发现各种不适,最后还是切换会默认主题,用了一段时间发现其实默认的miniflux已经非常好用了,不过需要做一点美化,所以简单做了一下调整,这里分享一下我的miniflux主题。

项目地址:https://github.com/rebron1900/mini-miniflux

主题就叫 mini-miniflux ,复制项目中 theme.css 文件中的样式或直接复制下面的css到miniflux中的 custom css 中即可。

:root {
    --font-family: "Noto Serif SC", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --body-color: #efefef;
    --body-background: #222;
    --hr-border-color: #555;
    --title-color: #aaa;
    --link-color: #aaa;
    --link-focus-color: #ddd;
    --link-hover-color: #ddd;
    --link-visited-color: #f083e4;
    --header-list-border-color: #333;
    --header-link-color: #ddd;
    --header-link-focus-color: rgba(82, 168, 236, 0.85);
    --header-link-hover-color: rgba(82, 168, 236, 0.85);
    --header-active-link-color: #9b9494;
    --page-header-title-color: #aaa;
    --page-header-title-border-color: #333;
    --logo-color: #bbb;
    --logo-hover-color-span: #bbb;
    --table-border-color: #555;
    --table-th-background: #333;
    --table-th-color: #aaa;
    --table-tr-hover-background-color: #333;
    --table-tr-hover-color: #aaa;
    --button-primary-border-color: #444;
    --button-primary-background: #333;
    --button-primary-color: #efefef;
    --button-primary-focus-border-color: #888;
    --button-primary-focus-background: #555;
    --input-border: 1px solid #555;
    --input-background: #333;
    --input-color: #ccc;
    --input-placeholder-color: #666;
    --input-focus-color: #efefef;
    --input-focus-border-color: rgba(82, 168, 236, 0.8);
    --input-focus-box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
    --alert-color: #efefef;
    --alert-background-color: #333;
    --alert-border-color: #444;
    --alert-success-color: #efefef;
    --alert-success-background-color: #333;
    --alert-success-border-color: #444;
    --alert-error-color: #efefef;
    --alert-error-background-color: #333;
    --alert-error-border-color: #444;
    --alert-info-color: #efefef;
    --alert-info-background-color: #333;
    --alert-info-border-color: #444;
    --panel-background: #333;
    --panel-border-color: #555;
    --panel-color: #9b9b9b;
    --modal-background: #333;
    --modal-color: #efefef;
    --modal-box-shadow: 0 0 10px rgba(82, 168, 236, 0.6);
    --pagination-link-color: #aaa;
    --pagination-border-color: #333;
    --category-color: #efefef;
    --category-background-color: #333;
    --category-border-color: #444;
    --category-link-color: #999;
    --category-link-hover-color: #aaa;
    --item-border-color: #666;
    --item-padding: 15px;
    --item-title-link-font-weight: 900;
    --item-status-read-title-link-color: #666;
    --item-status-read-title-focus-color: rgba(82, 168, 236, 0.6);
    --item-meta-focus-color: #aaa;
    --item-meta-li-color: #ddd;
    --current-item-border-width: 2px;
    --current-item-border-color: rgba(82, 168, 236, 0.8);
    --current-item-box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
    --entry-header-border-color: #333;
    --entry-header-title-link-color: #bbb;
    --entry-content-color: #999;
    --entry-content-code-color: #fff;
    --entry-content-code-background: #555;
    --entry-content-code-border-color: #888;
    --entry-content-quote-color: #777;
    --entry-content-abbr-border-color: #777;
    --entry-enclosure-border-color: #333;
    --parsing-error-color: #eee;
    --feed-parsing-error-background-color: #3a1515;
    --feed-parsing-error-border-style: solid;
    --feed-parsing-error-border-color: #562222;
    --feed-has-unread-background-color: #1b1a1a;
    --feed-has-unread-border-style: solid;
    --feed-has-unread-border-color: rgb(33 57 76);
    --category-has-unread-background-color: #1b1a1a;
    --category-has-unread-border-style: solid;
    --category-has-unread-border-color: rgb(33 57 76);
    --keyboard-shortcuts-li-color: #9b9b9b;
    --counter-color: #bbb --entry-content-font-weight: 300;
    --entry-content-font-family: "Noto Serif SC", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --entry-content-quote-font-family: var(--entry-content-font-family) --gray-100: rgba(196, 196, 196, 0.1);
    --gray-200: rgba(196, 196, 196, 0.2);
    --gray-500: rgba(196, 196, 196, 0.5);
    --gray-700: rgba(196, 196, 196, 0.7);

    --color-link: #84b2ff;
    --color-visited-link: #84b2ff;

    --body-font-color: #e9ecef;
    --color-hover-link: #589ff2;

    --btn-color: red;
    --btn-bg: blue;

    --icon-filter: brightness(0) invert(1);

    --box-radius: 0.25rem;

    --hint-color-info: #6bf;
    --hint-color-warning: #fd6;
    --hint-color-danger: #f66;
}

* {
    font-family: var(--entry-content-font-family);
    text-shadow: 0 0 .75px var(--gray-500);
}


main {
    .item {
        border-radius: var(--box-radius);
    }
}

@media (max-width: 630px) {
    body {
        padding: 0 1.25rem;
    }
    
    .page-header{
        margin-top: 1rem;
    }

    .header {
        position: fixed;
        bottom: 10px;
        right: 10px;
        #header-menu {
            background-color: var(--alert-border-color);
            padding: 10px;
            border-radius: var(--box-radius);
            list-style: none;
        }
    }
}
.entry-content {
    line-height: 1.8;


    > :first-child {
        margin-top: 0;
    }

    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
        font-weight: bolder;
        line-height: 1;
        margin-top: 1.5em;
        margin-bottom: 1rem;

        a.anchor {
            opacity: 0;
            font-size: 0.75em;
            vertical-align: middle;
            text-decoration: none;
        }

        &:hover a.anchor,
        a.anchor:focus {
            opacity: initial;
        }
    }

    h4,
    h5,
    h6 {
        font-weight: bolder;
    }

    h5 {
        font-size: 0.875em;
    }

    h6 {
        font-size: 0.75em;
    }

    b,
    optgroup,
    strong {
        font-weight: bolder;
    }

    a {
        color: var(--color-link);
        text-underline-offset: 0.3em;

        &:hover {
            color: var(--color-hover-link);
            text-shadow: 0 0 0.75px var(--color-hover-link);
        }
        &:visited {
            color: var(--color-link);
        }
    }

    img {
        max-width: 100%;
        height: auto;
        border-radius: 0.25rem;
    }

    code {
        padding: 0 0.25rem;
        background: var(--gray-200);
        border-radius: 0.25rem;
        font-size: 0.875em;
    }

    pre {
        padding: 1rem;
        background: var(--gray-200);
        border-radius: 0.25rem;
        overflow-x: auto;
        position: relative;

        code {
            padding: 0;
            background: none;
            color: white;
        }
    }

    p {
        word-wrap: break-word;
        margin-bottom: 1.3rem;
    }

    /** adapter ghost callout cards **/
    blockquote,
    .kg-callout-card {
        margin: 1rem 0;
        padding: 0.5rem 1rem 0.5rem (1rem - 0.25rem);

        border-inline-start: 0.25rem solid var(--gray-200);
        border-radius: 0.25rem;

        color: var(--gray-700);
        font-style: italic;

        word-break: break-all;

        :first-child {
            margin-top: 0;
        }
        :last-child {
            margin-bottom: 0;
        }
    }

    table {
        overflow: auto;
        display: block;
        border-spacing: 0;
        border-collapse: collapse;
        margin-top: 1rem;
        margin-bottom: 1rem;

        tr th,
        tr td {
            padding: 0.5rem 1rem;
            border: $padding-1 solid var(--gray-200);
        }

        tr:nth-child(2n) {
            background: var(--gray-100);
        }
    }

    hr {
        height: 1px;
        border: none;
        background: var(--gray-200);
    }

    ul,
    ol {
        padding-inline-start: 2rem;
        word-wrap: break-word;
    }

    dl {
        dt {
            font-weight: bolder;
            margin-top: 1rem;
        }

        dd {
            margin-inline-start: 0;
            margin-bottom: 1rem;
        }
    }

    .highlight table tr {
        td:nth-child(1) pre {
            margin: 0;
            padding-inline-end: 0;
        }
        td:nth-child(2) pre {
            margin: 0;
            padding-inline-start: 0;
        }
    }

    details {
        padding: 1rem;
        border: $padding-1 solid var(--gray-200);
        border-radius: 0.25rem;

        summary {
            line-height: 1;
            padding: 1rem;
            margin: -1rem;
            cursor: pointer;
        }

        &[open] summary {
            margin-bottom: 0;
        }
    }

    figure {
        margin: 1rem 0;
        figcaption p {
            margin-top: 0;
        }
    }
}

MacType正确注册表全局热替换、渲染尝试

前言

因为Windows系统字体渲染太垃圾,而我又没需求换4k,所以需要用到其他工具对字体进行渲染。而MacType自然是Windows平台的不二选择,我使用的时间估计已经有10年了把?

以前在XP、Win7上都能很完美的渲染,但是Windows10开始陆续出现了一些程序渲染不上的问题,

  • 比如大部分UWP程序
  • 而Chrome也因为安全策略更改也无法正常进行渲染,虽然百分浏览器有使用GDI++的版本,但是所有Emoji会变成黑白色
  • 微信、企业微信 也因为使用了WebView也无法正常渲染

期间在MacType的仓库里发了一个求助issue 为何微信中FontSubstitutes替换雅黑后字体会直接失去所有渲染呢,有回答的人说通过注册表模式可以正常渲染,但是我进行了尝试后死活没有效果,Maple-font的作者也遇到了和我同样的问题 Segoe UI Variable 似乎无法替换

不过昨天因为 Windows11 LTSC发布了,我重新安装系统后对MacType进行了尝试,发现似乎是因为火绒的问题导致无法进行渲染 火绒 6.0 导致 MacType Version 2024.8.15 无法渲染 ,在进行了很多次尝试后发现不单纯是因为火绒的问题,即便在开了注册表模式下有些配置文件依旧无法正常渲染、替换,尝试了很久终于总结了一套对于我来说可行的全局渲染、热替换方案。

方案

MacType安装到C盘下

根据Snow大佬的解释,因为Windows11系统安全的原因在其他目录可能无法加载dll,但是C盘的 Windows 目录和 Program Files 目录除外,所以我们要将程序装在 Program Files 中。

启用火绒或开启白名单

火绒系统加固里在「自动处理」中给下面几个程序开启「关键进程规则」下所有选项,「文件规则」下的「系统任务目录」

启用注册表模式

因为Windows11安全引导的问题,新版本的MacType取消了在设置向导中开启注册表模式的选项,但是可以通过手动导入、设置注册表的形式启用。

  1. 关闭BIOS中的安全引导(Secure boot)选项。
  2. 在MacType的设置向导中设置为「不使用自动加载」
  3. 导入注册表
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows]
"AppInit_DLLs"="MacType64.dll"
"LoadAppInit_DLLs"=dword:00000001
"RequireSignedAppInit_DLLs"=dword:00000000


[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows]
"AppInit_DLLs"="MacType.dll"
"LoadAppInit_DLLs"=dword:00000001
"RequireSignedAppInit_DLLs"=dword:00000000

配置文件

我之前一直用的自己改的一个配置文件,但是这个配置文件之前我用MacType服务模式时也有一些莫名奇妙的BUG,比如微信中输入框里的字体无法渲染。

因为喜欢比较厚重的字体,这次测试我首先使用MacType服务模式对所有的配置文件进行测试,找出一个渲染风格是我喜欢的配置文件,最后选中了 XMac.LCD.Default.ini 这个配置,然后开启MacType注册表模式不断的修改配置、重启进行测试,最终找到了一个能正常渲染的配置。

在这个配置文件中我只将 Segoe UI Variable 字体热替换成了 Maple UI ,这部分可以根据使用需求进行任意热替换。

; Only for MacType (NOT SUPPORT THE OLD VERSION OF GDI++/HE)
; maxchow@qq.com

[Preview]
Font=Tahoma
Color=$990088
Text=卍 XMac.LCD 默认设置
Size=12
Align=Center

[General]
Name=XMac.LCD.Default
Icon=XMac\XMac.ico,0

;【自动挂钩子进程】
;[0:Disable]  1:Enable
HookChildProcesses=1

;【字体微调】
;0:Normal 1:NoHinting 2:AutoHinting 3:Light+AutoHinting
HintingMode=1

;【抗锯齿方式】
;-1:Disable 0:Normal 1:Light 2:LCD(RGB) 3:LCD(GBR) 4:Light-LCD(RGB) 5:Light-LCD(GBR)
AntiAliasMode=2

;【常规体调整】
;-32:+32
NormalWeight=14

;【粗体字的调整】
;-16:+16
BoldWeight=2

;【斜体字的倾斜角度微调】
;-16:+16
ItalicSlant=0

;【只有在FontLoader=0(在下面)时此项设置才有效。建议选0,选1会占用大量内存,也没看出有什么好处。】
;0:Disable 1:Enable
UseMapping=0

;【Gamma模式开关】
;-1:关闭;0:使用设置的Gamma值(GammaValue);1:使用sRGB的Gamma值;2:不明
GammaMode=0

;【Gamma值】
;GammaMode=0的时候此项设置才有效
;sRGB ≒ 2.2 CT-Default = 1.4
;windows的cleartype的Gamma值是1.4
GammaValue=1.4

;【对比度】
;数字越大越锐利,数字越小越发虚
Contrast=1.0

;【字体轮廓】
;数字越大字体就越粗越黑
RenderWeight=1.0

;【文字边界设置】
;数字越大越强调背景(类似文字边界被侵蚀的感觉),强调背景会有中字体锐利的感觉。适当降低对比度,提高TextTuning,可以使文字周围的颜色看起来“淡一点”
;再加上RenderWeight,把这三个设置的微妙平衡调节好的话,能得到不输给Mac的效果
;0:12

;AntiAliasMode=1时,通过TextTuning分量来调节
TextTuning=0

;AntiAliasMode=2时,通过TextTuning的RGB分量来调节
TextTuningR=0
TextTuningG=0
TextTuningB=0

;【字体加粗模式】
;对NormalWeight和BoldWeight有效
;0:Weight值大时横向加粗,值小时双向加粗,主要是避免Weight值过大时,模式3中在小字号时加粗过度出现斩头的现象;
;1:总是横向加粗;
;2:总是双向加粗,也就是freetype本身的粗体渲染。
;注意:通过加大GammaValue、TextTuning的值,可以减少Weight值过大的造成的效果问题,即可以在BolderMode=2时也可以避免转接头
BolderMode=0

;【字体的载入方法】
;(注意是载入方法,不是渲染方法)
;0:用freetype载入字体
;1:用windows的绘图核心载入字体
;很多freetype的设置需要此项选0才能生效
FontLoader=0

;【字体链接】
;当FontLoader=0时
;0:什么都不做;
;1:使用注册表里的字体链接设置,通过FreeType的字符表进行查找
;2:使用注册表里的字体链接设置,使用Windows的转换函数直接查找
;当FontLoader=1时
;会保持启用的状态,但是所有字体链接的处理会交由WIN32API完成。
FontLink=2

;【字体替代】
;当FontLoader=0时
;0:什么都不做;
;1:安全替换方案(兼容性较好),根据以下FontSubstitutes的配置进行替换;
;2:完全替换方案(效果较全面,如替换后出现乱码,请尝试使用1),根据以下FontSubstitutes的配置进行替换;
;当FontLoader=1时
;会保持启用的状态,但是所有字体替代的处理会交由WIN32API完成
FontSubstitutes=2

;【让freetype处理的最大的字体尺寸,单位是像素,0的话就是全尺寸】
;交付 gdi++ 处理的最大尺寸(单位为像素)
;0-2147483647
MaxHeight=0

;【阴影设置】
;格式:水平偏移,垂直偏移,深色文字的阴影透明度(0-100),深色文字的阴影颜色,浅色文字的阴影透明度(0-100),浅色文字的阴影颜色
;颜色值格式:BBGGRR 
;Shadow=1,1,30,FFFFFF,20,000000

;【液晶显示器的优化配置】
;[0:None]  1:Default  2:Light  16:Legacy
LcdFilter=2

;【微调LcdFilter】
;LcdFilter>0,且AntiAliasMode>-1时,该微调才生效
;这5个值分别代表文字笔划中从左到右的笔划浓度,最小为0,最大为255
;注释掉这个参数则恢复使用
;LcdFilter=1,默认值为“16,64,112,64,16”
;LcdFilter=2,默认值为“0,85,86,85,0”
;LcdFilterWeight=20,80,120,80,20

;【字体缓存、内存的设置】
LoadOnDemand=1
CacheMaxFaces=256
CacheMaxSizes=33554432
CacheMaxBytes=67108864
EnableKerning=0

;【强制使用某一个字体】
;无视所有其他字体,全系统的字体都会被这里设置的字体代替
;ForceChangeFont=华文中宋

;【优先使用点阵】
;小于指定字号的文字将优先使用内嵌点阵
MaxBitmap=0

; DirectWrite 支持;DirectWrite support
DirectWrite=1

[DirectWrite]
;渲染模式,0=默认,1=锯齿,2=经典,3=经典自然,4=自然,5=自然对称,6=字体原型
RenderingMode=6
;Gamma值,不写则根据上面的GammaValue自动计算
GammaValue=1.5
;对比度,默认为1
Contrast=1.0
;抗锯齿程度,默认1
ClearTypeLevel=1

[Individual]
;【单独设置的字体】
;格式=Hinting, AAMode, NormalWeight, BoldWeight, ItalicSlant, Kerning
XSong=,,28,0,,
XSong SimSun=,,28,0,,
XSong NSimSun=,,28,0,,
XSong Sharp=,,28,0,,
XSong Sharp.SimSun=,,28,0,,
XSong Sharp.NSimSun=,,28,0,,
XSong Harmony=,,28,0,,
XSong Harmony.SimSun=,,28,0,,
XSong Harmony.NSimSun=,,28,0,,
XSong Classical=,,28,0,,
XSong Classical.SimSun=,,28,0,,
XSong Classical.NSimSun=,,28,0,,
XSong Tradition=,,28,0,,
XSong Tradition.SimSun=,,28,0,,
XSong Tradition.NSimSun=,,28,0,,

宋体=,,28,0,,
新宋体=,,28,0,,
SimSun-ExtB=,,28,0,,
仿宋_GB2312=,,28,0,,
仿宋=,,28,0,,
华文仿宋=,,28,0,,
华文宋体=,,28,0,,
华文中宋=,,28,0,,
Batang=,,28,0,,
MingLiU=,,28,0,,
MingLiU-ExtB=,,28,0,,
PMingLiU=,,28,0,,
PMingLiU-ExtB=,,28,0,,
PMingLiU_HKSCS=,,28,0,,
PMingLiU_HKSCS-ExtB=,,28,0,,
MS Mincho=,,28,0,,
MS PMincho=,,28,0,,

[Exclude]
;【除外的字体】

[ExcludeModule]
;【不渲染的程序,但仍会加载DLL】

[ExcludeSub]
;【排除不替换字体的程序】
;以下程序不会被进行字体热替换
;===== Office =====
;EXCEL.EXE
;POWERPNT.EXE
;WINWORD.EXE

[UnloadDll]
;【不渲染的程序,同时完全不加载DLL】
fontview.exe
MainType.exe
AxureRP.exe
FontCreator.exe
FontCreator.cn.exe
notecaselauncher.exe
notecase.exe
gnucash-bin.exe
bleachbit.exe
Fontforge_TC.exe
Fontforge_SC.exe
Fontforge_EN.exe
NexusFont.exe
dwm.exe

[FontSubstitutes@firefox.exe]
;【字体替代】
;系统在调用“=”前面的字体时会忽视“=”前面的字体,而去直接调用“=”后面的字体
;Fixedsys=XHei NSimSun
SimSun=XHei SimSun
@SimSun=@XHei SimSun
NSimSun=XHei NSimSun
@NSimSun=@XHei NSimSun

[FontSubstitutes]
;【字体替代】
;系统在调用“=”前面的字体时会忽视“=”前面的字体,而去直接调用“=”后面的字体
Segoe UI Variable=Maple UI
Segoe UI Variable Small Light=Maple UI
Segoe UI Variable Small Semilight=Maple UI
Segoe UI Variable Small=Maple UI
Segoe UI Variable Small Semibold=Maple UI
Segoe UI Variable Small Bold=Maple UI
Segoe UI Variable Text Light=Maple UI
Segoe UI Variable Text Semilight=Maple UI
Segoe UI Variable Text=Maple UI
Segoe UI Variable Text Semibold=Maple UI
Segoe UI Variable Text Bold=Maple UI
Segoe UI Variable Display Light=Maple UI
Segoe UI Variable Display Semilight=Maple UI
Segoe UI Variable Display=Maple UI
Segoe UI Variable Display Semibold=Maple UI
Segoe UI Variable Display Bold=Maple UI
Segoe UI=Maple UI
Segoe UI Semilight=Maple UI
Segoe UI Semibold=Maple UI
Segoe UI Black=Maple UI
Segoe UI Light=Maple UI

沉默的霸凌施暴者们

💡
本文包含部分关键剧情剧透,请谨慎阅读。
每个人都是施暴者

等了这么久,默杀终于上流媒体平台了。

恰逢假期,家里也来了客人,中午吃了饭便和家人们一起认认真真看看完了这部电影。

不似一些其他电影对内在观点输出的含蓄,默杀这部电影则是非常直给。而标题「沉默的霸凌施暴者们」是我总结的默杀这部片子导演想传达的观点。是呀,「默杀」这个名字和电影封面上每个人的状态不正是最好的阐述吗,同时电影中非常多的直给镜头都在赤裸裸展现。

施暴者

在影片的前半部分间断呈现的「霸凌」画面中,我们能看到所有相关人员的冷漠旁观,而他们都是这场悲剧的酿造者。

首先那几个施暴的女同学自是不必说,罄竹难书,说她们是未进化的动物动也不足为过,其次从校长,普通的同学,校园保安,方老师,房东,这些人在事件发生之后的选择及冷漠更是成了帮凶。

在看这部影片的时候我的姨妈恰好讲了一个这样的故事,

她在商场经营了几家服装店,这几家服装店都处于商场内,而门店旁边都是另外的老板开的娃娃机店,因为现在的娃娃机店都是自助式,没有营业员,所以平时需要有人打扫卫生,整理娃娃。我姨妈便和对方商量了,说承包下他们的这些工作,每天负责打扫的人给于50元补贴,其实我小姨的想法是看工作简单,想给员工捞些外快,她一分钱也没有赚。

因为工作其实非常简单,每天去一次,简单扫一下地,重新补充一下空掉的娃娃机就好了,其中一个门店有一个姐姐认为的确很简单,所以每天都积极的干这个工作,这也影响了同门店的其她同事,最终大家都抢着做这项工作。而另外一个门店有人则觉得工钱太少,工作很麻烦,导致没有一个人愿意去做,然后那个店的承包工作只能无奈放弃了。

这个小故事中两个门店所表现的选择不恰好贴合了默杀这部电影的故事内核吗?

如果故事中的这些人,能和第一个门店中的姐姐们那样,这些人中哪怕只有一个人能放下冷漠、事不关己的心态做出正确的选择,这个故事的结尾至少都不会这么悲剧。

因果

其次是中国儒释道三家观点中时常提到的「因果」。

在这个短短的故事中,所有人都在种下自己的因,也都纷纷吃下了由这个因得出的不同程度的「果」。

  • 施暴的女学生种下霸凌的因,得到了被在福报复杀害的「恶果」。
  • 校长、钟晓晴、李涵、警察戴国栋种下教育无方的因,得到了失去女儿、儿子变成偷拍狂的果。
  • 女房东种下事不关己的因,得到被挟持,绑架的果。
  • 方老师种下谅解、「她们还能被救赎的因」,得到被挟持、伤害的果。
  • 陈明章强奸9岁女儿、家暴老婆,得到被女杀死的「恶果」。
  • 在福杀复仇,得到付出性命的果。
另外小涵最后在监狱的画面我认为是「中国电影审核哲学」的影响下造成的,不纳入这个清单里,我更相信的是小涵已经逃离。

白鸽

电影中多次出现了白鸽,我个人认为这是影片对正义、正确的具象化。

最后在天台上,林在福完成了自己最后的使命,看着这起事件的所有相关人员围在一起,他们好似忽然将自己曾经的冷漠抛的无影无踪,背靠着无可匹敌的正义对着他口诛笔伐。

在福露出了嘲讽的微笑,转身没有丝毫犹豫的跳下了高楼,坠落的半空中,数十只白鸽飞起,好似托着林在福一般,而这在传统港片中只有在正义的英雄出场才会有白鸽飞舞的场景。

而这里,是为 英雄 来福送别。

司法体系的缺失让这一切发生,所以在福已经不相信法律,也认为自己的作为没有必要受到法律制裁,他所做一切都是发自内心,对于夺取那几个女孩子的性命心中自是也有愧疚,所有源自存从自己内心的价值观以死偿命,但是他不愿接受这已经畸形的司法体系的审判,和这些施暴者们的口诛笔伐,所以遵从自己真正的正确,自我了结。

最后

校园霸凌是全球所有学校都存在的普遍现象。

我可以肯定在所有学校,至少我的小、初、高、大的学业生涯中全都有。所有班级中都有那么一两个弱势者会或多或少的被班里的坏孩子、强势者欺凌,虽然大多数情况下并没有影片中表达的这么极端,但是对那些孩子所造成的伤害依旧是不可磨灭的。

我们作为这些旁观之人,如果都能伸出援手,这些悲剧可能就不会发生了,至少不会让一切都变得没有余地...。

想起电影「第二十条」中的一句台词:「所有正确的事情都是有代价的,但不能因为有代价就不去做。」 

愿世上处处都有白鸽飞舞...。

强心针

在写完 馄饨、饺子和抄手 一文后发现回忆是真的很美好。

人们总说要向前看,向前看,未来会更加美好!所以总是回忆过去是不是说明当下过的并不好呢?

虽然这个论点并不充分,但是至少我这段时间想把自己归在这类情况中去。

人们在越是在困苦、迷茫、无助的时刻越是想下意识通过这些美好滋润干涸的心河,引导我们行进的方向,人在不断的进化过程中演化出了这种能力,虽然这种做法在有些人看来确实有些「鸵鸟效应」、「掩耳盗铃」,不过对我来说却也不全是望梅止渴的行为。

中国有句俗语「人生不如意十之八九」这个说法,好在还是有这「一、二」可以追求,因为任何事情都没有绝对的说法,他就像没开盒的「猫」,我们得给自己留一条退路,很多极端的人正是因为把这最后一条路也断了,而做出后悔一辈子的决定。

当然,我们也不能一直沉静在这些记忆里不出来。

毕竟和强心针一样,回忆所带来的美好是有时效的,在那个激励后的短暂余韵里,也是我们该重新迈步向前的时候。

然后,努力生活。

为了下一个「可能」的止步不前,生产另一支强心针。

两款CMS系统体验

因为Ghost只提供文章的 HTML 代码,不提供 Markdown 的问题导致我曾经一度想更换CMS系统。但是将近800篇文章的转移工作十分麻烦,而且之前也尝试过用一些工具处理,无一都失败了,问题多是因为古早之前的很多文章存在格式问题,无法通过一些 HTML to Markdown 工具的转换规则,所以这个事也就一直没下定决定弄。

不过不影响我寻找下一位有动力让我这么做的CMS继任者。

最近发现了两款CMS工具,分别是:

  • KeyStatic: 之前有在周报 [[🤣JUSTFUN 周刊:第13期]] 中推荐过,一款基于Github的CMS系统。
  • PocketBase:一款用Go写的非常轻量、高速的CMS工具,使用简单,扩展性强。

KeyStatic

基于Github的CMS系统有很多,我所知道的就不下四五款,比如有: statamic/cmskeystonejs/keystone 等。

在周报里我也提到过,我是在Astro的CMS推荐列表中看到了KeyStatic,并在昨天花了一点时间体验了一下,整体体验还是不错的,总结如下。

  • 字段:可以进行丰富的字段配置和自定义,但是字段配置需要通过配置文件进行,而且可选项有点多,初上手会比较麻烦,好在官方提供了一些模板库,可以直接超。
  • 同步:可以基于本地也可以基于Github,基于Github模式的话每次增加文件文章后需要手动提交pull request,在页面上有个按钮,点击能跳转到Github,然后再进行后续操作,其实还是比较麻烦的。
  • 部署:官方提供了一些Astro、Next.js等集成,可以将网站、管理面板都部署在Vercel、Netlify、Clouflare上,对白嫖党来说是还是比较友好的。而且因为集成了SSG工具,网站也能同一时间进行更新发布。
  • 文件:
    • 文章:撰写的文章会在项目里生成对应的markdown文件
    • 图片:也会传到Github的内,官方目前提供了一个云服务,后续应该也是会支持S3、R2等服务的。
  • 定制化:后台的定制性非常强,你甚至可以在字段设置里使用你写的Astro组件,不过需要一定的开发知识。
  • API:很丰富,还提供了Nodejs的api库,操作非常方便。
💡
昨天在部署到Vercel后提示链接不安全,在文档里找了半天没发现有相关的说明,后来想起在初始化链接到GitHub时提示了需要设置一个发布网址,当时留空了,所以重新去Github中的App Setting里增加了Vercel链接的Rule后解决。

Poketbase

现在的CMS系统都设计都好强悍呀!昨天刷到蜗牛大佬Star了一个叫 pocketbase 的开源内容管理系统,程序是用Go写的,基于SQLite数据。

管理全在网页上进行,虽然结构简单,但是API、权限、功能拓展什么的都一应俱全,相当哇撒了。

  • 字段:官方提供的字段略少,而且Content内容目前只能保存解析好后的HTML字符串,而且没有自动生成slug。
  • 同步:因为是通过二进制程序直接运行,也有其他开发者提供了Docker版本,就是一个在线服务,所以没有数据同步问题,所以这部分会比较简单,好管理一些。
  • 部署:同上,直接起个Docker就行了,但是并没有集成前端框架,不过有完善的API、权限管理,在SSG中实现一下就好。
  • 文件: 文件可以存在本地,也支持上传到R2等服务上。
  • 定制化:这部分可能会比KeyStatic弱很多,目前文档里没怎么有这方面的介绍
  • API:很完善,接口权限划分的很细。

因为官方没有自带slug生成的功能,所以我跟着官方文档写了个转换脚本,很有意思。

onRecordBeforeUpdateRequest((e) => {
    const pinyin = require(`pinyin`);

    const title = e.record.get("title");
    const titleToSlug = (title) => {
        const pinyinTitle = pinyin(title, {
            style: pinyin.STYLE_NORMAL, // 普通风格
            heteronym: false, // 不使用多音字
        })
            .flat()
            .join("-"); // 将拼音数组转换为字符串并用短横线连接

        return pinyinTitle
            .trim() // 去除首尾空格
            .replace(/[\s]+/g, "-") // 将空格替换为短横线
            .replace(/[^\w\-]+/g, "") // 删除非字母数字和非短横线字符
            .replace(/--+/g, "-") // 替换多个短横线为一个短横线
            .replace(/^-+|-+$/g, ""); // 去除首尾的短横线
    };

    // 示例用法
    const slug = titleToSlug(title);
    e.record.set("slug", slug);
}, "posts");

官方提供的event hook也相当多,能玩出非常多的花样。

目前唯一的疑惑是为啥不提供markdown格式的fields呀?

至于换不换CMS,还是准备再蹲蹲。

如何快速解析HTML里的WikiLink?

前几天在 你好Astro! 中提到想把Obsidian的文章集成在博客中来,并实现CMS和Obsidian之间通过Wikilink的互相引用。

不过因为Ghost的API只提供了文章的HTML字符串,Remark衍生的WikiLink插件肯定都是无法使用的,而且目前Github上能找到的WikiLink相关的案例基本上都是整对于Markdown语法去解析的,对于我来说基本上没什么参考价值。

所以我只能走另外的路子了,不过方法无非就是以下几种

  • 正则过滤替换
  • 生成虚拟DOM处理

正则过滤

昨天晚上我倒是找到了 loveminimal 大佬写的一个姑且能用方案,但是这段代码有个不大不小的缺陷,就是正则会将所有标签内的 [[]] 内容都进行解析转换,所以 pre 代码块内的内容也会被解析成Wikilink,这意味着会无法控制的出现在任何你可能不想他出现的地方,所以目前这种方案暂时待选。

const wikilinks = (innerHtml) => {
    let _innerHtml = innerHtml;
    if (_innerHtml.indexOf('[[') > -1) {
        let _re = /!\[\[(([\/\-\.\*\$\&]|\w|\s|[^\x00-\xff])*\.\w+)\s*\|?\s*(\d*)\]\]/g;
        let _str = _innerHtml.replace(_re, '<img src="/$1" alt="$1" width="$3" />');


        // 2. 后匹配替换链接
        let _reLink = /\[\[(([\/\-\.\*\$\:\#]|\w|\s|[^\x00-\xff])*)\|?(([\/\-\.\*\$]|\w|\s|[^\x00-\xff])*)\]\]/g;
        // let _strLink = _str.match(_reLink);
        // let _strLink = _str.replace(_reLink, '<a href="$1">$3</a>');
        let _strLink = _str.replace(_reLink, (val) => {
            val = val.replace(/[\[\]]/g, '');
            let _arr = val.split(/\s*\|\s*/);
            let _relLink = _arr[0];
            let _desc = _arr[1] ? _arr[1] : _arr[0];

            // 检查链接描述是否包含 #锚点,形式有(我们假设当前文章名称为 test ,它有一个章节 ttt):
            // - 2.1. 孙子兵法#军争篇 - 此类可以正常识别
            // - 2.2. cpu-是如何制造出来的#18.-等级测试 - https://example.com/cpu-是如何制造出来的#18.-等级测试 ,
            //        此类锚点中包含特殊符号 `.` ,在新标签中打开,且无法正确定位到锚点
            // - 2.3 test#ttt - https://example.com/test#ttt 默认会在新标签页中打开,需要优化为在当前页面滚动
            // - 2.4  #ttt - 不能正常,会翻译为 https://example.com/#ttt ,丢失了当前页面路径
            let _idx = _desc.indexOf('#');
            if (_idx > -1) {
                // 2.4
                if (_idx == 0) {
                    _relLink = location.pathname.slice(1) + _desc;
                } else {
                    // 2.3
                    _relLink = _desc.replace('#', '/#');

                    // 2.2
                    _relLink = _relLink.replace(/[\.\、]/g, '');
                }
            }

            // console.log(_arr);
            // console.log(_desc);
            // return `<a href="${_arr[0]}">${_desc}</a>`
            return `<a href="/${_relLink.replace(/\s/g, '-').toLowerCase()}">${_desc}</a>`;
            // });
        });

        return _strLink;
    }
};

虚拟化DOM

这个方式就是在Nodejs里将HTML字符串虚拟化成DOM,再去对DOM进行操作,虽然理论上可行的,但是效率应该是极低的,这个我是不愿意使用的,所以直接排除掉了。

没有其他方案了吗?

真的没有其他方案了吗?

我今天这样想着,抱着试一试的心态换着各种关键字在Github上搜索,运气不错的让我找到了这个个库:html-parse-stringify ,他可以将html字符串快速抽象成一个AST语法树,我的想法是通过遍历这个语法树来找出文本内容,再反序列化成html字符串不就好了?

所以我试着按着我的思路写了个Demo,好像的确可行,而且还可以配置那些情况下包含的 [[]] 不进行渲染。


import HTML from 'html-parse-stringify';
var ast = HTML.parse(page?.html); // [!code highlight]
let fatherList = '';
function findTextNodes(node, text) {
    let result = [];
    fatherList += text; // 记录节点路径

    // 检查当前节点,并做wikilink的识别和路径排除,这里做排除名单应该很方便
    if (node.type === 'text' && node.content.startsWith('[[') && !fatherList.endsWith('precode'))   // [!code highlight]{
        node.content = node.content.replace('[[', '<a>test').replace(']]', '</a>');
        result.push(node);
    }

    // 如果有子节点,递归遍历
    if (node.children) {
        node.children.forEach((child) => {
            result = result.concat(findTextNodes(child, node.name));
        });
    }

    return result;
}

const result = [];

ast.forEach((node) => {
    const temp = findTextNodes(node, node.name);
    let fatherList = '';
});

const html = HTML.stringify(ast);

这是我目前能找到的最优方案了,不过这个方案应该也是偏向于解析成dom,不过这个库没有进行更多消耗资源的操作,应该是比传统的解析库性能更优秀的。

各种标签测试

WikiLink测试

[[note1]]

[[note2]]

[[note3]]

[[note4]]
  • [[note5]]

[[note6]]

[[note7]]

[[note8]]

@1900’Blog
All work and no play makes Jack a dull boy
@1900'Blog

https://cms.1900.live/ni-hao-astro/

测试一下文字包裹[[note9]]后的解析情况

[[note9]]

实际引用测试

已经初步完成联动,目前的考虑的策略顺序如下,

博客中

  1. 优先从CMS中去匹配文章 Title 一致的文章
  2. 如果博客内没有一直的则去Obsidian中匹配文件Title字段一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

Obsidian中

  1. 优先匹配Obsidian中Title一致的文章
  2. 如果没有则去博客内匹配Title一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

如Obsidian中的 About 这篇文章,在博客中以 [[About]] 写入,最终文章内就会解析成 [[About]] 。

因为Astro的本地路由都是基于网站根目录,所以我们只要将博客和Obsidian的文件分别进行静态生成,并在生成过程干涉 [[]] 的转换成 <a href=''><a/> 标签,然后给出正确的slug,便可以很方便的实现SPA形式的页面跳转。

完。

🤣JUSTFUN 周刊:第14期

一图

骑行路上

1. 种的辣椒 2. 骑行路上

流水账

  • 2024年9月2日:忽然对Astro的语法有了新的理解,也许我可以做到?
  • 2024年9月2日:如果地球是个学校,各个国家是上学的孩子,那么老鹰就是校园霸凌的恶霸。
  • 2024年9月2日:自由的含意是:我能自由的干你,你不能还手。
  • 2024年9月2日:我真是个傻逼,之前吐槽说Ghost文章丢失了,刚刚忽然发现特么是我写成Page了,压根不在Posts列表里。
  • 2024年9月4日:路永远在自己的脚下。
  • 2024年9月4日:正式靠别了Tai,改用ActivityWatch
  • 2024年9月4日:11ty-book有人点了Star,开心
  • 2024年9月4日:成年人变得更成熟时需要同时学会两件事情:第一,你想要的东西,懂得主动提出要求,不要让别人猜;第二,你提出来的要求,无论别人接受还是拒绝都要尊重别人的决定。这两者看起来不可调和的矛盾,其实是你自己对于被拒绝的恐惧,因为恐惧所以要么害怕提出要求,要么要求被拒绝后转化为厌恶情绪。
  • 2024年9月5日:开始移植11ty-book到astro上,新主题:astro-book
  • 2024年9月5日:我以后的孩子英文名可以叫 Dura ?是柔软、纯洁的意思,好像是乌克兰语?
  • 2024年9月11日:终于完成了Astro的所有功能移植。

读了、看了、听了

  • 2024年9月1日:看了 [[花束般的恋爱]] ,对两性相处有了新的看法,可以说是教科级别的两性相处案例。
  • 2024年9月2日:看了[[因果报应]] ,可惜是五分钟解说版本版本,后悔。
  • 2024年9月6日:在微信听书听王阳明——知行合一有声书
  • 2024年9月6日:开始看 [[心流]]
  • 2024年9月10日:看了 [[从21世纪安全撤离]],想起我小时候想的那些特异画面就是这部电影的风格,独一无二的味道,还是以前李献计一样的味道。

发现

一个动作改善脖子前倾

#知识 来自 x

在推上看到一个改善脖子前倾动作的视频,试了一下挺有效果,不过要长期坚持才行。

地址:x.com

极简主题参考

#设计 来自 ?

偶然发现的一个博客平台,他们默认提供的主题风格多是纯文字极简风格,简洁大气,找不到设计灵感的时候可以参考一下。

地址:Templates - Blot

足不出户看全球

#网站 来自 ?

偶然发现的一个全球范围热门景点的在线视频监控网站,不过国内的公开摄像头比较少。

网址:实况摄像头 马尔代夫-库雷杜 | SkylineWebcams

TV版Surfboard

#工具 来自 Github

优化TV操作的SurfBoard,懂的都懂。

项目地址:GitHub - getsurfboard/surfboard

GitHub - getsurfboard/surfboard

基于Bilibli音源的在线音乐播放器

#工具 来自 Github

一款基于bilibili的在线音乐播放器。

Eno Music 是一个基于 bilibili 的音乐播放器,完全改变了你在该平台上的体验。它重新构想了 bilibili 作为一个音乐平台的样子,并提供了独特而沉浸式的体验。

项目地址:GitHub - cloudflypeng/eno-music

3D打印桌面手机支架

#工具 来自 Youtube

一个可以通过3D打印制作的多功能桌面手机支架,emmm,应该说桌面时钟更贴切?

各种加载样式

#网站 来自 ?

无意间找到的一个拥有各种五花把门加载效果的CSS网站,在线预览,及拿及用。

网站地址:The Classic CSS Loaders Collection

你好Astro!

选型往事

在2024年3月份,我正式将Ghost博客作为无头CMS使用,并搭配SSG工具生成静态页面 。

其实在当时的技术选型时其实已经考虑过Astro,当时的Astro的版本号还是3.x,不过因为自己的技术不够成熟,编码思路还一直是Ghost时期的handlers这种模板结构,尝试多次无果后最终还是选择了11ty作为网站的生成工具,并撰文 新主题11ty-book 分享了当时的成果。

积累

不过这段时间我都有在不间断的重新查阅Astro的文档,思考该如何用Ghost搭配它,希望能用上这个热门的网站生成器。

事情的转折点是前几天发现 Astro-remote 这个插件,它提供的组件能加载HTML字符串或远程Markdown文件,渲染成网页内容,并在这个过程中还可以对HTML元素使用Astro的组件进行替换,这意味着可以不再用之前在11ty中那样,用 JSDOM 这个库通过通过虚拟DOM进行操作 eleventy/transforms/parseContent.js

之后我便根据自己的思路写了几个Demo,以前困扰的几个点似乎都能解决

  • 可以通过这个插件的容器替换功能,使用Shiki、Prism等工具的代码高亮
  • 可以自定义容器,之前 hugo-book中的Columns Expand 等功能都机会实
  • 甚至可以把之前Obsidian 数字花园整合进一个网站来。

新开始

所以没有犹豫,在9月2号就开始了移植工作。

这段时间,我边看文档边学习、实现、思考,得益于我在11ty-book上花费的时间和对Node.js逐渐了解,以前看不懂的Astro代码忽然像是顿悟般的或霍然开朗,明白了组件形式和之前11ty时传统模板形式的区别,和意义。

Astro的开发体验的确很棒,这也是我之前在选型11ty之后依旧还对Astro念念不忘的原因。

高速的迭代频率,优秀的设计语言和活跃的开发群体让这款工具一直充满着活力,得益于官方全站的中文文档,而且手册十分详尽,让我在实际开发过程中能少走了不少弯路。

终于在昨天,一共历时七天,我终于将11ty-book的功能移植了个80%作用,并提交了第一个正式版本到了Github。并在昨天晚上正式切换到了Astro版本的huog-book。

开源地址:https://github.com/rebron1900/astro-book/

不过目前以前还有一些问题还未来得及处理,现在还有一些构思等待实现:

  • 整合Obsidian
  • 实现Ghost CMS和Obsidian之间互相的WikiLink引用
  • 全站搜索
  • 影集功能
  • 文章内丰富的卡片引用,如NeoDB,CodePen,视屏,音乐等。
  • 试试用SSR实现文章的密码访问
  • ...以及其他无数种可能

结束语

发现自己这些年在折腾博客这条路上越走越远。

从以前的博客大巴只会转发文章,到后开始学习编程,开始SelfHost Wordpress,开始满世界找主题、找插件优化博客,到现在我已经能流畅使用JavaScript,Node.js、Python等工具进行代码开发,之前还自己实现了一个在博客显示博主状态服务 将博主PC上使用的应用信息实时显示到博客,体验了一把Socket和服务端的开发,那时候还狂妄的想着「自己是不是已经可以前后端一把梭了?」,哈哈。

虽然13年前毕业后我没有从事IT工作。

但是我热爱编程,我喜欢编程,我喜欢互联网蓬勃的生命力。并且不同于需要拿找饭吃,我更多是处于喜欢、兴趣使然,基于这个初心去学自己感兴趣的技术,并在折腾的路上学以致用。

我很满足,也很开心,希望我20年后依旧初心不改。

Thks。

Obsidian 和 Windows 我都用什么插件和工具

刚刚看到雅余哥发布了一篇 Obsidian 和 WordPress 我都用什么插件 的文章,遂也想分享一下我的Obsidian所用的插件,而我没用WordPress,所以就分享一下Windows上的一些工具把。

Obsidian

先聊聊Obsidian,其实笔记软件我用过很多了,从Trilium到Logseq再到Obsidian,我换软件并不因为那款软件不优秀,相反,他们都非常有自己的特点,只单纯是因为我的笔记习惯发生了改变,Trilium和Logseq的优缺点我之前的文章中也有细碎的提起过,不过和本篇文章主题不搭就放待下次再说把。

主题

先说说主题把。

最开始使用的Obsidian主题是Minimal,一款非常简洁的主题,热度一直也都是名列前茅,不过在 JUSTFUN 周刊:第四期 时我分享了我在B站发现的新主题 Border ,Border同样也是一款简约风格的主题。

但是不同于Minimal的极简,Border的配色要略微活泼一点,且同样拥有丰富的自定义选项,并对一些插件做作了适配,而且我非常喜欢卡片样式的布局,在调整好后会让我有一种协调的舒适感,记笔记也更有动力了。

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

Border

插件

归功于Obsidian的用户数量,这让它的第三方插件生态非常强大,可玩的花样非常多,我以前启用的插件多的时候二三十个,近些年来回归本心,渐渐弃用了很多插件,明白要合理利用工具,别捡了芝麻丢了西瓜。

这里以插件在Obsidian列表中出现的先后顺序列出把。

aw-watcher-obsidian

#统计

ActivityWatch 是一款跨平台的统计软件,他可以帮你统计花费在电脑、手机、编辑器上的时间,并提供了正则分类,目前我已经使用几个月了,改天单独分享一下它的使用体验。

而是aw-watcher-obsidian 则是能帮你统计你在Obsidian的各个笔记上所花费的时间的插件,同样它还有VSCode的版本。

auto-link-title

#增强

这个很多应该都用过,粘贴链接可以自动获取链接的Meta信息,生成Markdown格式的链接。

BRAT

#插件

Obsidian上的TestFlight,有些插件没有上架Obsidian的官方商店就需要靠它安装和更新跟进。

Calendar 

#管理

日历插件,在侧边栏中显示日历,最近好像有一些支持农历的日历插件。

Custom Frames

#增强

可以在Obsidian里嵌入任意网址,比如可以在Obsidian嵌入你的Memos服务。

Dataview

#增强

Obsidian里的SQL,强大的自定义查询可以实现非常多功能,不过我玩不太会,现在只用了一些最简单的特性。

Douban

#增强

可以很方便的在Obsidian插入书籍信息,搭配Templater还可以实现自定义格式的书籍信息。

Floating TOC

#增强

给笔记加上TOC,有些主题会单独为它适配样式。

Folder Note

#增强

给文件夹创建笔记,我这里主要是搭配Waypoint生成MOC。

GIT

#同步

将Obsidian库同步到Github上。

Kanban

#管理

让Obsidian拥有看板功能,对于任务管理,项目推进等非常有用。还能联动其他插件,功能强大。

Linter

#格式

Obsidian的笔记格式化插件,功能非常非常强大,可以细微到各个Markdown元素的处理,而且有了它Pangu插件也可以不用了。

List Callouts

#美化

让无序列表拥有Callouts的样式,支持自定义

MySnippets

#美化

在右下角增加一个图标,点击即可快速切换Snippets的启用状态

PeriodicNotes

#管理

搭配 Calendar  可以生成周记、月记、年记等。

Recent Files

#增强

显示你最近打开的笔记记录

Style Settings

#美化

主题增强,大部分主题的个性化设置都要同过这个插件实现。

Surfing

#增强

可以在Obsidian中打开网页,就像浏览器那样。

Waypoint

#增强

我用它在FolderNote中生成MOC导航,非常好用。

Webpage HTML Export

#增强

可以将笔记库导出成可以部署的HTML,支持Canvas,关系图谱,主题等,非常强大,之前我用它分享过我重庆行的旅行规划。

Windows

平时用的工具比较多,这边挑几款我必备的分享

ActivityWatch

#统计

可以统计你在Windows上各个软件中所花费的时间,同时有Obsidian、VSCode等插件,可以辅助统计这些平台的使用数据,并通过图标进行展示。

PixPin

#图片处理

截图工具,替代了以前的Snippets,Snippets有的它都有,没有的他也有,就是首次启动会比较慢一些,也只是相对比较慢而已。

QuickLook

#工具

可以像Mac一样,通过空格快速预览各种文件

uTools

#工具

通过快捷键唤起搜索框,可以快速实现计算器、文件搜索、图片转换等,之前弃用过一段时间,后来又用了回来,u1s1,还是非常好用的。

LocalSend

#工具

多平台快速互发文件,简单好用。

酷呆桌面

#工具

桌面整理工具,刚发布就开始用,后来收费了,买了付费版,不想花钱也行,最后那个版本也能用,我付费是纯属支持工作了。

StartAllBack

#工具

Windows任务栏自定义工具,可以恢复传统的多功能开始菜单,建议入个正,淘宝上不贵,盗版的有时候在Windows更新后会出BUG。

Chromium

#浏览器

这个没啥可说的,必备工具,用的Hibbiki 编译的版本。

xDown

#工具

IDM的平替免费下载工具

Everthing

#工具

文件搜索神器,几乎都是必备了把?

Notepad4

#工具

Notepad++替代工具,Windows记事本替代工具

Geek

#工具

Windows应用卸载程序,卸载完后还能帮你清楚注册表和一些文件夹。

irfanView

#工具

速度非常块的看图工具,由于替代Windows默认看图器。

PM Players

#影音

一位大佬做的PotPlayer带解码的小白包,非常好用。

Rime-Weasel

#输入法

输入法,不用多说。

VPS备份脚本修复

之前在 服务器重装设置指南 一文中分享了关于初装VPS后的一些列的优化操作,其中分享了一个数据的备份脚本,通过丰富的配置项即可实现文件、mysql数据的多版本备份策略,备份策略参见下放列表:

  • 保留一年前的1个备份
  • 保留150天前的1个备份
  • 保留90天前的一个备份
  • 保留30天前的1个备份
  • 保留最近7天的每天备份

用尽量少的空间备份足够容错的数据备份,并通过rclone等工具同步到其他网盘或储存空间之上。

但是最近忽然发现这个脚本似乎没有正常工作,当时是通过ChatGPT生成的备份脚本,可能存在某些问题,在排查日志时发现是因为路径问题导致脚本中断。

所以今天又用AI重新对脚本做了修复和优化,目前能正常工作了,如果有需要的朋友可以拿去体验。

💡
配置中需要用到.my.cnf文件,这个是mysql的登录凭证,格式如下

[mysqldump]
user = root
password = gdsagag1217B
#!/bin/bash
set -e  # 遇到错误停止执行
set -x  # 打开调试模式

# 自定义变量
CUSTOM_HOME="/home/username"  # 可以根据需要修改为其他路径 // [!code highlight]

# 设置参数	
BACKUP_DIR="$CUSTOM_HOME/backup"	# 备份数据的存放文件夹 // [!code ++]
TARGET_DIR="$CUSTOM_HOME/data"	# 需要备份的数据文件夹 // [!code --]
DATABASES=( "database_prod" )  		# 需要备份的mysql数据库列表
ZIP_PASSWORD="password"		# 压缩包密码
DATE=$(date +"%Y%m%d%H%M%S")		# 日期格式
MYSQL_CONTAINER_NAME="mysql"		# mysql容器的名称
TEMP_HOME_DIR="$CUSTOM_HOME/backup_tmp"  # 临时目录
TEMP_BACKUP_DIR="$TEMP_HOME_DIR/backup_temp_$DATE"	# 临时目录名称
LOG_DIR="$CUSTOM_HOME/backup_logs"  # 日志文件存放目录
LOG_FILE="$LOG_DIR/backup_script.log"	# 输出日志

# 创建日志目录
mkdir -p "$LOG_DIR"

# 创建日志文件
touch "$LOG_FILE"

# 日志记录函数
log() {
    echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" | tee -a "$LOG_FILE"
}

# 创建目录
create_dir() {
    mkdir -p "$1"
    log "Created directory: $1"
}

# 复制 .my.cnf 文件到 Docker 容器
copy_my_cnf() {
    if ! docker cp "$CUSTOM_HOME/.my.cnf" "$MYSQL_CONTAINER_NAME":/root/.my.cnf; then
        log "Failed to copy .my.cnf to Docker container $MYSQL_CONTAINER_NAME"
        exit 1
    fi
}

# 复制数据到临时备份目录
copy_data() {
    if [ -d "$TARGET_DIR" ]; then
        cp -r "$TARGET_DIR"/* "$TEMP_BACKUP_DIR"
        log "Copied data from $TARGET_DIR to $TEMP_BACKUP_DIR"
    else
        log "Target directory $TARGET_DIR does not exist"
        exit 1
    fi
}

# 导出数据库
export_databases() {
    for MYSQL_DATABASE in "${DATABASES[@]}"; do
        SQL_FILE="$TEMP_BACKUP_DIR/${MYSQL_DATABASE}_backup_${DATE}.sql"
        if ! docker exec "$MYSQL_CONTAINER_NAME" sh -c "mysqldump --defaults-extra-file=/root/.my.cnf $MYSQL_DATABASE" > "$SQL_FILE"; then
            log "Failed to export database $MYSQL_DATABASE"
            exit 1
        fi
        log "Exported database $MYSQL_DATABASE to $SQL_FILE"
    done
}

# 创建加密压缩包
create_archive() {
    ARCHIVE_NAME="backup_${DATE}.zip"
    if ! zip -r -P "$ZIP_PASSWORD" "$BACKUP_DIR/$ARCHIVE_NAME" "$TEMP_BACKUP_DIR"; then
        log "Failed to create backup archive"
        exit 1
    fi
    log "Created encrypted archive $BACKUP_DIR/$ARCHIVE_NAME"
}

# 清理过期备份
cleanup_old_backups() {
    log "Cleaning up old backups"
    find "$BACKUP_DIR" -type f -name "*.zip" | while read -r backup_file; do
        backup_date=$(basename "$backup_file" | grep -o -E '[0-9]{14}')
        if [ -n "$backup_date" ]; then
            backup_epoch=$(date -d "${backup_date:0:8} ${backup_date:8:2}:${backup_date:10:2}:${backup_date:12:2}" +%s)
            current_epoch=$(date +%s)
            diff_days=$(( (current_epoch - backup_epoch) / 86400 ))

            if [ $diff_days -ge 365 ]; then
                log "Deleting backup $backup_file older than 365 days"
                rm "$backup_file"
            elif [ $diff_days -ge 150 ] && [ $(( diff_days % 150 )) -ne 0 ]; then
                log "Deleting backup $backup_file older than 150 days but less than 365 days"
                rm "$backup_file"
            elif [ $diff_days -ge 90 ] && [ $(( diff_days % 90 )) -ne 0 ]; then
                log "Deleting backup $backup_file older than 90 days but less than 150 days"
                rm "$backup_file"
            elif [ $diff_days -ge 30 ] && [ $(( diff_days % 30 )) -ne 0 ]; then
                log "Deleting backup $backup_file older than 30 days but less than 90 days"
                rm "$backup_file"
            elif [ $diff_days -ge 3 ] && [ $(( diff_days % 3 )) -ne 0 ]; then
                log "Deleting backup $backup_file older than 3 days but less than 30 days"
                rm "$backup_file"
            fi
        else
            log "Could not extract date from $backup_file"
        fi
    done
}

# 主执行流程
log "Backup script started"
create_dir "$BACKUP_DIR"
create_dir "$TEMP_HOME_DIR"
create_dir "$TEMP_BACKUP_DIR"
copy_my_cnf
copy_data
export_databases
create_archive
log "Removing temporary backup directory $TEMP_BACKUP_DIR"
rm -rf "$TEMP_HOME_DIR"
cleanup_old_backups
log "Backup completed and expired backups cleaned"
❌