Typecho 博客搭建尝试

拥有一台自己的服务器之后就尝试搞一个动态博客了,毕竟有后台管理会方便很多。看到 Typecho 原生支持 Markdown,而且有一个很漂亮、功能强大的主题 Handsome,就决定尝试将我的 Hexo 博客迁移到 Typecho Handsome 了。(20201027更新:感觉还是喜欢 Hexo 的博客,就转回去了,期间半年的评论数据大概有四五十条记录,似乎没有特别的价值就懒得从 Typecho 那边转到 Valine 评论里面了。)

1. Typecho 的个性化

1.1 自定义样式

主要的自定义工作我都通过加入自定义 CSS 和 JS 文件来完成了,具体代码可以参考我的 GitHub 项目 Typecho-AssetsBanbanStyle 插件。其中包含了比如随机彩色标签云、中英文字符件自动添加空格(pangu.js)、macOS 风格代码框、今日诗词、看板娘组件等等。

有几个样式或功能还需要一点点额外的配置:

  • 删除首页中间标题/usr/themes/handsome/index.php 删除以下代码
1
<h1 class="m-n font-thin text-black l-h"><?php $this->options->title(); ?></h1>
  • 添加今日诗词布局(需要配合 JS 文件加载今日诗词 SDK):在上述标签下方找到以下代码
