在 Linux 下使用 Clash For Windows 管理 Clash 订阅

早之前写过一篇文章,当时我直接使用命令行的方式使用 clash,但用过一段时间之后发现 Clash For Windows 支持了 Linux,所以切换到 Clash For Windows,用 UI 界面方便随时切换。

不要看这个 Clash For Windows 的名字有点歧义,实际上是全平台的,不是 Windows 专属的。Clash For Windows 在下文中会简称 CFW。

Clash for Windows 的优点:

  • 支持自动选择节点,可以根据延迟自动选择,也可以根据规则
  • 支持本地编辑规则
  • 支持查看当前订阅的流量等信息

更加详细的使用指南可以参考这个文档

CFW 使用

可以在 GitHub 下载(项目已被删除),也可以使用镜像地址下载

下载完成后是一个压缩包,解压,并执行其中的 cfw 即可。

添加快捷方式,因为我使用 Cinnamon 是基于 Gnome ,所以可以执行如下命令添加一个快捷方式:

gnome-desktop-item-edit ~/.local/share/applications --create-new

在弹出的窗口中,填写 Name 和 Command

gnome new launcher

说明:

  • Name: 显示的名字
  • Command: cfw 的绝对路径

Dashboard:

Clash For Windows 页面:

在 Profile 页面上可以添加订阅地址,订阅地址可以在这里 获取。


2021-10-17 linux , clash , v2ray , proxy , shadowsocks , clash-for-windows

独服 Proxmox VE 配置 NAT 使虚拟机共用一个公网 IP

[[so-you-start]] 的独立服务器本来安装了 Ubuntu 20.04,后来想想为了充分利用 CPU 和内存,不如安装一个 [[Proxmox VE]] 然后在其基础之上再安装 Ubuntu 或者其他的系统测试。So you Start 通过后台安装 Proxmox 的过程比较简单,我直接使用了后台的 Proxmox VE 6 模板安装了 Proxmox。

So you Start 其他独立服务器提供商通常只会提供一个公网 IP,其他的 failover IP 可能需要购买(So you Start 可以使用 1.5$ 购买一个),如果不想多花这个购买 IP 的钱,可以配置 Proxmox VE 的虚拟机走 NAT,共用宿主机 Proxmox VE 的 IP,做一下端口转发。实际在一些 VPS 提供商那边也能看到 [[NAT VPS]],这类型的 VPS 就是共用同一个 IP 的 VPS,这一类的 VPS 通常比较便宜,但是可用的端口数量有限。

完成下文的配置之后大致的网络拓扑图是这样的:

proxmox ve nat

前提知识

在继续下文之前有一些前提知识需要了解,否则会理解起来有些困难。

Linux Bridge

Linux Bridge 一般翻译成网桥,相当于一个软件实现的交换机。

如果要配置成 NAT 网络,Proxmox VE 上所有的虚拟机共享一个网桥 vmbr1,通过 vmbr1 访问外部网络。

理论上 Proxmox VE 支持 4094 个网桥。默认情况下 Proxmox VE 会创建一个 vmbr0 的网桥,并和检测到的第一块网卡(eno3)桥接。可以查看网口的配置文件 /etc/network/interfaces 对应的信息:

root@pve:~# cat /etc/network/interfaces
# network interface settings; autogenerated
# Please do NOT modify this file directly, unless you know what
# you're doing.
#
# If you want to manage parts of the network configuration manually,
# please utilize the 'source' or 'source-directory' directives to do
# so.
# PVE will preserve these directives, but will NOT read its network
# configuration from sourced files, so do not attempt to move any of
# the PVE managed interfaces into external files!

auto lo
iface lo inet loopback

iface eno3 inet manual

iface eno4 inet manual

auto vmbr0
iface vmbr0 inet dhcp
    bridge-ports eno3
    bridge-stp off
    bridge-fd 0

NAT

[[NAT]] 全称是 Network Address Translation,在计算机网络中是网络地址转换的含义,也被叫做网络掩蔽,这是一种在 IP 数据包通过路由器或防火墙时重写来源 IP 地址或目的 IP 地址的技术。

在我们的 Proxmox VE 只有一个公网 IP 的情况下,如果要让多个虚拟机共享同一个 IP 地址对外提供服务,就需要用到 NAT 技术,让请求访问到宿主机的时候,转发到对应的虚拟机。

所有的虚拟机使用内部私有 IP 地址,并通过 Proxmox VE 的公网 IP 访问外部网络。我们会使用 iptables 来改写虚拟机和外部通信的数据包:

  • 对于虚拟机向外部网络发出的数据包,源地址是内网 IP,目标终端在返回数据的时候,无法把数据包发送对正确的路由,所以在发送出去前,将源地址替换成 Proxmox VE 的 IP 地址
  • 对于外部网络返回的数据包,将目的地址替换成对应虚拟机的 IP

NDP

[[NDP]] 全称是 Neighbor Discovery Protocol,简称 NDP,类似 IPv4 中的 ARP 协议。

配置 Proxmox VE NAT

上面的配置也提到了默认情况下 Proxmox VE 会创建一个 vmbr0 桥接找到的网卡。

首先查看一下网络接口:

root@pve:~# ip -f inet a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
4: vmbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet 51.xx.xx.xx/24 brd 51.xx.xx.xxx scope global dynamic vmbr0
       valid_lft 81976sec preferred_lft 81976sec

Proxmox VE 上使用 NAT 创建虚拟机的原理是,创建一个 Linux Bridge 并创建一个子网,然后将所有虚拟机包括宿主机都连接到这个子网内,再开启 [[iptables]] 的 NAT 功能。

创建 Linux Bridge

在安装网桥之前,首先安装:

apt install ifupdown2

然后登录 Proxmox VE 后台,创建 Linux Bridge,点击 PVE,然后选择 System -> Networks,然后点击创建。

填写 IP 和子网掩码,IP 地址填写个局域网的网段地址就行。其他项目不用填也不用改,保持默认(不用 IPv6 的情况下)。

  • IP 地址填写一个局域网地址:10.0.0.1/24

上面的配置创建了一个新的 vmbr1 网桥分配了一个子网 10.0.0.1/24,宿主机(网关)在子网的 IP 是 10.0.0.1。

创建完成之后,查看 /etc/network/interfaces,然后在对应的 vmbr1 后面修改成相应的配置(注意 vmbr1 下面的配置):

auto lo
iface lo inet loopback

iface eno3 inet manual

iface eno4 inet manual

auto vmbr0
iface vmbr0 inet dhcp
        bridge-ports eno3
        bridge-stp off
        bridge-fd 0

auto vmbr1
iface vmbr1 inet static
        address 10.0.0.1/24
        gateway 1.2.3.254  # 独立服务器IP前三个数加上 254
        bridge-ports none
        bridge-stp off
        bridge-fd 0
        post-up echo 1 > /proc/sys/net/ipv4/ip_forward
        post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j MASQUERADE
        post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j MASQUERADE
        
        post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 2022 -j DNAT --to 10.0.0.102:22
        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 2022 -j DNAT --to 10.0.0.102:22

