青江的个人站

“保持热爱,奔赴星海”

  • 主页
  • 目录
  • 图床
  • 留言板
  • -关于我-
友链 搜索文章 >>

青江的个人站

“保持热爱,奔赴星海”

  • 主页
  • 目录
  • 图床
  • 留言板
  • -关于我-

【云服务器(Ubuntu)配置(折腾)】四、Hexo博客(Nginx)部署与配置


阅读数: 0次    2024-12-08
字数:7.9k字 | 预计阅读时长:35分钟

想拥有一台云服务器很重要的一个原因就是搭博客,以前博客是托管在GitHub上的,用的国外的服务器,国内访问速度一般,有时候还会卡住进不去,实在有点忍不了。

不过开始部署才意识到这里面的坑居然有那么多……

更换博客源文件备份的仓库名字

新建一个新仓库,git clone到本地,把原来的所有文件复制到新仓库,重复文件选择跳过,再将配置文件_config.yml中hexo-git-backup插件的仓库名配置改一下,执行hexo b即可将文件都放到新仓库里,老仓库的commit也会转移过来。然后把老仓库删掉就行。

设置alias方便测试、部署和备份博客

在.bashrc或.zshrc中添加配置:

1
2
3
4
5
6
7
8
# 快速切换到博客根目录
alias blog='cd ~/Blog'
# 切换到博客根目录,清理原来的网页文件,重新生成网页文件,打开服务器
alias blogtest='blog;hexo cl;hexo g;hexo s'
# 切换到博客根目录,清理原来的网页文件,重新生成网页文件,推送网页文件,备份博客
alias blogdeploy='blog;hexo cl;hexo g;hexo d;hexo b'
# 将整个博客文件夹打包放在与博客文件夹同级的Backup文件夹中(需要提前创建Backup文件夹)
alias blogbackup='tar -zcvf ~/Backup/Blog-backup.tar.gz -C ~ Blog'

执行后需要关注输出信息,有时候可能会有报错或连接问题需要解决。

修改完成配置文件后需要执行source .bashrc或source .zshrc来应用文件。

配置Nginx解析Hexo博客

首先需要了解一些有关Git中钩子的使用的相关知识:

Git高级操作: Git钩子Git钩子是一组脚本,这些脚本对应着Git仓库中的特定事件,每一次事件发生时,钩子会被触发。 - 掘金

了解git裸仓库并利用post-receive自动化部署-CSDN博客

nginx配置:

自建博客 Nginx 基本配置 | 胡涂说

请马上停止使用TLS 1.0和TLS 1.1! | 网络热度

整体部署流程:

基于Hexo的静态博客网站搭建并部署至云服务器 | Glimound的个人技术经验分享

使用 Hexo 搭建个人博客并部署到云服务器 - Cheyaoyao - 博客园

【Nginx/Hexo】在云服务器上搭建个人博客 - 知乎

/etc/nginx下的Nginx配置文件中的nginx.conf,第一行user的配置尽量改为运行Nginx的用户。

通过Git部署时指定其他SSH端口:hexo配置发布至ssh非22端口服务器_hexo deploy 端口-CSDN博客

拓展配置:

好好学Hexo:Hexo配置多个git仓库 | 好好学习的郝

域名重定向:实现不带www的域名访问

笑花落Blog学习 - Nginx禁止IP直接访问、防止域名恶意解析、源IP扫描SSL泄露

Nginx 配置,禁止通过ip地址直接访问Web 服务-腾讯云开发者社区-腾讯云