1
2
3
4
5
6
7
8
9
10
11
12
echo '加载中……';
echo '
<script>
$.ajax({
type: \'Get\',
url: \'https://v1.hitokoto.cn/\',
success: function(data) {
var hitokoto = data.hitokoto;
$(\'.indexWords\').text(hitokoto);
}
});
</script>';

替换为:

1
2
3
4
echo '
<div class="poem-wrap">
<span id="poem_sentence">正在加载今日诗词....</span>
</div>';
  • 文章结尾显示标签/usr/themes/handsomepost.php 打赏模块结尾添加以下代码
1
2
3
4
5
<!--生成当前文章标签-->
<div id="tag_cloud-2" class="post-tags tags l-h-2x" style="text-align:center;margin-top:5px;">
<style>.post-tags a{font-size:12px!important;color:#fff!important;}</style>
<?php $this->tags(' ', true, '暂无标签'); ?>
</div>
  • 页脚更改/usr/themes/handsome/component/footer.php 第 5-13 行代码
1
2
3
4
5
6
7
8
9
10
11
<div class="wrapper bg-light">
<span class="pull-right hidden-xs text-ellipsis">
<?php $this->options->BottomInfo(); ?>
Powered by <a target="_blank" href="http://www.typecho.org">Typecho</a>&nbsp;|&nbsp;Theme by <a target="_blank"
href="https://www.ihewro.com/archives/489/">handsome</a>
</span>
<span class="text-ellipsis">
&copy;&nbsp;<?php echo date("Y");?> Copyright&nbsp;
<?php $this->options->BottomleftInfo(); ?>
</span>
</div>

修改为:

1
2
3
4
5
6
7
8
<div class="wrapper bg-light">
<span class="pull-right hidden-xs text-ellipsis">
<?php $this->options->BottomInfo(); ?>
</span>
<span class="text-ellipsis">
<?php $this->options->BottomleftInfo(); ?>
</span>
</div>

然后就可以直接在主题的「开发者设置」里添加需要的内容了,不会受到原本页脚内容的局限。

  • 自定义翻译:修改 /usr/themes/handsome/lang/ 文件夹的内容。

1.2 PJAX 回调函数

如果开启了 PJAX,一些每次刷新页面都要执行的脚本需要加入回调。目前我根据自定义的 JS 文件设置了以下回调函数。其中最后一条是因为开启了图片延迟加载后,相册页缩略图无法显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// pangu.js
pangu.spacingPage();

// colorful tag cloud
colorful_tags();

// macOS 代码框
if (typeof Prism !== 'undefined') {
Prism.highlightAll(true,null);
}

// 今日诗词
if ($("div").hasClass("poem-wrap")) {
get_poem()
}

// 相册缩略图
$(".album-thumb img").lazyload({
effect: "fadeIn",
threshold: "200"
});

1.3 FancyBox 缩略图

就如在用 Hexo 时候进行的修改一样,我希望在使用 FancyBox 时候预览的缩略图是小图,点开后可以加载原图,这样就需要修改 /usr/themes/handsome/assets/js/core.min.js 文件。因为是经过压缩后的版本,所以先恢复回有缩进的版本,然后修改 seFancyBox 函数的定义。

找到 seFancyBox 函数最后一句:

1
j += i, "undefined" !== f ? b.prop("outerHTML", '<a class="light-link img-blur" data-fancybox="gallery" style="background-image: url(' + g + ')" no-pjax data-type="image" data-caption="' + c + '" href="' + g + '">' + j + "</a>") : b.prop("outerHTML", '<a class="light-link" data-fancybox="gallery" no-pjax data-type="image" data-caption="' + c + '" href="' + g + '">' + j + "</a>")

在这一句之前添加(以适配我自己设置的腾讯云对象存储剪裁后缀,如 !500x):

1
g = g.replace(/![0-9]{3,}x/,"");

然后再对 core.min.js 文件进行压缩保存即可。

2. Valine 评论迁移到 Typecho

之前是使用 Valine 搭配 LeanCloud 的评论系统,要迁移到 Typecho 还是蛮复杂的,毕竟没有现成的工具,而且评论的 id 和文章的 id 与 Typecho 格式也不一致。目前是参考一个 Valine 转 Wordpress 评论的脚本,自己修改了代码。脚本需要先安装 jq 这个 JSON 文件处理包,然后可以实现 JSON 转为 SQL 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env bash
# by @lei2rock
# Valine to Typecho
# 2020-04-22

comfile="$1"

line=$(grep comment $comfile |wc -l)
author_mail="这里填写自己的邮箱地址"
author_url="这里填写自己的博客链接"
#author_ip="这里填写自己的ip"

comment() {
for ((i=0; i<$line; i++)); do
let j=$i+1
printf "第 $j 条评论\n"
coid=$(jq -r ".results[$i].objectId" $comfile)
cid=$(jq -r ".results[$i].url" $comfile)
cid='['$cid']'
author=$(jq -r ".results[$i].nick" $comfile)
mail=$(jq -r ".results[$i].mail" $comfile)
url=$(jq -r ".results[$i].link" $comfile | sed 's/^null$//')
ip=$(jq -r ".results[$i].ip" $comfile | sed 's/^null$//')
dateYMD=$(jq -r ".results[$i].createdAt" $comfile | sed 's/T/ /; s/\.[0-9]\{3\}Z//')
date=$(date -d "$dateYMD" +%s)
parent=$(jq -r ".results[$i].rid" $comfile | sed 's/^null$/0/')
text=$(jq -r ".results[$i].comment" $comfile)
agent=$(jq -r ".results[$i].ua" $comfile)
if [[ $mail == $author_mail ]]; then
authorId=1
ownerId=1
url=$author_url
# ip=$author_ip
else
authorId=0
ownerId=0
fi
if [[ $j == $line ]]; then
echo "($coid, ""'""$cid""'"", $date, ""'""$author""'"", $authorId, $ownerId, ""'""$mail""'"", ""'""$url""'"", ""'""$ip""'"", ""'""$agent""'"", ""'""$text""'"", ""'""comment""'"", ""'""approved""'"", $parent);">>$comfile.sql
else
echo "($coid, ""'""$cid""'"", $date, ""'""$author""'"", $authorId, $ownerId, ""'""$mail""'"", ""'""$url""'"", ""'""$ip""'"", ""'""$agent""'"", ""'""$text""'"", ""'""comment""'"", ""'""approved""'"", $parent),">>$comfile.sql
fi
done
}

touch $comfile.sql
echo "INSERT INTO \`typecho_comments\` (\`coid\`, \`cid\`, \`created\`, \`author\`, \`authorId\`, \`ownerId\`, \`mail\`, \`url\`, \`ip\`, \`agent\`, \`text\`, \`type\`, \`status\`, \`parent\`) VALUES">>$comfile.sql
comment
echo done!

保存该文件命名为 valine2typecho.sh,重命名 LeanCloud 导出的 Valine 评论文件为 comment.json,然后在这两个文件所在目录执行以下命令:

1
sh valine2typecho.sh comment.json

之后可能需要一些手工调整(所以评论数少的话可以用):例如调整评论的 coid、文章的 cid,修正一些 SQL 可能不支持的格式(比如部分字符要转义),一些 html 标签要在后台评论设置中添加支持,修改部分评论的 parent 值以匹配相应上级评论等等,具体可以参考 MySQL 导出的数据库文件格式。

导入评论后文章统计的评论数量可能不准确,可以先备份数据库,然后数据库中执行以下语句:

1
UPDATE typecho_contents t1 SET t1.commentsNum = (select count(*) from typecho_comments t2 where t2.cid = t1.cid)

3. 插件介绍

介绍一下自己在用的插件,也推荐了一些其他不错的插件,都是在 GitHub 上开源免费的插件。

3.1 在使用的插件

插件介绍
BanbanStyle为我自己的博客设置而开发的插件,功能见 GitHub 项目介绍
CommentToMail评论邮件通知,可以参考我的邮件模板:通知博主通知访客
HandsomeHandsome 主题配套插件
Sitemap自动生成网站地图
cosUploadV5上传文件、图片到腾讯云对象存储并提供链接
MemorialDay自己开发的哀悼日开启全站黑白滤镜

3.2 其他推荐的插件

插件介绍
Comment2Wechat评论推送至微信,依托 Server 酱实现
Comment2Telegram评论推送至 Telegram,支持回复评论、通过评论、垃圾评论和删除评论
macOScode自己开发的实现 macOS 风格代码框,应该只适配 Handsome 主题
EditorMD比原生更好用的 Markdown 编辑器(可以只使用编辑器功能)

4. Handsome 主题时光机

Handsome 主题内置了一个「时光机」功能,可以类似微博、微信朋友圈一样发布动态,而且支持通过微信公众号、Chrome 插件等多平台发布。同时,还可以在该页面上利用 RSS 订阅源把自己的微博和 Twitter 等平台的动态同步显示。

4.1 多平台发布动态

Handsome 开发者提供了一个微信公众号发布的渠道,但是这样需要用到别人的服务,而且后期如果要添加新的网站等都需要在该平台修改,不是很方便。既然有自己的服务器,还有自己的微信公众号,那就不如自己搭建一个。

  1. 首先下载 wechat_for_handsome 项目到自己服务器目录,我们可以添加一个新的网站和域名来部署,同时需要创建一个对应的数据库。

  2. 在安装该服务前,我们需要先去微信公众号后台获取必要的设置参数。在微信公众号后台「开发-基本配置」处获取 AppID 和 AppSecret。然后我们参照微信公众号的文档,使用刚才获得的两个参数在 微信公众平台接口调试工具 获取 access_token

    服务器配置

  3. 接下来回到微信公众号后台「开发-基本配置」处启用服务器配置。服务器地址(URL)填写为「项目域名/server.php」;令牌(Token)就是第二步得到的 access_token;消息加解密密钥(EncodingAESKey)可以随机生成但是一定要记录,因为我们还需要在项目安装时候填写;消息加解密方式可以选择「安全模式」。因为还没有在服务器上安装,所以先不要提交,放一边,进入下一步。

  4. 回到之前添加的站点,访问「项目域名/install.php」进入安装步骤。依次输入要求的几个参数,这些我们都通过上面的几个步骤获得了,设置完毕后安装即可。这样就可以回到第三步的公众号服务器配置,提交验证。

  5. 然后向公众号发送「绑定」,点击链接填写相关信息进行绑定,绑定完毕就可以参考主题文档的说明发布动态了。如果没有反应,可能是刚刚验证完毕,稍微等一会儿就好。其中,时光机的 cid 可以在后台独立页面管理处获得,验证编码可以在主题设置处自行设置一个当作访问密码。

  6. 如果公众号启用了「服务器配置」后,自定义的菜单不见了,可以在功能插件设置处重新启用即可。Chrome 扩展发布动态的方法可以直接参考主题文档配置。

4.2 添加 RSS 订阅源

时光机上要显示自己的微博或者 Twitter 的动态,可以借助 RSSHub 生成的订阅源。RSSHub 的部署可以参考我之前的文章,其中 Twitter 还需要去申请获得 API 相应的 key 才可以访问。

当然,使用的时候不能直接填写这个订阅源,因为会被禁止跨域访问,这时候可以在该订阅源链接前面加上 https://cors-anywhere.herokuapp.com/。例如我们要订阅微博的 RSS 订阅源链接是 https://rsshub.app/weibo/user/{weibo_user_id},那么为了方便跨域访问,可以在时光机设置时候填写:

1
https://cors-anywhere.herokuapp.com/https://rsshub.app/weibo/user/{weibo_user_id}

当然,可能通过访问 herokuapp.com 来获取订阅源速度比较慢,那就可以自行部署这个 CORS Anywhere 服务。方法也很简单,以使用宝塔面板为例,因为部署该项目使用的是 Nodejs 环境,需要先安装 Nodejs,方法可以参考我之前的文章。

然后下载上述项目源码到需要部署的服务器目录,项目根目录下创建环境变量设置文件 .env,编辑文件添加环境变量(具体含义用法见该项目文档),例如:

1
2
PORT = 1400
CORSANYWHERE_WHITELIST = https://blog.dlzhang.com,https://rss.zdl.one

之后安装所需要的依赖(以下命令二选一):

1
2
3
4
5
6
7
# yarn 安装方式
yarn
yarn add dotenv # 调用环境变量文件需要的依赖

# npm 安装方式
npm install
npm install dotenv --save # 调用环境变量文件需要的依赖

修改启动文件 server.js,在最开头加入以下内容,以便在最开始就引入环境变量文件的参数:

1
2
3
// Import .env
let dotenv = require('dotenv');
dotenv.config('./env');

可以手动启动,或者用「PM2 管理器」启动,选择启动文件为 server.js 即可。

1
node server.js

最后,为服务设置反向代理,使得可以通过域名访问该服务。

5. 访问速度与安全

5.1 CDN

最开始是静态资源走腾讯云境内 CDN(存储在对象存储中),毕竟腾讯云每个月有 10G 免费境内 CDN 流量包可以使用。但是测试和实际使用发现,如果只是使用境内 CDN 分发静态资源,海外访问的时候会有「云减速」的效果,但是如果开启全球加速,海外访问的流量费也是一笔钱。

而如果选择 Cloudflare 的 CDN,似乎对境内访问有「云减速」的效果。如果是 DNS 双线解析,境内直接访问服务器,境外访问 Cloudflare 的 CDN(可以通过 Cloudflare Partner 面板实现 CNAME 接入), 可以让海外访问走 CDN,不过感觉对境内一样是源站访问的话用 CDN 意义不算很大。而且,因为目前服务器和我同地域,访问延迟特别低,体验很好,加上 Cloudflare 的 CDN,纵使是境外也感觉有「云减速」的效果,似乎没有给我选择最近的节点或者是回源获取资源了吧。

反正经过几番尝试,最后还是只选择了主题的静态文件直接放到 GitHub,借助 jsDelivr 的 CDN 进行分发。自定义的静态文件也放到了 GitHub,但主要是版本控制的目的,毕竟 jsDelivr 的 CDN 缓存刷新速度不是那么快的。我采用 GitHub Actions 同步到腾讯云对象存储中,直接用对象存储的链接进行访问,感觉这样并没有比采用加速域名走 CDN 慢。

目前没有用 memcached 或者 redis 作内容缓存,其一是并没有很大的并发需求,其二是 Typecho 没有好用的插件(或多或少有一些问题)。

5.2 Google BBR

Google BBR 是一个 TCP 加速优化工具,可用于优化TCP连接,根据介绍开启可以加快访问的网速,这里参考了 Rat 介绍的方法

1
2
3
4
5
6
7
8
9
10
11
12
# 修改系统变量
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf

# 保存生效开启 BBR
sysctl -p

# 查看内核是否已开启 BBR
sysctl net.ipv4.tcp_available_congestion_control

# 查看 BBR 是否启动
lsmod | grep bbr

显示以下即内核已开启 BBR:

1
2
# sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = bbr cubic reno

显示类似以下内容即 BBR 启动成功:

1
2
# lsmod | grep bbr
tcp_bbr 20480 14

5.3 TLS 与 HSTS

宝塔面板安装最新版本,Nginx 安装 1.17 版本的话,应该已经支持了 TLS 1.3 协议,可以在 ssllabs.com 或者 myssl.com 测试一下自己的站点。为了在上述测试中实现 A 的评级,获得 SSL 证书后,我还在 Nginx 中禁用了 TLS 1.1 协议:删除服务器所有网站 Nginx 配置文件中的 TLSv1.1

1
2
-ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
+ssl_protocols TLSv1.2 TLSv1.3;