说明:

  • ip_forward 一行表示开启 IPv4 转发,这个是内核参数,将 Linux 当作路由器用的参数。一般来说,一个路由器至少要有两个网络接口,一个 WAN,一个 LAN,为了让 LAN 和 WAN 流量相同,需要内核上的路由
  • post-uppost-down 分别表示网卡启用和禁用之后,执行后面的命令
  • iptables 行表示,开启防火墙转发,-A 表示添加规则,配置一条 NAT 规则,源地址为 10.0.0.0/24 的流量,转发到 vmbr0 接口。
  • MASQUERADE 对 IP 地址数据包进行改写
  • 网卡关闭后 -D 删除这条规则
  • 最后 2 行是把虚拟机 10.0.0.102 上的 22 端口 NAT 到宿主机的 2022 端口,这样使得外部的网络可以通过 Proxmox VE 宿主机的 2022 端口访问虚拟机的 22 端口,可以直接使用上面的配置,或者手动执行下面的命令:

      iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 2022 -j DNAT --to 10.0.0.102:22
    

最后两行配置在网络配置中是为了让系统重启之后配置依然生效。否则 iptables 的转发就可能丢失。

启用网桥:

sudo ifup vmbr1

显示信息:

ip address show dev vmbr1

查看 iptables 配置是否生效:

iptables -L -t nat

结果:

root@pve:~# ip address show dev vmbr1
9: vmbr1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 62:32:cf:9d:5d:f9 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.1/24 scope global vmbr1
       valid_lft forever preferred_lft forever
    inet6 fe80::6032:cfff:fe9d:5df9/64 scope link 
       valid_lft forever preferred_lft forever

重启网络:

sudo systemctl restart networking
systemctl status networking.service

通过上面的方式创建了 vmbr1 的网桥之后,再创建新的虚拟机就可以使用这个新的子网。因为这里没有配置 DHCP,所以虚拟机需要设定静态 IP 地址。

在我的真实操作中,我发现无论我怎么重启 systemctl restart networking 都无法使得 Proxmox VE 的网络配置生效,最后不得不重启服务器才可以。

虚拟机配置

在创建虚拟机的时候,记得在网络配置的时候选择 vmbr1,因为没有配置 DHCP 所以需要用下面的方式手动指定虚拟机的静态 IP 地址。

Ubuntu 虚拟机网络配置

Ubuntu 从上一个版本开始就使用 netplan 来管理网络配置,所以需要修改 netplan 的配置:

einverne@ubuntu2:~$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    ens18:
      addresses:
      - 10.0.0.102/24
      gateway4: 10.0.0.1
      nameservers:
        addresses:
        - 8.8.8.8
        - 8.8.4.4
  version: 2

注意配置其中的静态地址和网关。

  • 静态地址配置 10.0.0.0/24 网段下的地址,比如 10.0.0.102
  • gateway4 地址配置 vmbr1 网桥的地址
  • DNS 服务器可以使用 Google 的,也可以用 1.1.1.1 Cloudflare 的,或者 OVH,或者 So you Start 提供的都行

修改完成后执行:

netplan apply

之后可以使用如下命令测试连通性:

ping 1.1.1.1
ping google.com

Debian 虚拟机网络配置

Debian 上的网络配置如下:

einverne@debian:~$ cat /etc/network/interfaces.d/50-cloud-init
auto ens18
iface ens18 inet static
address 10.0.0.100
netmask 255.255.255.0
gateway 10.0.0.1
dns-nameserver 1.1.1.1
dns-nameserver 8.8.8.8

说明:

  • ens18 是 Debian 虚拟机的网络接口
  • address 填写 10.0.0.0/24 网段的静态地址
  • gateway 填入 vmbr1 的地址

然后重启网络:systemctl restart networking

如何调试

在配置的过程中遇到很多问题,可以用一下一些命令熟悉 Linux 下的网络配置。

iptalbes -L -t nat
ip a
ip route show
cat /proc/sys/net/ipv4/ip_forward
qm config <VMID>

遇到的问题

虚拟机 VM 内部无法访问互联网

在按照上述步骤配置后,登录 VM (Ubuntu 20.04) 内部,只能 Ping 通网关(10.0.0.1) 而无法 ping 通任何外部网络。

再经过一番调查之后发现在上述配置中的 iptables 转发并没有生效,并且重启网络也没有生效,所以我只能重启服务器解决。

端口映射

在启用 NAT 网络之后,如果要外部网络访问 VM 的端口,则需要开启 iptables 端口转发。

下面一句的含义就是将 Proxmox VE(vmbr0) 的 2022 端口转发到 10.0.0.102 这台虚拟机的 22 端口:

iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 2022 -j DNAT --to 10.0.0.102:22

这样在外部互联网就可以通过 ssh -p 2022 root@<proxmox ip> 来访问 Proxmox VE 中的虚拟机了。

如果遇到要转发一组端口可以使用:

post-up   iptables -t nat -I PREROUTING -p tcp -i vmbr0 --dport 8000:9000 -j DNAT --to 10.0.0.102:8000-9000
post-down iptables -t nat -D PREROUTING -p tcp -i vmbr0 --dport 8000:9000 -j DNAT --to 10.0.0.102:8000-9000

延伸阅读

如果已经购买了 Failover IP,或者独立服务器提供了多个可用的 IP,那么也可以参考 这篇文章 配置 Proxmox VE 的虚拟机使用额外的 IP 地址。这样使用一台独立服务器就可以开多个 KVM 的 VPS 了。

reference


2021-10-15 proxmox , pve , linux , nat , networking , operating-system

命令行下使用 jdupes 删除重复的文件

很早之前整理过一篇文章介绍了几个之前在 Linux 上用过的用来快速找到重复文件并删除的文章,这么多年过去了,最后发现还是 jdupes 最好用,因为是 c 语言编写,可以在所有平台上面运行,并且速度远远好于其他的命令。

之前的文章对于 jdupes 总结的比较简单,所以这里单独再总结一下 jdupes 常用的几个例子。

查找文件夹下重复文件

查找文件夹下重复的文件,这也是最常见的需求,可以直接

jdupes -r path/to/dir

-r 参数在这里表示 --recurse 递归查找文件夹下所有的子文件夹。

删除重复内容

jdupes 在输出重复的内容的同时也提供了方法可以让用户自行选择删除哪个文件,或者自动发现并删除重复的文件。

注意:个人推荐每一次都手动选择需要删除的内容,否则请提前做好备份工作,防止文件丢失

jdupes 提供了 -d 选择,如果使用 -d 选项,jdupes 会弹出提示让用户选择是否删除重复文件:

jdupes -dr path/to/dir

如果确定自己不需要手动选择删除,可以使用 -N 选项,表示 --noprompt,谨慎使用该选项:

jdupes -r -N path/to/dir

查找两个文件夹下重复文件并删除第二个文件夹下的重复内容

有些时候文件在两个文件夹中,比如 dir1, dir2,需要实现的是将 dir2 中和 dir1 中重复的文件删除,而保留 dir1 中的文件,这个时候可以使用 -O 选项。

-O 选择可以让用户控制出现在第一条的文件,以便将其保留。

jdupes -nrdNO dir1 dir2

上面的命令会递归查找 dir1 和 dir2 中的文件,并自动将 dir2 中和 dir1 重复的文件删除。

reference


2021-10-12 jdupes , linux , file-manage , duplicate , tutorial , cli

So you Start 独服 Proxmox VE 虚拟机配置 Failover IP

最近买了一台 [[so-you-start]] 的独立服务器,开始的时候安装了 Ubuntu 没有充分利用独立服务器的优势,所以这两天把系统重新安装成了 [[Proxmox VE]],然后在上面又安装了 Ubuntu 20.04,So you Start 提供了额外 16 个可以以 1.5 美元购买的 [[Failover IPs]],Failover IP 本来是为了可以将服务器迁移到另外的服务器而提供的机制,但在 Proxmox VE 虚拟化技术系统下,可以给虚拟机也分配不同的 IP,这样就实现了从一台服务器虚拟化多个 VPS 的操作。