nginx关闭默认站点/空主机头(禁止IP直接访问、防止域名恶… - LNMP一键安装包 - VPS侦探论坛 - Powered by Discuz!

NGINX 配置避免 IP 访问时证书暴露域名 - ZingLix Blog

Nginx:自定义错误页面-云社区-华为云

Nginx 生产环境下的安全配置 - piaohua’s blog

手动指定403报错为404,防止被扫后台目录:nginx自定义错误页_nginx自定义错误页面-CSDN博客

解决配置default_server报错:nginx- duplicate default server error - Stack Overflow,将nginx.conf中的http{}块的include /etc/nginx/sites-enabled/*;注释掉即可。

【问题解决】 网关代理Nginx 301暴露自身端口号 - 东北小狐狸 - 博客园

拓展知识:

将hexo博客部署到服务器,并支持多线部署 | Nes的草稿箱

NGINX Reverse Proxy 反向代理的使用 | Marco Nie

为域名添加ICP备案信息

ICP 备案 备案号悬挂说明-ICP 备案后处理须知-文档中心-腾讯云

hexo+yilia添加网站运行时间,ICP备案信息,设定站点建立时间 | 荷塘月色的博客

在Blog\themes\yilia\layout\_partial\footer.ejs文件的<div class="footer-left">前面添加:

1
2
3
4
5
<!-- 添加网站备案号begin -->
<% if (theme.icp && theme.icp.enable && theme.icp.number){ %>
<a href="https://beian.miit.gov.cn/" target="_blank"><%- theme.icp.number %></a>
<% } %>
<!-- 添加网站备案号end -->

<br>用于换行,让备案号单独在页脚的第一行并居中。

还需要再Yilia的配置文件Blog\themes\yilia\_config.yml中添加:

1
2
3
4
# 添加ICP备案信息
icp:
enable: true
number: "xICP备xxxxxxxxxx号-x"

添加域名SSL证书

虽然Let’s Encrypt的证书可能在某些情况下不被信任(Let’s Encrypt OCSP 域名被封 | Wolfogre’s Blog),但它胜在免费,支持泛域名,可自动部署,对于个人博客来说足够了

SSL 证书 Nginx 服务器 SSL 证书安装部署(Linux)-证书安装-文档中心-腾讯云

如何在Nginx服务器上安装SSL证书_数字证书管理服务(原SSL证书)(SSL Certificate)-阿里云帮助中心

泛域名证书比单域名证书方便的多,一次签发所有子域名通用,只需要担心续期问题就行了,因此考虑使用泛域名,泛域名验证只支持DNS验证,因此使用Certbot对Let’s Encrypt证书进行自动续期,需要使用相应的自动化脚本来管理DNS验证,下面是手动配置的方法。

使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期 - 平元兄 - 博客园

手动配置明显比较麻烦,因此考虑用自动化脚本acme.sh:acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol

嘶,突然发现acme.sh默认使用的CA是ZeroSSL,而且根据这篇博客(使用 acme.sh 配置自动续签 SSL 证书 - 烧饼博客)的对比,感觉ZeroSSl确实要好一点,这里是acme.sh更换默认CA的说明:Change default CA to ZeroSSL · acmesh-official/acme.sh Wiki,以及有一个ZeroSSL和Let’s Encrypt的对比:Let’s Encrypt Alternative - ZeroSSL

原来是acme.sh被ZeroSSL商业收购了,acme.sh被收购 更换默认证书颁发机构为ZeroSSL 还安全吗? Li.005 | 李子菜Lizicai,虽然按这篇文章中说的,每次申请证书会暴露用户信息,但用的人那么多,而且每天上网暴露的信息也不少,也不差这点了,而且我这云服务器也就是搭个网站,万一被干了也影响不大。

那就还是选择用官方默认的ZeroSSL吧,放弃Let’s Encrypt证书,全站更换ZeroSSL证书 - 饭饭’s Blog,根据文档:ACME Documentation - ZeroSSL,ZeroSSL使用ACME也可以免费申请泛域名证书

配置过程在官方的Wiki页面已经很详细了:Home · acmesh-official/acme.sh Wiki

可以参考中文页面:说明 · acmesh-official/acme.sh Wiki

也可以参考官方给出的配置教程博客推荐:Blogs and tutorials · acmesh-official/acme.sh Wiki

使用acme.sh自动申请免费ssl证书 - 佛天魔音

使用 acme.sh 配置自动续签 SSL 证书 - 烧饼博客

NGINX 配置 HTTPS 最佳实践 - ZingLix Blog

如何使用acme.sh申请 SSL(HTTPS) 泛域名证书并自动续签?最新acme.sh安装证书教程 – 今日指点

acme.sh使用说明 - Dnomd343 - Blog

如何使用acme.sh与阿里云DNS自动签发Let’s Encrypt的免费数字证书 - 初心

ACME - 运维个人技术栈

配置完成后,为确保定时任务能自动续签证书,可以通过以下命令来手动调用一次定时任务进行续签:

1
acme.sh --force --cron

配置Nginx防护(配合Fail2Ban)

利用Fail2Ban保护你的服务器(Fail2Ban使用教程) | hash070’s blog

Nginx如何防护DoS和CC攻击 | hash070’s blog

Turn Your Nginx Server into a Fortress with Fail2ban and UFW | Scalastic 👨🏻‍💻

Nginx fail2ban:个人站点 DDOS 攻击生存指南 | Ming’s Blog

nginx 限流模块和fail2ban搭配使用-阿里云开发者社区

Module ngx_http_limit_req_module

配置完后把封禁IP阈值调低,让Nginx的error更容易触发,在浏览器中用F5不断刷新网页触发error,测试了很久,都没有出现封禁IP的情况,检查了Nginx的error.log,是有报错信息的,检查了Fail2Ban的匹配原则,各种设置项,都没有问题,而且SSH的封禁IP正常运作。查了大半天,终于在官方的一个Issues中找到答案:Jail not banning, cant find cause · Issue #2856 · fail2ban/fail2ban,是因为Nginx IP监狱nginx-limit-req的配置里面backend这一项被设置为了systemd,这意味着这将监视systemd-journal而不是日志文件,通过sudo fail2ban-client -d可以查看目前所有监狱的信息,发现['add', 'nginx-limit-req', 'systemd'],在jail.local文件的监狱设置里添加backend = auto,重启Fail2ban后,监狱封禁正常运行。

但是查看jail.conf文件,发现默认值就是backend = auto,然而自己新建的监狱的backend值又自动改为了systemd???这是为什么呢?原来是Fail2ban的配置文件中,对于配置文件优先级:

  • /etc/fail2ban/jail.conf
  • /etc/fail2ban/jail.d/*.conf,按字母顺序排列
  • /etc/fail2ban/jail.local
  • /etc/fail2ban/jail.d/*.local,按字母顺序排列

因此jail.d文件夹里的配置项可以覆盖jail.conf中的配置,查看jail.d文件夹,发现其中有一个defaults-debian.conf文件,其中默认配置中写入了backend = systemd,进一步查找官方Issus,发现:[BR]: fail2ban does not start on some debian/ubuntu systems - backend should probably be set to systemd on all systemd-based distros · Issue #3292 · fail2ban/fail2ban,这可能就是jail.d文件夹中出现defaults-debian.conf文件,将backend默认配置为systemd的原因,在官方Wiki的Q&A里也有有关backend参数设置的提醒的提醒,How fail2ban works · fail2ban/fail2ban Wiki,不过说实话,一般不出问题的话很难注意到……

查看sudo fail2ban-client -d输出发现sshd监狱就是['add', 'sshd', 'systemd'],但sshd的监狱就能正常运行,同样配置下nginx-limit-req监狱就无法正常运行,我猜测可能是sshd比较靠近系统底层,因此做了基于systemd的发行版系统的适配,日志除了在/var/log/auth.log中找到,服务日志信息还会被记录到systemd日志中,因此sshd监狱使用systemd模式可以正常运行。

但像Nginx等软件,可能不会专门为了某一种系统做适配,因此日志信息只能在寻常的log文件中找到,因此即使所有配置都正确,将监狱设置为systemd,Fail2ban仍然不能正确找到日志文件,这时候就必须手动设置:backend = auto。

就像开发者sebres (Sergey G. Brester)在Issue里的comment[BR]: fail2ban does not start on some debian/ubuntu systems - backend should probably be set to systemd on all systemd-based distros · Issue #3292 · fail2ban/fail2ban中说的,作为开发者,维护软件是一件很麻烦的事情,很难做到匹配世界各地每个软件的每个发行版和每个版本,评估影响后的一次更新,很可能就因为发行版的一次改动出Bug,fail2ban只是一个工具(不是魔术盒),并且每个工具都需要正确配置(即使已经进行了一些预配置)。所以我们应该理解和尊重这些开源完全免费软件的开发者们,给他们多一些包容,如果可以的话,为维护版本做出贡献来帮助其他人。

终于把这个困扰了我大半天的问题解决了,网上各种论坛问答博客翻了个底朝天都没有找到解决方法,还得是在官方的Issues中才能找到,这个故事告诉我们,实在找不到解决方法时候,不要畏惧查找外文资料,外网博客或官方的Issues中很可能会有完美的解决方法。

添加nginx-botsearch.conf过滤器,此过滤器重点关注对不存在的 URL 的请求(404 错误),这通常是机器人或扫描程序试图查找漏洞或隐藏页面的迹象。

添加方法同上,测试error.log文件发现过滤器无法正常匹配,查找发现官方Issue中有解答:[BR]: Fail2Ban nginx-botsearch doesn’t match any of the error log entries · Issue #3301 · fail2ban/fail2ban

过滤器后半部分有一个<block>参数需要自己根据botsearch-common.conf文件指定想要过滤器匹配的URL列表,用来匹配不同的阻止报错信息:

1
2
3
4
5
6
7
8
9
10
# Block is the actual non-found directories to block
block = \/?(<webmail>|<phpmyadmin>|<wordpress>|cgi-bin|mysqladmin)[^,]*

# These are just convenient definitions that assist the blocking of stuff that
# isn't installed
webmail = roundcube|(ext)?mail|horde|(v-?)?webmail

phpmyadmin = (typo3/|xampp/|admin/|)(pma|(php)?[Mm]y[Aa]dmin)

wordpress = wp-(login|signup|admin)\.php

最简单的情况是将其设置为\S+,每个未找到的文件将被视为来自机器人的请求,配置后的正则表达式:

1
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/\S+ \S+\"\, .*?$

不过最好不要改动nginx-botsearch.conf,而是在jail.local中指定<block>:

1
2
[nginx-botsearch]
filter = botsearch-common[block="\S+"]

配置后测试成功。

测试发现nginx-botsearch监狱触发封禁规则后,看起来是实施了封禁,但正在访问的网页还可以继续访问一段时间,于是fail2ban.log就出现了一堆xxx.xxx.xxx.xxx already banned的消息,有没有办法在触发封禁的第一时间就将目标IP地址的所有连接都切断,禁止其访问所有内容呢?答案是有的,同样的,这个问题在浏览器中搜索了半天都没有解答,都是在讲一些基础的配置,无奈只好又去官方的Issue中找,果然有人遇到了同样的问题:Fail2ban says already banned an ip but the ip can still visit webserver · Issue #2545 · fail2ban/fail2ban,开发者给出了解决方法:设置默认值banaction = ufw[kill-mode=ss]、banaction_allports = ufw[kill-mode=ss],这样配置的情况下,一旦IP地址被封禁,就会直接切断该IP地址与服务器的所有连接。

可以参考另一个相关Issue:Update ufw.conf by usernamepi · Pull Request #3018 · fail2ban/fail2ban

但这样配置的情况下需要注意,一定再也不能用本机的IP地址测试Fail2ban的监狱起效情况,一旦IP被封禁,正在连接的SSH连接也会被切断,只能等待时间到了解封或者更换IP连接。

不过根据Issue,UFW有一些其他问题:https://github.com/fail2ban/fail2ban/issues/2545#issuecomment-2460393322、https://github.com/fail2ban/fail2ban/issues/2545#issuecomment-2460472090,因此不建议使用,`debian`系操作系统例如`Ubuntu`中默认的是`nftables`,查看`jail.d`文件夹下的`defaults-debian.conf`,可以发现默认的配置就是`nftables`,虽然也可以改用`jail.conf`中的`iptables`,但由于开发者设置了默认设置,直接用就行。注意如果SSH连接修改了默认了22端口,需要在监狱配置中加入`port = xxxx`的配置,确保封禁了正确的端口。配置完成后重启Fail2ban服务,测试成功。

扩展:安全:nftables的常用命令(查看规则) - 刘宏缔的架构森林 - 博客园

添加nginx-forbidden.conf过滤器:

1
2
3
4
[Definition]
failregex = ^ \[error\] \d+#\d+: \*\d+ (.*?) \"\S+\" is forbidden, client: <HOST>,

ignoreregex =
1
2
3
4
5
6
7
8
9
[nginx-forbidden]
enabled = true
filter = nginx-forbidden
port = http,https
logpath = %(nginx_error_log)s
backend = auto
bantime = xx
findtime = xx
maxretry = xx

封禁频繁扫描目录时导致403 Forbidden报错的IP地址。

添加nginx-connection-refused.conf过滤器:

1
2
3
4
[Definition]
failregex = ^ \[error\] \d+#\d+: \*\d+ connect\(\) failed \(\d+: Connection refused\) while connecting to upstream, client: <HOST>,

ignoreregex =
1
2
3
4
5
6
7
8
9
[nginx-connection-refused]
enabled = true
filter = nginx-connection-refused
port = http,https
logpath = %(nginx_error_log)s
backend = auto
bantime = xx
findtime = xx
maxretry = xx

封禁频繁访问后台服务器拒绝连接的Nginx反代地址时导致Connection refused的IP地址。

Twikoo评论自部署

给博客搭建一个带后端的评论系统,虽然无后端的Viline搭建起来比较方便,但无后端总归是不好管理,速度可能比较慢,也没办法防止恶意评论批量灌入。Waline是一个Viline改版的评论系统,但看了一下效果,还需要登录什么的,感觉有点复杂了。最终选了Twikoo:Twikoo | 一个简洁、安全、免费的静态网站评论系统,并选择自部署,这样国内访问的速度应该会比较快。

云函数部署 | Twikoo 文档

自建 Twikoo 评论系统私有部署 - 七味茶盏

linux 自建 Twikoo 评论系统私有部署保姆级(非 Docker) | a.d 博客

Linux系统服务神器:systemctl的配置与使用 - 掘金

Twikoo 评论系统私有部署(非Docker) | 网上冲浪中心

评论区 | 从 01 开始

部署博客评论系统:Twikoo

博客评论系统Twikoo本地化部署 | ALLBS

在静态博客里添加Twikoo评论系统,并配置邮箱和TG通知

hexo-yilia主题支持twikoo评论系统 [yilia-more](https://github.com/bux - 掘金

buxiaoxing/yilia-more: 一个简洁优雅的hexo主题 A simple and elegant theme for hexo.

Twikoo部署心得 | Asa-World

对于Yilia,编辑ejs文件部署评论框前端:

在Blog\themes\yilia\layout\_partial\post\下添加twikoo.ejs文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="tcomment"></div>
<script src="https://cdn.jsdelivr.net/npm/twikoo@1.6.40/dist/twikoo.min.js"></script>
<script>
twikoo.init({
envId: '<%= theme.twikoo.envId %>', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
el: '#tcomment', // 容器元素
// region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
// path: location.pathname, // 用于区分不同文章的自定义 js 路径,如果您的文章路径不是 location.pathname,需传此参数
// lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/js/utils/i18n/index.js
})
</script>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css" integrity="sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js" integrity="sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js" integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa" crossorigin="anonymous"></script>
</head>

编辑Blog\themes\yilia\layout\_partial下的article.ejs文件,在尾部最后一个<% } %>前添加:

1
2
3
4
5
6
7
8
<!-- twikoo评论系统 -->
<% if (theme.twikoo && theme.twikoo.enable && theme.twikoo.envId){ %>
<%- partial('post/twikoo', {
key: post.slug,
title: post.title,
url: config.url+url_for(post.path)
}) %>
<% } %>

在article.ejs文件的代码<%- partial('post/title', {class_name: 'article-title'}) %>下面添加单篇文章阅读量统计:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 添加Twikoo阅读量bagin-->
<% if (!index && post.comments){ %>
<% if ( theme.twikoo && theme.twikoo.enable && theme.twikoo.visitor ){ %>
<br />
<a class="cloud-tie-join-count" href="javascript:void(0);" style="color:gray;font-size:14px;">
<span class="icon-sort"></span>
<span id="twikoo_visitors_statistics" style="color:#ef7522;font-size:14px;">
阅读数: <span id="twikoo_visitors">0</span>次 &nbsp;&nbsp;
</span>
</a>
<% } %>
<% } %>
<!-- 添加Twikoo阅读量end -->

由于Twikoo内置的阅读量统计只有在评论区加载时候才会加载,因此设置为打开某一篇文章时候再加载阅读量,主页(文章部分浏览的情况)下没有阅读量统计(加载条件与评论加载条件相同)。

在主题配置文件Blog\themes\yilia\_config.yml中添加配置:

1
2
3
4
5
#6、twikoo https://twikoo.js.org/
twikoo:
enable: true
envId: https://twikoo.example.com
visitor: true

添加自定义表情包:Twikoo評論系統的個性化設置 · 嘰嘰乞乞

评论区美化:

文章 | Butterfly主题美化教程

Hexo博客添加自定义css和js文件 | Leonus

给你的评论添加一个输入提醒吧 | Leonus

评论表情包放大功能,超实用 | Leonus

Twikoo评论回复邮件模板:Acrylic Mail 粉 | 张洪Heo

Hexo博客私有部署Twikoo评论系统并迁移评论记录(自定义邮件回复模板)-CSDN博客

参考:魔改笔记六:twikoo及导航栏美化 | LiuShen’s Blog

自定义表情国内加速版CDN链接:https://fastly.jsdelivr.net/gh/willow-god/owo/owo-fast.json

编辑CSS文件对评论框的样式做一些改动:

在@media screen and (max-width:800px) {上面添加:

1
2
3
4
5
6
/* Twikoo评论块样式(大屏幕) */
.twikoo {
background: #fff;
margin: 10px 30px;
padding: 10px;
}

在@media screen and (max-width:800px) {中添加:

1
2
3
4
5
6
/* Twikoo评论块样式(小屏幕) */
.twikoo {
background: #fff;
margin: 10px 0;
padding: 5px;
}

在文件最后添加:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* 给有链接的评论用户名字变颜色,增加可读性 */
.tk-nick.tk-nick-link {
color: #ec8106 !important;
}

/* 为评论添加输入提醒:https://blog.leonus.cn/2022/inputAlert.html */
/* 设置文字内容 :nth-child(1)的作用是选择第几个 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(
1
):before {
content: "输入QQ号可以自动获取昵称和头像🐧";
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(
2
):before {
content: "收到回复将会发送到您的邮箱📧";
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(
3
):before {
content: "通过点击昵称即可访问您的网站🔗";
}

/* 当用户点击输入框时显示 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::before,
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::after {
display: block;
}

/* 主内容区 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::before {
/* 先隐藏起来 */
display: none;
/* 绝对定位 */
position: absolute;
/* 向上移动60像素 */
top: -60px;
/* 文字强制不换行,防止left:50%导致的文字换行 */
white-space: nowrap;
/* 圆角 */
border-radius: 10px;
/* 距离左边50% */
left: 50%;
/* 然后再向左边挪动自身的一半,即可实现居中 */
transform: translate(-50%);
/* 填充 */
padding: 14px 18px;
background: #444;
color: #fff;
}

/* 小角标 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::after {
display: none;
content: "";
position: absolute;
/* 内容大小(宽高)为0且边框大小不为0的情况下,每一条边(4个边)都是一个三角形,组成一个正方形。
我们先将所有边框透明,再给其中的一条边添加颜色就可以实现小三角图标 */
border: 12px solid transparent;
border-top-color: #444;
left: 50%;
transform: translate(-50%, -48px);
}

/* twikoo评论区美化,参考:
https://www.cxl2020mc.top/post/Hexo-Butterfly-Twikoo%E8%AF%84%E8%AE%BA%E5%8C%BA%E7%BE%8E%E5%8C%96
https://blog.liushen.fun/posts/b9d10ed/ */
/* 个人信息和文本输入之间的空位置 */
.tk-main .tk-submit .tk-col .tk-meta-input {
margin-bottom: 10px;
}

/* 圆角设置 */
.tk-meta-input input {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}

.tk-meta-input div {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}

/* 输入框样式 */
.tk-input.el-textarea textarea {
border-radius: 8px;
min-height: 250px !important;
height: auto;
}

/* 评论区评论大框 */
.twikoo .tk-comments-container > .tk-comment,
.twikoo .tk-comments .tk-submit {
/* 内边距 */
padding: 15px;
margin-top: 0px;
margin-bottom: 3px;
/* 圆角 */
border-radius: 8px;
/* 背景颜色 */
background: #d8d8d833;
}

/* 评论区按钮样式 */
.tk-row.actions button.el-button {
border-radius: 8px !important;
}

/* 设备信息 */
.twikoo .tk-extra {
/* 圆角 */
border-radius: 6px;
/* 背景颜色 */
background: #dcdcdc71;
/* 内边距 */
padding: 6px;
/* 底边距 */
margin-bottom: 2px;
}