安装 Proxmox VE 的过程就不多说了,在 So you Start 或者 OVH 后台直接使用模板安装即可,我使用的 6.x 版本,没有升级到最新的 7 版本。

安装完成后使用 Ubuntu Server 的 ISO 镜像完成虚拟机的安装。

前提准备工作

  • 一台安装好 Proxmox VE 的独立主机
  • 新建一台可以登录的虚拟机,操作系统不限 (推荐 Debian/Ubuntu)
  • 购买好至少一个额外的 Failover IP

配置 Failover IP 到虚拟机

Create a Virtual MAC Address

首先到 So you Start 后台 IP,然后选择购买的 Failover IP,新增 virtual MAC 地址,然后复制该 MAC 地址备用。

比如:

02:01:00:78:95:aa

新增 MAC 地址可能有一点延迟,等待一小会儿生效即可。

Add virtual MAC to the NIC of a VM

然后需要在 Proxmox VE 虚拟机配置中将上述 MAC 地址配置。如果还没有安装虚拟机,可以参考 Proxmox VE 官网的教程

VM 配置前需要是关闭状态。

在 Proxmox VE 中,找到虚拟机的 Hardware

找到 Network Device 选项,默认情况下是一个随机生成的 MAC 地址:

点击 Edit,然后在 MAC address 一栏将上一步的虚拟 MAC 地址填入,并保存。

然后启动 VM,接下来需要配置虚拟机的网络接口。

Configuring Network Settings

配置虚拟机网络。

Debian 10

首先查看一下接口:

ip addr

除了一个 lo 应该能看到类似 ens18 这样的接口。

Debian 的网络接口配置在 vi /etc/network/interfaces:

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto ens18
iface ens18 inet static
address 192.0.2.1
netmask 255.255.255.0
gateway 203.0.113.254
dns-nameservers 208.67.222.222 208.67.220.220

说明:

  • 修改其中的 ens18 为自己的相应的配置
  • address 一行:192.0.2.1 修改为自己的 Failover IP
  • gateway 一行: 203.0.113.254 前 3 个字节的数字,修改成独立服务器 IP 地址的前三个字节 IP 地址,最后添加 254,比如独立服务器的 IP 是 1.2.3.4,那么使用 1.2.3.254 作为网关
  • 最后一行是 DNS 设置,可以使用上面使用的 OpenDNS,也可以使用任何其他的,比如 Cloudflare 的 1.1.1.1, 或者 Google 的 8.8.8.8

然后使得接口生效:

ip link set ens18 up

最后重启 networking:

systemctl restart networking

然后可以测试连通性:

ping 8.8.8.8

能够 ping,表示已经可以联网。

ping google.com

然后看一下 DNS 解析,如果域名无法解析,我这边情况是少了 /etc/resolv.conf,手工创建文件并写入:

nameserver 8.8.8.8

即可。

Ubuntu 18.04

Ubuntu 从 17 版本开始就使用 Netplan 来管理网络配置,所以和 Debian 有一些区别。

修改 netplan 配置文件,根据不同的系统可能配置文件路径不一样,请注意一下:

vi /etc/netplan/01-netcfg.yaml
vi /etc/netplan/00-installer-config.yaml

然后使用:

# This is the network config written by 'subiquity'
network:
  version: 2
  renderer: networkd
  ethernets:
    ens18:
      dhcp4: no
      dhcp6: no
      addresses:
        - 192.0.2.1/32  # 这里填写 failover ip(vMAC 地址需要提前配好)
        - 1111:2222:3333:6666::2/64 # 如果有 IPv6 地址也可以配上,an ipv6 from your server allocation
      gateway4: 1.2.3.254
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]
      routes:
      - to: 1.2.3.254/32
        via: 0.0.0.0
        scope: link

和上面的配置类似,对应替换即可。

然后使之生效:

sudo netplan apply

注意在配置 Ubuntu 22.04 的时候 Netplan 可能会有一 WARNING:

gateway4 has been deprecated, use default routes instead. See the ‘Default routes’ section of the documentation for more details.

简单的查了一下 Netplan 的配置格式发生了改变。如果不修改理论上也没有关系,不过为了之后兼容,可以改用 Netplan 最新的配置。1

reference


2021-10-11 so-you-start , proxmox , proxmox-ve , pve , linux , ubuntu , failover-ip , network , ip

搭建自动化签到网站

很早以前就在用 binux 大佬的 qiandao.today,但后来不知道是用的人多了,还是大佬不怎么维护了,所以后来渐渐地就会登录不上,或者签到失败。干脆直接自己部署一个自己使用。

qiandao 项目是一个使用 Python 编写的自动化签到框架。最早由 Binux 开发,现在项目转到了组织中由 a76yyyy 维护。qiandao 是一个 HTTP 请求定时任务自动执行框架,基于 HAR 和 Tornado 服务。

Docker 部署

docker-compose 配置见 GitHub,克隆项目,直接 docker-compose up -d ,搭配 Nginx Proxy Manager 即可。

我自己搭建的服务如果有兴趣可以加入讨论群组 向我索要地址。

什么是 HAR

[[HAR]] 是 HTTP Archive format 的缩写,即 HTTP 存档格式,一种 JSON 格式的存档格式,用于记录浏览器和网站的交互过程。文件扩展名通常是 .har。签到项目提供了一个基于 Web 页面的 HAR 编辑器,可以通过浏览器录制,然后上传到网站中编辑的方式来快速制作符合自己需求的 HAR 文件。

HAR 文件包含有关网页或应用程序加载过程中所执行的 HTTP 请求和响应的信息。 HAR 文件通常是由浏览器开发工具或类似的工具生成的,可以在许多浏览器中使用。可以使用这些工具来查看网站或应用程序的加载情况,并分析其中的性能问题。

如何获取 HAR 文件

在 Google Chrome 中,可以打开开发人员工具(通常可以通过按 F12 键或在浏览器菜单中选择“更多工具”>“开发人员工具”来访问)。然后,在左侧的菜单中选择“网络”,然后在页面加载过程中会显示所有 HTTP 请求的列表。可以使用“导出为 HAR”按钮将这些请求保存到一个 HAR 文件中。

如何制作 HAR 签到模板

在制作模板时,需要将请求中的 Cookie 替换为双花括号,并且保存,那么通过这个模板创建签到时就会有 Cookie 字段定义。

如何处理 Cloudflare 拦截

api

api 地址 参数 参数是否必须 说明 用例
时间戳 http://localhost/util/timestamp ts 默认返回当前时间戳和时间,如果参数带时间戳,返回所对应北京时间 http://localhost/util/timestamp http://localhost/util/timestamp?ts=1586921249
Unicode 转中文 http://localhost/util/unicode content 要转码的内容 http://localhost/util/unicode?content=今日签到:1\u5929\u5ef6\u4fdd
Url 转中文 http://localhost/util/urldecode content 要转码的内容 http://localhost/util/urldecode?content=签到成功!每日签到获得%2C
正则表达式 http://localhost/util/regex data,p data:原始数据 p:正则表达式 http://localhost/util/regex?data=origin_data&p=regex
字符串替换 http://localhost/util/string/replace p,s,t p:正则表达式 s:要替换的字符串 t:要替换的内容 http://localhost/util/string/replace?p=regex&t=text_to_replace&s=text

其他工具