/* 加载更多按钮 */
.twikoo .tk-expand {
/* 圆角 */
border-radius: 8px;
background-color: #dcdcdc71;
}

/* 加载更多按钮(鼠标悬浮时) */
.twikoo .tk-expand:hover {
/* 背景颜色 */
background-color: #dcdcdc3f;
}

/* 分类卡片移动端个人信息卡片字体缩小 */
@media screen and (max-width: 570px) {
.tk-main .tk-extras {
font-size: 0.5em;
}
}

自定义评论邮件回复样式:

MAIL_TEMPLATE(用户回复通知邮件模板):

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
<!DOCTYPE html>
<html>
<body>
<style>
body {
margin: 0px;
}
.header {
padding: 50px 0 0;
}
.overlay {
height: 120px;
position: absolute;
width: 100%;
}
.profilepic {
box-sizing: border-box;
border: 5px solid #fff;
border-radius: 300px;
width: 128px;
height: 128px;
margin: 0 auto;
overflow: hidden;
background: #88acdb;
position: relative;
}
.js-avatar {
max-width: 100%;
}
</style>
<div class="overlay" style="background: #4d4d4d"></div>
<header class="header">
<div class="profilepic">
<img src="https://hanqingjiang.com/img/avatar.jpg" class="js-avatar" />
</div>
</header>
<div class="page flex-col">
<div
class="box_4 flex-col"
style="
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
"
>
<div
class="text-group_5 flex-col justify-between"
style="
display: flex;
flex-direction: column;
align-items: center;
margin: 0 20px;
"
>
<span
class="text_1"
style="
font-size: 26px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #000000;
line-height: 37px;
text-align: center;
"
>嘿!您在&nbsp;${SITE_NAME}&nbsp;中收到一条新回复!</span
>
<span
class="text_2"
style="
font-size: 16px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #00000090;
line-height: 22px;
margin-top: 21px;
text-align: center;
"
>您之前的评论收到了来自&nbsp;${NICK}&nbsp;的回复</span
>
</div>
<div
class="box_2 flex-row"
style="
margin: 0 20px;
min-height: 128px;
background: #f7f7f7;
border-radius: 12px;
margin-top: 34px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 32px 16px;
width: calc(100% - 40px);
"
>
<div
class="text-wrapper_4 flex-col justify-between"
style="
display: flex;
flex-direction: column;
margin-left: 30px;
margin-bottom: 16px;
"
>
<span
class="text_3"
style="
height: 22px;
font-size: 20px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #cc7d23ea;
line-height: 22px;
"
>${PARENT_NICK}:</span
>
<span
class="text_4"
style="
margin-top: 6px;
margin-right: 22px;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #000000;
line-height: 22px;
"
>${PARENT_COMMENT}</span
>
</div>
<hr
style="
display: flex;
position: relative;
border: 1px dashed #cc7d2358;
box-sizing: content-box;
height: 0px;
overflow: visible;
width: 100%;
"
/>
<hr style="border: 1px dashed #00000000" />
<div
class="text-wrapper_4 flex-col justify-between"
style="
display: flex;
flex-direction: column;
margin-left: 30px;
"
>
<span
class="text_3"
style="
height: 22px;
font-size: 20px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #cc7d23ea;
line-height: 22px;
"
>${NICK}:</span
>
<span
class="text_4"
style="
margin-top: 6px;
margin-right: 22px;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #000000;
line-height: 22px;
"
>${COMMENT}</span
>
</div>
<a
class="text-wrapper_2 flex-col"
style="
min-width: 106px;
height: 38px;
background: #dfb58394;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin: auto;
margin-top: 32px;
"
href="${POST_URL}"
>
<span class="text_5" style="color: #1f1e1d">查看详情</span>
</a>
</div>
<div
class="text-group_6 flex-col justify-between"
style="
display: flex;
flex-direction: column;
align-items: center;
margin-top: 34px;
"
>
<span
class="text_6"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #3b362f;
line-height: 17px;
"
>此邮件由评论服务自动发出,直接回复无效。</span
>
<span
class="text_6"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #3b362f;
line-height: 17px;
"
>如非本人操作,请忽略此邮件。</span
>
<a
class="text_7"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #cc7d23;
line-height: 17px;
margin-top: 6px;
text-decoration: none;
"
href="${SITE_URL}"
>前往博客</a
>
</div>
</div>
</div>
</body>
</html>