Google 提供的分析 HAR 的工具

最后

我自己的新建的签到网站,虽然目前只有我一个人用,但是我自己也创建了不少好用的模板,尤其是 PT 站点签到的模板,如果有朋友有需要可以加入 Telegram Group ,然后大家在群里一起讨论怎么充分利用签到程序。

reference


2021-10-11 qiandao , 签到 , python , har , chrome , http-request

使用 Remark42 替换博客的 Disqus 评论系统

前两天用隐身窗口打开自己的博客的时候突然发现 Disqus 评论框上一大片广告,没想到现在 Disqus 已经这样了,并且之前还暴露出过隐私问题。所以就想着什么时候替换掉它。

虽然之前也调研过静态博客的评论系统,但说实话那个时候还没有让我有替换掉 Disqus 的动力,毕竟有一些评论系统是基于 GitHub issue 的,也有一些现在来看比 Disqus 存活的时间都要短,连官网都不存在了。

问题

整理一下 Disqus 目前让我不舒服的一些地方:

  • 在评论框上方插入了大片广告
  • 正常留言被错误标记,我去 Disqus 后台看有不少正常的留言都被标记为了垃圾留言,而实际上完全没有问题,并且附加的链接也都是合理的,并且 Disqus 没有任何通知。所以也得对过去留了言没有得到回复的读者说声抱歉。
  • Disqus 拖慢网页加载速度,用 GTmetrix 跑一下,可以看到一大半是因为 Disqus。

替换了 Disqus 至少可以提升一下访问速度,访客也不会被广告追踪。

Disqus 代替品

所以接下来就研究了一下 Disqus 的代替品,我大致把他们分成了一下几个部分:

  • 类似于 Disqus 以中心化的云服务方式提供评论服务,并且兼顾用户隐私,所以基本上都按照访问量来收费,最少的也需要 5$ 一个月,这一类的服务有
  • 第二类是以 GitHub issue 作为评论系统的后端,借助 GitHub 开放 API 的能力,使用 issue 来保存博客的评论,这一类评论系统必须要求用户有 GitHub 账户,并且我并不乐意「滥用」GitHub issue 的功能,我认为一个功能就有一个功能设计的目的和意义,GitHub issue 的功能是为项目上报问题或围绕项目展开的讨论而非针对内容本身所以这一类的也就不采用了。这一类的服务有:
  • 另外剩下来的一大类是提供自建的方案,需要自己在云服务,Heroku,或者 VPS 上自建的,需要依赖 PostgreSQL 或 SQLite 这类数据库,这一类的评论系统往往实现了评论接口。
    • Commento 需要 PostgreSQL
    • Remark42,[[Remark42]] 可以自行搭建,Go 实现,非常简洁轻便,可以嵌在任何需要评论的地方。支持常用的社交账号登录,匿名留言,多级留言。可以从 Disqus 或 WordPress 导入数据,支持邮件,Telegram 等通知。
    • Isso,支持匿名,有简单的管理后台,支持导入 Disqus 评论, Python 实现
  • 最后剩下一个比较特殊的,就是 Staticman 它将评论系统的评论部分拆成纯文本的数据,提交到静态博客的项目中,当用户发起评论后会自动提交一个 comment,或者发起一个 Pull Request 将内容保存下来。

综合比较下来因为已经排除了第一、二两类,在自建的服务中 Isso 和 Remark42 看着非常轻便,即使自建,使用 Docker 也非常快。并且 Remark42 更加强大一些,所以就选 Remark42 了。

Remark42 搭建

Remark42 是使用 Go 编写,并且提供了 Docker 部署方式,一个 docker-compose 文件搞定:

version: "3"

services:
  remark42:
    image: umputun/remark42:latest
    container_name: "remark42"
    restart: always

    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
    environment:
      - REMARK_URL=${URL}
      - SITE=${SITE}
      - SECRET=${SECRET}
      - STORE_TYPE=bolt
      - STORE_BOLT_PATH=/srv/var/db
      - BACKUP_PATH=/srv/var/backup
      - CACHE_TYPE=mem
      - DEBUG=true
      - AUTH_TELEGRAM=${AUTH_TELEGRAM}
      - TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
      - AUTH_ANON=true
      - ADMIN_PASSWD=${ADMIN_PASSWD}
      - VIRTUAL_HOST=${YOUR_DOMAIN}
      - VIRTUAL_PORT=8080
      - LETSENCRYPT_HOST=${YOUR_DOMAIN}
      - LETSENCRYPT_EMAIL=${YOUR_EMAIL}
    volumes:
      - ${STORAGE_PATH}:/srv/var

networks:
  default:
    external:
      name: nginx-proxy

我使用 nginx-proxy 做域名转发,以及 SSL 证书自动生成。

导入 Disqus 数据

Disqus 提供导出评论到一个压缩包的工具,可以在 Disqus Admin > Setup > Export 找到 1

docker exec -it remark42 import -p disqus -f /srv/var/xxxx-2021-10-08T12_57_49.488908-all.xml -s site_id

通过以上命令导入。

reference


2021-10-08 comment , disqus , isso , python , jekyll , static-website

club 域名宕机近 3 小时故障回顾

刚开始的时候收到了报警,说网站挂了,我的第一反应是 VPS 出问题了,赶紧 SSH 登录上去看,好像都正常。难道是 VPS 网络问题,于是看了看同一台机器上的其他服务,一切都没问题。

然后开始排查为什么登录不上,先看了一 DNS 解析,发现在我本地已经无法给出 DNS 解析的结果了,返回 SERVFAIL。

❯ nslookup www.techfm.club
Server:		192.168.2.1
Address:	192.168.2.1#53

** server can't find www.techfm.club: SERVFAIL

我下意识的还以为遭到 GFW DNS 污染了,用其他地区的 VPS nslookup 了一下发现是同样的错误。

于是我把怀疑点移到了 Cloudflare,但是登录 Cloudflare 发现其他域名都正常,并且查看 Cloudflare 的 status 页面也没有说有服务故障,然后就去 Help 里面想要联系一下客服寻求帮助一下,不过 Cloudflare 的页面做的很人性化,首先提供了自查故障的页面,所以自查了一下,Cloudflare 说:

The authoritative nameservers for techfm.club are set incorrectly. For Cloudflare to activate, your domain registrar must point to the two nameservers provided by Cloudflare as the authoritative servers. Set your authoritative nameservers in your registrar’s admin panel (contact your registrar for support). Review changing your nameservers.

Name servers 服务器设置错误?我今天根本没有动过 Google Domains 的 NS 设置啊。不过还是按照帮助文档中的内容查询了一下域名的 NS,发现真的获取不到 NS 地址了。

einverne@sys ~ % dig techfm.club +trace @1.1.1.1