MAIL_TEMPLATE_ADMIN (博主新评论通知邮件模板):

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
<!DOCTYPE html>
<html>
<body>
<style>
body {
margin: 0px;
}
.header {
padding: 50px 0 0;
}
.overlay {
height: 120px;
position: absolute;
width: 100%;
}
.profilepic {
box-sizing: border-box;
border: 5px solid #fff;
border-radius: 300px;
width: 128px;
height: 128px;
margin: 0 auto;
overflow: hidden;
background: #88acdb;
position: relative;
}
.js-avatar {
max-width: 100%;
}
</style>
<div class="overlay" style="background: #4d4d4d"></div>
<header class="header">
<div class="profilepic">
<img src="https://hanqingjiang.com/img/avatar.jpg" class="js-avatar" />
</div>
</header>
<div class="page flex-col">
<div
class="box_4 flex-col"
style="
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
"
>
<div
class="text-group_5 flex-col justify-between"
style="
display: flex;
flex-direction: column;
align-items: center;
margin: 0 20px;
"
>
<span
class="text_1"
style="
font-size: 26px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #000000;
line-height: 37px;
text-align: center;
"
>嘿!您在&nbsp;${SITE_NAME}&nbsp;中收到一条新评论!</span
>
<span
class="text_2"
style="
font-size: 16px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #00000090;
line-height: 22px;
margin-top: 21px;
text-align: center;
"
>您收到了来自&nbsp;${NICK}&nbsp;的评论</span
>
</div>
<div
class="box_2 flex-row"
style="
margin: 0 20px;
min-height: 128px;
background: #f7f7f7;
border-radius: 12px;
margin-top: 34px;
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 32px 16px;
width: calc(100% - 40px);
"
>
<div
class="text-wrapper_4 flex-col justify-between"
style="
display: flex;
flex-direction: column;
margin-left: 30px;
"
>
<span
class="text_3"
style="
height: 22px;
font-size: 20px;
font-family: PingFang-SC-Bold, PingFang-SC;
font-weight: bold;
color: #cc7d23ea;
line-height: 22px;
"
>${NICK}:</span
>
<span
class="text_4"
style="
margin-top: 6px;
margin-right: 22px;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #000000;
line-height: 22px;
"
>${COMMENT}</span
>
</div>
<a
class="text-wrapper_2 flex-col"
style="
min-width: 106px;
height: 38px;
background: #dfb58394;
border-radius: 32px;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin: auto;
margin-top: 32px;
"
href="${POST_URL}"
>
<span class="text_5" style="color: #1f1e1d">查看详情</span>
</a>
</div>
<div
class="text-group_6 flex-col justify-between"
style="
display: flex;
flex-direction: column;
align-items: center;
margin-top: 34px;
"
>
<span
class="text_6"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #3b362f;
line-height: 17px;
"
>此邮件由评论服务自动发出,直接回复无效。</span
>
<span
class="text_6"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #3b362f;
line-height: 17px;
"
>如非本人操作,请忽略此邮件。</span
>
<a
class="text_7"
style="
height: 17px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #cc7d23;
line-height: 17px;
margin-top: 6px;
text-decoration: none;
"
href="${SITE_URL}"
>前往博客</a
>
</div>
</div>
</div>
</body>
</html>