; <<>> DiG 9.16.1-Ubuntu <<>> techfm.club +trace @1.1.1.1
;; global options: +cmd
.                       515539  IN      NS      a.root-servers.net.
.                       515539  IN      NS      b.root-servers.net.
.                       515539  IN      NS      c.root-servers.net.
.                       515539  IN      NS      d.root-servers.net.
.                       515539  IN      NS      e.root-servers.net.
.                       515539  IN      NS      f.root-servers.net.
.                       515539  IN      NS      g.root-servers.net.
.                       515539  IN      NS      h.root-servers.net.
.                       515539  IN      NS      i.root-servers.net.
.                       515539  IN      NS      j.root-servers.net.
.                       515539  IN      NS      k.root-servers.net.
.                       515539  IN      NS      l.root-servers.net.
.                       515539  IN      NS      m.root-servers.net.
.                       515539  IN      RRSIG   NS 8 0 518400 20211020050000 20211007040000 14748 . Ivt+gf/MP9jMrhxG7kVEO6LfUeGvL6RaeaR4b19+hakqU2FplgG2DSMf ycLHYn2zaBPyyZysSh1AbgWO7L2nRZj5yMQB6A7IFR3ifp1ksCTDbtUf 4X0rzwzZcv2BVbJBsDAjVVdAFxVsnfX6siOx9JLxshe1/JECAaRoXo4X Fl5JTeEN+s+WBZdOShKmvkILGRt9UkMeFton3dIP47ZBvnlgmMGkv9Jw VZHQmzdufQSfta0HtjPwN+/mzlH6nnGs4beqlhsIAttzQALgzcspCjP+ NenqtiXTxg7jvtP8Dy/JkTYbecQX+mcL19ySGDoBkGov2RSfJURdXgrN PN7QZA==
;; Received 1097 bytes from 1.1.1.1#53(1.1.1.1) in 4 ms

club.                   172800  IN      NS      ns1.dns.nic.club.
club.                   172800  IN      NS      ns2.dns.nic.club.
club.                   172800  IN      NS      ns3.dns.nic.club.
club.                   172800  IN      NS      ns4.dns.nic.club.
club.                   172800  IN      NS      ns6.dns.nic.club.
club.                   172800  IN      NS      ns5.dns.nic.club.
club.                   86400   IN      DS      29815 8 2 3B67F899B57454E924DD1EFAE729B8741D61BA9BC8D76CD888919E5C 0950CA23
club.                   86400   IN      DS      29815 8 1 7F2B8E1D8B715BB382111A84F4552A599462017A
club.                   86400   IN      RRSIG   DS 8 1 86400 20211020050000 20211007040000 14748 . AQaz5Kne3pWNMUOyrCJ67y3q8mN0fe2cukuTY0oiyMJNi/OuL7eGxqiq 3RlfRL+Y9+50jOkEdw6170xKqeU/XAdyYRI9R6xQYTCZE2y+YSnHW81k PGrFVb4N8RfmD8/AX0RckRMzu4DqokMXnfYd2WFGqrNJtvWMGxDkdkxU PfJv0jHHBzV0s1YyS/UuFC9joaYGeZ8L81HVeQV0aZn7pz3+u794OQgf 0SpqbiiSuYJDGXvldA7ZkXA9Nd+pQAzd+DjJK8F4b68cuNrlmS3W923D iVUqFfPXXqx03pNuUfJPp7XAZNGsGrfrMEQSSl0LI01ct7FM2YilJkUx fF+thg==
couldn't get address for 'ns1.dns.nic.club': failure
couldn't get address for 'ns2.dns.nic.club': failure
couldn't get address for 'ns3.dns.nic.club': failure
couldn't get address for 'ns4.dns.nic.club': failure
couldn't get address for 'ns6.dns.nic.club': failure
couldn't get address for 'ns5.dns.nic.club': failure
dig: couldn't get address for 'ns1.dns.nic.club': no more
einverne@sys ~ % dig techfm.club +trace @1.1.1.1

; <<>> DiG 9.16.1-Ubuntu <<>> techfm.club +trace @1.1.1.1
;; global options: +cmd
.                       511583  IN      NS      a.root-servers.net.
.                       511583  IN      NS      b.root-servers.net.
.                       511583  IN      NS      c.root-servers.net.
.                       511583  IN      NS      d.root-servers.net.
.                       511583  IN      NS      e.root-servers.net.
.                       511583  IN      NS      f.root-servers.net.
.                       511583  IN      NS      g.root-servers.net.
.                       511583  IN      NS      h.root-servers.net.
.                       511583  IN      NS      i.root-servers.net.
.                       511583  IN      NS      j.root-servers.net.
.                       511583  IN      NS      k.root-servers.net.
.                       511583  IN      NS      l.root-servers.net.
.                       511583  IN      NS      m.root-servers.net.
.                       511583  IN      RRSIG   NS 8 0 518400 20211020050000 20211007040000 14748 . Ivt+gf/MP9jMrhxG7kVEO6LfUeGvL6RaeaR4b19+hakqU2FplgG2DSMf ycLHYn2zaBPyyZysSh1AbgWO7L2nRZj5yMQB6A7IFR3ifp1ksCTDbtUf 4X0rzwzZcv2BVbJBsDAjVVdAFxVsnfX6siOx9JLxshe1/JECAaRoXo4X Fl5JTeEN+s+WBZdOShKmvkILGRt9UkMeFton3dIP47ZBvnlgmMGkv9Jw VZHQmzdufQSfta0HtjPwN+/mzlH6nnGs4beqlhsIAttzQALgzcspCjP+ NenqtiXTxg7jvtP8Dy/JkTYbecQX+mcL19ySGDoBkGov2RSfJURdXgrN PN7QZA==
;; Received 1097 bytes from 1.1.1.1#53(1.1.1.1) in 4 ms

club.                   172800  IN      NS      ns5.dns.nic.club.
club.                   172800  IN      NS      ns6.dns.nic.club.
club.                   172800  IN      NS      ns3.dns.nic.club.
club.                   172800  IN      NS      ns1.dns.nic.club.
club.                   172800  IN      NS      ns2.dns.nic.club.
club.                   172800  IN      NS      ns4.dns.nic.club.
club.                   86400   IN      DS      29815 8 2 3B67F899B57454E924DD1EFAE729B8741D61BA9BC8D76CD888919E5C 0950CA23
club.                   86400   IN      DS      29815 8 1 7F2B8E1D8B715BB382111A84F4552A599462017A
club.                   86400   IN      RRSIG   DS 8 1 86400 20211020050000 20211007040000 14748 . AQaz5Kne3pWNMUOyrCJ67y3q8mN0fe2cukuTY0oiyMJNi/OuL7eGxqiq 3RlfRL+Y9+50jOkEdw6170xKqeU/XAdyYRI9R6xQYTCZE2y+YSnHW81k PGrFVb4N8RfmD8/AX0RckRMzu4DqokMXnfYd2WFGqrNJtvWMGxDkdkxU PfJv0jHHBzV0s1YyS/UuFC9joaYGeZ8L81HVeQV0aZn7pz3+u794OQgf 0SpqbiiSuYJDGXvldA7ZkXA9Nd+pQAzd+DjJK8F4b68cuNrlmS3W923D iVUqFfPXXqx03pNuUfJPp7XAZNGsGrfrMEQSSl0LI01ct7FM2YilJkUx fF+thg==
couldn't get address for 'ns5.dns.nic.club': failure
couldn't get address for 'ns6.dns.nic.club': failure
couldn't get address for 'ns3.dns.nic.club': failure
couldn't get address for 'ns1.dns.nic.club': failure
couldn't get address for 'ns2.dns.nic.club': failure
couldn't get address for 'ns4.dns.nic.club': failure
dig: couldn't get address for 'ns5.dns.nic.club': no more

并且 club 默认的 6 台 NS 全部都返回 failure。而正常的域名会返回默认配置的 NS:

einverne@sys ~ % dig gtk.pw +trace @1.1.1.1

; <<>> DiG 9.16.1-Ubuntu <<>> gtk.pw +trace @1.1.1.1
;; global options: +cmd
.                       518159  IN      NS      a.root-servers.net.
.                       518159  IN      NS      b.root-servers.net.
.                       518159  IN      NS      c.root-servers.net.
.                       518159  IN      NS      d.root-servers.net.
.                       518159  IN      NS      e.root-servers.net.
.                       518159  IN      NS      f.root-servers.net.
.                       518159  IN      NS      g.root-servers.net.
.                       518159  IN      NS      h.root-servers.net.
.                       518159  IN      NS      i.root-servers.net.
.                       518159  IN      NS      j.root-servers.net.
.                       518159  IN      NS      k.root-servers.net.
.                       518159  IN      NS      l.root-servers.net.
.                       518159  IN      NS      m.root-servers.net.
.                       518159  IN      RRSIG   NS 8 0 518400 20211020050000 20211007040000 14748 . Ivt+gf/MP9jMrhxG7kVEO6LfUeGvL6RaeaR4b19+hakqU2FplgG2DSMf ycLHYn2zaBPyyZysSh1AbgWO7L2nRZj5yMQB6A7IFR3ifp1ksCTDbtUf 4X0rzwzZcv2BVbJBsDAjVVdAFxVsnfX6siOx9JLxshe1/JECAaRoXo4X Fl5JTeEN+s+WBZdOShKmvkILGRt9UkMeFton3dIP47ZBvnlgmMGkv9Jw VZHQmzdufQSfta0HtjPwN+/mzlH6nnGs4beqlhsIAttzQALgzcspCjP+ NenqtiXTxg7jvtP8Dy/JkTYbecQX+mcL19ySGDoBkGov2RSfJURdXgrN PN7QZA==
;; Received 1097 bytes from 1.1.1.1#53(1.1.1.1) in 0 ms

pw.                     172800  IN      NS      ns1.nic.pw.
pw.                     172800  IN      NS      ns6.nic.pw.
pw.                     172800  IN      NS      ns2.nic.pw.
pw.                     172800  IN      NS      ns5.nic.pw.
pw.                     86400   IN      DS      26645 7 2 7EF397EDF4D7CA228C0F5111F5E1696CDBF279C0B6AFA48FC7E71A12 E07E5880
pw.                     86400   IN      DS      26645 7 1 58EE332D303E2A64B7449C43AB770DAA1CA74C40
pw.                     86400   IN      RRSIG   DS 8 1 86400 20211020050000 20211007040000 14748 . ZKSbdDYOAuZYYX7LFUI6fZn6GtHJHrA04nENEPp6oGcGIh7IliGFyJai MkV6OfwYhyk6npWLaSNkYaU2Kv9mif6Bu1RBPbGbVaQphhFeqxmFRtf8 5B/Q+V6dYZJ8cnMZEMeuqlvfBzT6m+Dv6zsgvJ3dZ2Yly9ehkd9i2pXT F9Hv4mj+35B4r6H0/e1hlD8a0AmMITFPIAZ+ZQLkGaCf+d8jAP9oMIEG 2uezoE4PLybmCsovtT/zFcyrIXv0CLphN1Ky6yCkwu1nDMvWi3eoyunK ANPojlC6i3OCa7zmBuR+4qJWQeb9o5mqz+QXHkrPY/LEK8Vs9+t+xuzG ZzRc6Q==
;; Received 686 bytes from 192.5.5.241#53(f.root-servers.net) in 288 ms

gtk.pw.                 3600    IN      NS      vera.ns.cloudflare.com.
gtk.pw.                 3600    IN      NS      phil.ns.cloudflare.com.
5njihdv29htfqesp4s66h5ia7mau40g2.pw. 3600 IN NSEC3 1 1 1 - 5NJN8B0GFH3C6U7E54SIUSFMKRA3164C NS SOA RRSIG DNSKEY NSEC3PARAM
5njihdv29htfqesp4s66h5ia7mau40g2.pw. 3600 IN RRSIG NSEC3 7 2 3600 20211014233500 20210914093758 20159 pw. cGysLwA8FKKv9t+B0ywJA1yUNvytR6vINedx6Lz4ZPwsdBX0DTkn0OUM xR97Mxo58SoGCzTImM8JFsXJGid6j6txWh5KYN0NsmOd52sAOYXTz6uz m/fTDFMIXdLp8XJeRP8hGGAsdd7W7dhQTo8r4V1Rsc1JT3n33AEX7CAq Z5g=
vum0mlvs55o2lfpa00pfb93sl2dc98de.pw. 3600 IN NSEC3 1 1 1 - VUMSNHHGG0TDGRB3VN24B7GKEAA1IVGG TXT RRSIG
vum0mlvs55o2lfpa00pfb93sl2dc98de.pw. 3600 IN RRSIG NSEC3 7 2 3600 20211018221437 20210918161358 20159 pw. BPjMNyd1u4ci+m+FkCaVI+nW6gA+MPNPtNHdSJWwmCJN0GqYVgFNvj97 qTI1Jc/TiorDmURxE7zORU5IaI4K6XJG2ckpiq6xw+khy850dvAs2WVE ZI+uDc+nX4yFj7pvDJBiiNZR+Z9yAtDdvm1EomB0E91KBnGdZbBhYOsd qJk=
;; Received 601 bytes from 212.18.249.12#53(ns6.nic.pw) in 16 ms

gtk.pw.                 300     IN      A       104.21.51.157
gtk.pw.                 300     IN      A       172.67.182.127
;; Received 67 bytes from 2606:4700:50::adf5:3a93#53(vera.ns.cloudflare.com) in 0 ms

立马登录 Google Domains 查看 NS 设置,页面上还是 Cloudflare 提供的两个 NS 服务器地址,看着也没有问题,所以又联系了 Google Domains 的 Help,Google Domains 的帮助人员还是非常快的就能联系上,帮忙查询了一下 NS 设置,用 https://www.whatsmydns.net/#NS/ 查询了一下了全球的 NS,全部失败:

客服解释说需要时间等待配置传播生效,但问题在于我没有更改过任何配置。这个时候我就有看到相关的消息出来(source1, source2source3),这才发现不是我一个人的问题。无奈好像我也无法解决,只能等上游解决了。

终于从 10月7号下午 6:52 开始,到 9:23 分为止,宕机了近 3 小时。

原因分析

.club 通用顶级域名(gTLD) 的 name server 无响应,所有 6 台官方的服务器 get.club 都无响应,所以下游的 DNS 服务器都无法解析。

This morning there was a DNS service disruption impacting .Club websites. The issue has now been resolved. We apologize for any inconvenience this may have caused.

— .CLUB Domains (@getDotClub) October 7, 2021

GoDaddy Registry tweeted:

This morning there was a DNS service disruption impacting .club websites. The issue has now been resolved. We apologize for any inconvenience this may have caused.

— GoDaddy Registry (@GoDaddyRegistry) October 7, 2021


2021-10-07 domain , name-server , google-domains , cloudflare , dns , network

在 JetBrains IntelliJ IDEA 中使用 GitHub Copilot

虽然之前早早的就把 GitHub Copilot 在 Visual Studio Code 上用了起来,但是平时使用的 IDE 还是 IntelliJ IDEA 比较多,今天刷 Twitter 看到有人分享说在 IntelliJ IDEA 上可以通过添加 preview 的 plugin 源来添加 GitHub Copilot 插件支持,搜了一下果然可以。

具体的教程可以参考GitHub

主要的步骤就是通过在插件管理里面添加 Plugin repository:

https://plugins.jetbrains.com/plugins/super-early-bird/list

然后重启之后搜索 github copilot 安装启用。不过需要注意的是该插件只有在 IDEA 2021.2 及以上版本中才能安装。