上面的HTML代码需要经过压缩后填入配置中,推荐使用VS Code中的Minity插件对HTML文件做压缩处理。

增加外链安全跳转页面:给博客添加一个安全跳转页面 | LiuShen’s Blog,感觉有点复杂,以后有需要再说吧……

自建域名邮箱服务器搭建

Jinnrry/PMail: Private EMail Server

Pmail——仅用一分钟就可以搭建好一个邮箱服务器-腾讯云开发者社区-腾讯云

寻道之旅 - 强烈推荐,一个内存占用极低的自建域名邮箱 Pmail

搭了好几遍,测试了半天,接收倒是正常,发送全是error。

好!发现腾讯云默认把邮件SMTP需要的25端口封了,且只有云服务器才可以申请解封,轻量应用服务器不支持解封……

轻量应用服务器 常见问题-文档中心-腾讯云

关于无法使用非25的加密端口,通过SMTPS收发邮件的详细解释,可以参考这两篇Issue:是不是可以不用 25直接使用 465 · Issue #130 · Jinnrry/PMail、Emails cannot be sent out through port 25, can the port be changed to 465 · Issue #8 · Jinnrry/PMail

自建域名邮箱服务器宣告失败,老老实实用QQ邮箱的SMTP了。

自己云服务器厂商没有封25端口的朋友可以尝试一下,用Docker和非Docker都可以,搭建还是比较方便的。