安装之后在 Tools -> GitHub Copilot 中登录,启用。

快捷键

记录整理一些常用的快捷键。在默认情况下,Tab 就是选中默认的。Esc 是取消建议。

  • Option(macOS)/Alt(Windows/Linux) + [] 可以选择上一条或下一条建议
  • Option(macOS)/Alt(Windows/Linux) + Enter 可以查看更多的建议
  • Trigger inline suggestion: Alt + \ or Option + \

2021-10-06 jetbrains , intellij-idea , github , github-copilot

Rime 输入法中的快捷键

今天在整理 Rime 插件使用的时候,想起来整理一下 Rime 输入法的快捷键。

在之前整理 Rime 基础配置 的时候稍微带到一下。

通过配合这些快捷键可以在输入很长一段句子的时候提升体验。

  • ctrl+grave (grave) tab 键上面,1 左边的那个键用来切换 Rime 输入方案
  • shift+delete 删除选中的候选词,一般用来调整不希望在候选词前的词
  • ctrl+ n/p 上下翻页选择候选词
  • Ctrl+b/f 类似于左箭头,右箭头,可以快速调整输入,在输入很长一段后调整之前的输入时非常有效
  • Ctrl+a/e 贯标快速跳转到句首或者句末
  • Ctrl+d 删除光标后内容
  • Ctrl+h 回退,删除光标前内容
  • Ctrl+g 清空输入
  • Ctrl+k 删词,等效于 Shift + delete(macOS 上可以使用 ⌘+k)
  • -/+ 或者 tab 来翻页

更多的快捷键可以在 default.yaml 配置中看到。

key_binder:
  bindings:
    - {accept: "Control+p", send: Up, when: composing}
    - {accept: "Control+n", send: Down, when: composing}
    - {accept: "Control+b", send: Left, when: composing}
    - {accept: "Control+f", send: Right, when: composing}
    - {accept: "Control+a", send: Home, when: composing}
    - {accept: "Control+e", send: End, when: composing}
    - {accept: "Control+d", send: Delete, when: composing}
    - {accept: "Control+k", send: "Shift+Delete", when: composing}
    - {accept: "Control+h", send: BackSpace, when: composing}
    - {accept: "Control+g", send: Escape, when: composing}
    - {accept: "Control+bracketleft", send: Escape, when: composing}
    - {accept: "Alt+v", send: Page_Up, when: composing}
    - {accept: "Control+v", send: Page_Down, when: composing}
    - {accept: ISO_Left_Tab, send: Page_Up, when: composing}
    - {accept: "Shift+Tab", send: Page_Up, when: composing}
    - {accept: Tab, send: Page_Down, when: composing}
    - {accept: minus, send: Page_Up, when: has_menu}
    - {accept: equal, send: Page_Down, when: has_menu}
    - {accept: comma, send: Page_Up, when: paging}
    - {accept: period, send: Page_Down, when: has_menu}
    - {accept: "Control+Shift+1", select: .next, when: always}
    - {accept: "Control+Shift+2", toggle: ascii_mode, when: always}
    - {accept: "Control+Shift+3", toggle: full_shape, when: always}
    - {accept: "Control+Shift+4", toggle: simplification, when: always}
    - {accept: "Control+Shift+5", toggle: extended_charset, when: always}
    - {accept: "Control+Shift+exclam", select: .next, when: always}
    - {accept: "Control+Shift+at", toggle: ascii_mode, when: always}
    - {accept: "Control+Shift+numbersign", toggle: full_shape, when: always}
    - {accept: "Control+Shift+dollar", toggle: simplification, when: always}
    - {accept: "Control+Shift+percent", toggle: extended_charset, when: always}
    - {accept: "Shift+space", toggle: full_shape, when: always}
    - {accept: "Control+period", toggle: ascii_punct, when: always}

自定义快捷键

在上面的配置中可以看到 Rime 默认就定义了非常多的快捷键绑定,并且这些快捷键都可以通过配置改变。

这里做一个例子,比如平时用 Vim 较多,想要更换成更加舒服的 Vim 绑定,可以在 Rime 配置根目录中 vi default.custom.yaml 中配置:

# default.custom.yaml
patch:
	# 其他配置...
	key_binder:
		bindings:
      	- { when: has_menu, accept: "Control+k", send: Page_Up }
      	- { when: has_menu, accept: "Control+j", send: Page_Down }
      	- { when: has_menu, accept: "Control+h", send: Left }
      	- { when: has_menu, accept: "Control+l", send: Right }

这样就可以使用 Ctrl+j/k 来上下翻页,而使用 Ctrl+h/l 来左右切换候选词。不过我个人还是还是习惯默认设置的快捷键。

配置说明

在上面的例子中可以看到 bindings 配置中有三个配置选项。每一条 binding 下面可以包含:

  • accept,实际接受的按键
  • send,输出效果
  • toggle,切换开关
  • when,作用范围

toggle 的候选项有:

ascii_mode
ascii_punct
full_shape 全角字符
simplification 繁简
extended_charset

when 可以接受的选项有:

paging	翻页
has_menu	操作候选项用
composing	操作输入码用
always	全域

accept 和 send 可用字段除 A-Za-z0-9 外,还可以包含键盘上实际的所有按键:

BackSpace	退格
Tab	水平定位符
Linefeed	换行
Clear	清除
Return	回車
Pause	暫停
Sys_Req	印屏
Escape	退出
Delete	刪除
Home	原位
Left	左箭頭
Up	上箭頭
Right	右箭頭
Down	下箭頭
Prior、Page_Up	上翻
Next、Page_Down	下翻
End	末位
Begin	始位
Shift_L	左Shift
Shift_R	右Shift
Control_L	左Ctrl
Control_R	右Ctrl
Meta_L	左Meta
Meta_R	右Meta
Alt_L	左Alt
Alt_R	右Alt
Super_L	左Super
Super_R	右Super
Hyper_L	左Hyper
Hyper_R	右Hyper
Caps_Lock	大寫鎖
Shift_Lock	上檔鎖
Scroll_Lock	滾動鎖
Num_Lock	小鍵板鎖
Select	選定
Print	列印
Execute	執行
Insert	插入
Undo	還原
Redo	重做
Menu	菜單
Find	蒐尋
Cancel	取消
Help	幫助
Break	中斷
space
exclam	!
quotedbl	"
numbersign	#
dollar	$
percent	%
ampersand	&
apostrophe	'
parenleft	(
parenright	)
asterisk	*
plus	+
comma	,
minus	-
period	.
slash	/
colon	:
semicolon	;
less	<
equal	=
greater	>
question	?
at	@
bracketleft	[
backslash
bracketright	]
asciicircum	^
underscore	_
grave	`
braceleft	{
bar	|
braceright	}
asciitilde	~
KP_Space	小鍵板空格
KP_Tab	小鍵板水平定位符
KP_Enter	小鍵板回車
KP_Delete	小鍵板刪除
KP_Home	小鍵板原位
KP_Left	小鍵板左箭頭
KP_Up	小鍵板上箭頭
KP_Right	小鍵板右箭頭
KP_Down	小鍵板下箭頭
KP_Prior、KP_Page_Up	小鍵板上翻
KP_Next、KP_Page_Down	小鍵板下翻
KP_End	小鍵板末位
KP_Begin	小鍵板始位
KP_Insert	小鍵板插入
KP_Equal	小鍵板等於
KP_Multiply	小鍵板乘號
KP_Add	小鍵板加號
KP_Subtract	小鍵板減號
KP_Divide	小鍵板除號
KP_Decimal	小鍵板小數點
KP_0	小鍵板0
KP_1	小鍵板1
KP_2	小鍵板2
KP_3	小鍵板3
KP_4	小鍵板4
KP_5	小鍵板5
KP_6	小鍵板6
KP_7	小鍵板7
KP_8	小鍵板8
KP_9	小鍵板9

通过上面的组合就可以实现非常多自定义的功能,比如有人喜欢将 ; 绑定到第二个候选词:

{ when: has_menu, accept: ";", send: 2 }

这样当候选词出现在第二位时,直接按下 ; 就可以输入。

reference


2021-10-01 rime , shortcut , keybinding , input-method

用 Gatsby 写了一个券商推荐注册列表

放假在家,刚好刷 Twitter 的时候看到有人写了一个券商注册的页面,一个简单的列表,挂上了目前常用的美港股做新人推荐,顺便就看了一下源码,发现使用 [[Gatsby]] 和 [[TailwindCSS]] 写的,于是就抱着学习的态度,上手了一下 Gatsby,简单的了解了一下 Gatsby 也是一个静态网页生成器,用来 React 和 [[GraphQL]]。而 [[TailwindCSS]] 是一个 CSS 框架,通过简单的 class 就能实现不错的界面效果。

从了解一个新的技术,到上手的过程就不多说了,官网的新手教程非常详细。这里就想记录一下中间遇到的一些问题,以及解决的过程。以及结合之前看过的 [[费曼学习法]] 聊一聊学习的过程。

费曼学习法

首先先说一下什么叫做费曼学习法,如果用简单的话来说就是将复杂的知识简单化,用最通俗的语言去解释。

费曼学习法的具体内容很细致,但如果将学习划分几个步骤来的话,可以简化成这样几部:

  • 确定学习目标
  • 理解学习对象
  • 以输出代替输入
  • 回顾
  • 简化学习内容,再吸收

套用我了解学习 Gatsby 的过程,首先我的目标非常明确,就是制作一个简单的页面可以展示列表。

理解学习对象,在我看来就是通过查看其官网,文档,以及简单的 Wiki 了解 Gatsby 是什么,可以做什么。我这里要了解的内容有两部分,一个便是静态网站生成器 Gatsby,另一个是 CSS 框架 [[TailwindCSS]]。

静态网站生成器和 CSS 框架两个概念都不是很陌生,类似的技术也都非常多,当前的这个博客就是 Ruby 下的 Jekyll 生成的静态页面,而这个博客用的就是 Bootstrap CSS 框架。所以使用类比就能大致的对其有一定的了解。

第三步,用输出代替输入,这就是实践的部分。当然也是个人认为最重要的一步,再以前我往往就是忽视了这一步。所以直接根据官网的教程,先实现一遍新手任务,然后根据我的需要,先从官网初始化了一个最基本的项目,然后在本地跑起来,引入 Tailwind CSS,然后尝试对页面进行修改,先构建了一个基本的样式,将图中的一个样式实现,研究如何获取数据,如何实现循环。

最后定下的方案就是通过 YAML 文件 ,将页面中需要显示的内容配置在其中,因为 Gatsby 通过插件的方式可以非常方便的引入不同类型的数据源,所以看一下插件的介绍。在实践中花了最多时间的就是将图片动态的显示出来,直接使用 StaticImage 只能将固定的图片显示出来。所以继续阅读官网的文档。以 image 为关键字把所有搜出来的结果都看了,还是一头雾水,并且昨天状态也不太好就直接先睡了。今天起来从原理上简单的了解了一下 Gatsby 的插件,然后逐渐就有了一个思路。

在 Gatsby 中有三类插件:

  • 数据源插件,一般名字中会带 source,数据源插件负责从外部数据获取,然后将数据放在数据层中,方便应用通过 GraphQL 来获取使用
  • 数据转化插件, 名字中带 transformer,转换特定的格式,比如将 markdown 文件内容转换为对象
  • 功能插件,一般就叫做某某 plugin

我已经通过 gatsby-transformer-yaml 将 YAML 文件内容可以在界面中读取,剩下的就是将 YAML 配置中配置的文件路径在界面中展示出来,Gatsby 这一块做过一些 API 的调整,需要通过 GraphQL 查询出来之后再使用 GatsbyImage 来展示。

整个上面的过程完成之后就大致做了一个类似的列表页

回到费曼学习法的第四步,回顾,这一步也是正是我当前正在做的事情。

第五步,简化学到的内容,如果让我现在再去回忆我学到了什么,我脑海里首先会蹦出几个关键字,不是 Gatsby,也不是 [[TailwindCSS]],而是 React,GraphQL,基础的 CSS 长宽模型。我之前没有接触过 React,但是在使用的过程中大概的了解了其模块化的设计,并且界面和数据分离,通过 GraphQL 做数据绑定和交互整个这个过程就形成了 Gatsby 这个技术。而在使用 Tailwind CSS 的过程中最费我时间的就是调整页面中元素的位置。如果之前学习基础 CSS 的时候能够对 margin,padding 有更多的了解就好了。

什么是好的学习

在费曼学习法这本书中对学习的解释,让我对[[教育]]有了更加深刻的理解。上周末在吃饭的时候,朋友顺口聊起了什么是你认为的「好的教育」,在去探讨什么是「好的教育」之前,什么是学习,在过去的时间里,我常常认为记得比别人多,学的比人快就是好的学习。但学习,不是去比拼知识的储备,也不是去竞争学位的高低,而是思维方式的培养。学习不是为了记住什么,而是通过学习建立自己行之有效的思维方式,然后将知识应用到实际生活中去解决问题。那么回到我认为的「好的教育」,学习如果是主动发起的,那么往往会收获不错的结果。那么好的教育就是要让人激发出学习的兴趣。

在没有了解 Gatsby 之前我对静态网站的印象就停留在了 markdown 文件的渲染,但是我在了解的过程中发现 Gatsby 使用了一套不一样的实现思路,通过 GraphQL 的帮助,Gatsby 让 markdown 成为了其可以选择的一个数据源,只要做一层数据转换,那么其内容可以从任何的数据源中获取,比如从 WordPress 的数据库,从其他任何 CMS 的数据源。这样就使得 Gatsby 拥有了其他静态网站不一样的能力。

最后

代码在 https://github.com/einverne/broker 界面在 https://broker.einverne.info


2021-10-01 broker , invest , static-site , gatsby , react , graphql

电子书

本站提供服务

最近文章

  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。
  • 最棒的 Navidrome 音乐客户端 Sonixd(Feishin) Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。 Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。
  • 中心化加密货币交易所 Gate 注册以及认证 Gate.io 是一个中心化的加密货币交易所。Gate 中文通常被称为「芝麻开门」,Gate 创立于 2013 年,前身是比特儿,是一家致力于安全、稳定的数字货币交易所,支持超过 1600 种数字货币的交易,提供超过 2700 个交易对。
  • 不重启的情况下重新加载 rTorrent 配置文件 因为我在 Screen 下使用 rTorrent,最近经常调试修改 rtorrent.rc 配置文件,所以想要找一个方法可以在不重启 rTorrent 的情况重新加载配置文件,网上调查了一下之后发现原来挺简单的。
  • Go 语言编写的网络穿透工具 chisel chisel 是一个在 HTTP 协议上的 TCP/UDP 隧道,使用 Go 语言编写,10.9 K 星星。