包管理工具从NPM到PNPM

NPM有各种各样的遗留问题,node_modules文件夹占用空间巨大,速度慢,镜像源访问慢等,考虑使用新型包管理工具PNPM进行替换:

前端 - 包管理工具之从NPM到PNPM - 个人文章 - SegmentFault 思否

本文来源: 青江的个人站
本文链接: https://hanqingjiang.com/2024/12/08/241208_server_hexoBlogWithNginx/
版权声明: 本作品采用 CC BY-NC-SA 4.0 进行许可。转载请注明出处!
知识共享许可协议
赏

谢谢你请我喝可乐~

支付宝
微信
  • Linux
  • Server

扫一扫,分享到微信

微信分享二维码
2025新年快乐!
【云服务器(Ubuntu)配置(折腾)】三、Teamspeak服务器搭建
  1. 1. 更换博客源文件备份的仓库名字
  2. 2. 设置alias方便测试、部署和备份博客
  3. 3. 配置Nginx解析Hexo博客
  4. 4. 为域名添加ICP备案信息
  5. 5. 添加域名SSL证书
  6. 6. 配置Nginx防护(配合Fail2Ban)
  7. 7. Twikoo评论自部署
  8. 8. 自建域名邮箱服务器搭建
  9. 9. 包管理工具从NPM到PNPM
© 2021-2025 青江的个人站
晋ICP备2024051277号-1
powered by Hexo & Yilia
  • 友链
  • 搜索文章 >>

tag:

  • 生日快乐🎂
  • 新年快乐!
  • 小技巧
  • Linux
  • 命令
  • 语录
  • 复刻
  • Blog
  • Notes
  • Android
  • C
  • FPGA
  • Homework
  • MATLAB
  • Server
  • Vivado

  • 引路人-稚晖
  • Bilibili-稚晖君
  • 超有趣讲师-Frank
  • Bilibili-Frank