Hexo 配置记录

前言

在学习的路上越走越远,计划用Surface Pro准备实现全面电子笔记等,但是实际的使用上发现Onenote等其他软件写起笔记来,公式很难书写,排版也是不那么轻松,个人也不喜欢手写,加上需要显示代码啥的记录。于是选择Markdown,部署这个博客也属于个人的记录加上一些乱七八糟的折腾吧。

所有的修改和配置都遵循尽量不修改原来的代码,尽可能的采用覆盖或者修改配置文件进行部署

安装

Hexo + Next主题的基本安装就省略了,毕竟它们实时都在更新,还是参见官网最为靠谱。

更改初始Post布局

更改scaffolds/post.md

1
2
3
4
5
---
title: {{ title }}
date: {{ date }}
tags:
---

改为

1
2
3
4
5
6
7
8
9
10
11
12
13
---
title: {{ title }}
date: {{ date }}
mathjax: false
tags:
- TAG1
- TAG2
categories:
- 分类
description:
---

<!-- more -->

这样来就不用每次找一篇写过的文章复制了- -(多少人是这么干的)

数学公式渲染

Hexo本身的Markdown的渲染引擎和Next主题数学公式的渲染引擎mathjax有那么一点点冲突

  1. //换行需要打为////
  2. 内联公式出现两个_就会造成渲染失败
  3. 在Markdown的List语法出现公式导致前面的点或序号不显示

这里暂时先替换为hexo-renderer-kramed,以后有更好的选择再替换

1
2
3
4
# 卸载原来的渲染引擎
npm uninstall hexo-renderer-marked --save
# 安装新的渲染引擎
npm install hexo-renderer-kramed --save

但是这样还是会有一些问题,在博客根路径下node_modules\kramed\lib\rules\inline.js进行以下的替换

1
2
//escape: /^\\([\\`*{}\[\]()#$+\-.!_>])/,
escape: /^\\([`*\[\]()#$+\-.!_>])/,
1
2
//em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
em: /^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,

另外这样并没有解决第三个问题- -……


2019-9-28更新:

使用markdown_it_plus进行markdown的渲染,同时在next里面使用Ketax进行公式渲染

1
2
3
4
# 卸载原来的渲染引擎
npm uninstall hexo-renderer-kramed --save
# 安装新的渲染引擎
npm uninstall markdown_it_plus --save

hexo配置文件添加

1
2
3
4
5
6
7
8
9
10
# hexo-renderer-markdown-it-plus
markdown_it_plus:
highlight: true
html: true
xhtmlOut: true
breaks: true
langPrefix:
linkify: true
typographer:
quotes: “”‘’

以上3个问题都以解决

字数统计 · 阅读时长

首先进行插件的安装

1
npm install hexo-symbols-count-time --save

在博客根目录下_config.yml添加,这样启用插件

1
2
3
4
5
6
7
8
# Post wordcount display settings
# Dependencies: https://github.com/theme-next/hexo-symbols-count-time
symbols_count_time:
symbols: true
time: true
total_symbols: true
total_time: true
exclude_codeblock: false

然后在博客根目录下的themes/next/_config.yml可以配置插件

1
2
3
4
5
6
7
8
# Post wordcount display settings
# Dependencies: https://github.com/theme-next/hexo-symbols-count-time
symbols_count_time:
separated_meta: true
item_text_post: true
item_text_total: false
awl: 2
wpm: 275

手机页面底部不居中

去掉style: source/_data/styles.styl的注释,然后创建对应的文件
这里注意一下其目录对应的是根目录而不是next主题的目录

添加以下css

1
2
3
.footer-inner {
padding-right: 0;
}

部署

经过考虑后,还是准备部署在自己的服务器上,利用

  • Ubuntu 18.04 Server
  • docker
  • nginx
  • Let’s Encrypt

进行部署。

Nginx

对于nginx一般分为

  • 配置文件
  • 部署文件
  • 日志文件

三个部分,于是我们在一个文件夹下创建以下的文件结构,这里的文件是在主机上进行创建,稍后要映射到Docker容器里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker/nginx
├── conf # 配置文件
│ ├── general.conf #
│ ├── letsencrypt.conf #
│ ├── nginx.conf #
│ └── security.conf #
├── conf.d # 其他配置文件
├── logs # 日志
├── run # PID文件
├── sites-available # 可用网站配置
│ └── blog.huhu.dev.conf #
├── sites-enabled # 激活的网站配置
│ └── blog.huhu.dev.conf -> ../sites-available/blog.huhu.dev.conf #
├── start.sh #
└── www # 网站文件
└── blog.huhu.dev #
└── public #

对于Nginx的配置文件,可以通过nginxconfig进行生成,然后再进一步自定义。
nginxconfig也有步骤的描述,参考流程即可部署成功。

nginx.conf

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
user www-data;
pid /var/run/nginx.pid;
worker_processes 1;
worker_rlimit_nofile 1024;

events {
multi_accept on;
worker_connections 1024;
}

http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
client_max_body_size 16M;

# MIME
include mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

# logging
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE ciphersuites
ssl_dhparam /etc/nginx/dhparam.pem;

# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;

# load configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

general.conf

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
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}

# robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}

# assets, media
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
expires 7d;
access_log off;
}

# svg, fonts
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
add_header Access-Control-Allow-Origin "*";
expires 7d;
access_log off;
}

# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

letsencrypt.conf

1
2
3
4
# ACME-challenge
location ^~ /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
}

security.conf

1
2
3
4
5
6
7
8
9
10
11
12
# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# . files
location ~ /\.(?!well-known) {
deny all;
}

注意一下,因为MathJax用到了eval,所以需要加上’unsafe-eval’。否则数学公式将渲染失败。

sites-available/blog.huhu.dev.conf

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
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name blog.huhu.dev;
root /usr/share/nginx/html/blog.huhu.dev/public;

# SSL
ssl_certificate /etc/letsencrypt/live/blog.huhu.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.huhu.dev/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/blog.huhu.dev/chain.pem;

# security
include security.conf;

# index.html fallback
location / {
try_files $uri $uri/ /index.html;
}

# additional config
include general.conf;
}

# subdomains redirect
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name *.blog.huhu.dev;

# SSL
ssl_certificate /etc/letsencrypt/live/blog.huhu.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.huhu.dev/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/blog.huhu.dev/chain.pem;

return 301 https://blog.huhu.dev$request_uri;
}

# HTTP redirect
server {
listen 80;
listen [::]:80;

server_name .blog.huhu.dev;

include letsencrypt.conf;

location / {
return 301 https://blog.huhu.dev$request_uri;
}
}

sites-enabled

进入到sites-enabled里面,输入以下指令

1
sudo ln -s ../sites-available/blog.huhu.dev.conf .

即可’激活’blog.huhu.dev.conf这个网站的配置

www/blog.huhu.dev/public

将通过hexo g生成的日志根目录下public的全部内容移动到这里即可。

HTTPS证书

利用Let’s Encrypt进行证书签名,由于Let’s Encrypt的签名需要网站可以访问,所以我们需要先运行起来我们的网站
如果在内网环境下,也注意要先配置端口映射。在docker/nginx下输入

1
sed -i -r 's/(listen .*443)/\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g' sites-available/blog.huhu.dev.conf

暂时取消HTTPS的设置,否则Docker容器无法运行,然后输入

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -d -p 80:80 -p 443:443 \
--name nginx \
-v $PWD/conf/security.conf:/etc/nginx/security.conf \
-v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf \
-v $PWD/conf/general.conf:/etc/nginx/general.conf \
-v $PWD/conf/letsencrypt.conf:/etc/nginx/letsencrypt.conf \
-v $PWD/conf.d:/etc/nginx/conf.d \
-v $PWD/sites-enabled:/etc/nginx/sites-enabled \
-v $PWD/sites-available:/etc/nginx/sites-available \
-v $PWD/logs/:/var/log/nginx/ \
-v $PWD/www/:/usr/share/nginx/html \
-v $PWD/run/:/var/run/ \
nginx

运行Docker容器,进入Nginx容器内部进行签名的生成

1
2
3
4
5
6
7
8
9
10
11
12
docker exec -it nginx /bin/bash
apt-get update && apt-get install software-properties-common certbot python-certbot-nginx

# Generate Diffie-Hellman keys:
openssl dhparam -out /etc/nginx/dhparam.pem 2048

# Create a common ACME-challenge directory (for Let's Encrypt):
mkdir -p /var/www/_letsencrypt
chown www-data /var/www/_letsencrypt

# Obtain certificate:
certbot certonly --webroot -d blog.huhu.dev --email [email protected] -w /var/www/_letsencrypt -n --agree-tos --force-renewal

这里会把HTTPS证书等文件都放到/etc/letsencrypt/live文件夹里面。
然后恢复HTTPS的设置,重新加载Nginx的配置

1
2
3
# Uncomment SSL related directives in configuration:
sed -i -r 's/#?;#//g' /etc/nginx/sites-available/blog.huhu.dev.conf
nginx -t && nginx -s reload

以上,即可完成Let’s Encrypt的证书配置,似乎有自动更新的脚本(/etc/letsencrypt/renewal-hooks/post/),但是不知道为什么这次安装后,脚本的文件夹里面是空的,暂时先不搞自动更新。

总结

总结一下Nginx容器内部的文件结构,一些自带的文件就不放出来了

  • 配置文件:/etc/nginx
1
2
3
4
5
6
7
8
9
10
11
12
/etc/nginx
|-- conf.d
|-- dhparam.pem
|-- general.conf
|-- letsencrypt.conf
|-- mime.types
|-- nginx.conf
|-- security.conf
|-- sites-available
| `-- blog.huhu.dev.conf
|-- sites-enabled
| `-- blog.huhu.dev.conf -> ../sites-available/blog.huhu.dev.conf
  • 日志文件:/var/log/nginx/
1
2
3
/var/log/nginx/
|-- access.log
`-- error.log
  • 网站文件:/usr/share/nginx/html/
1
2
3
/usr/share/nginx/html/
`-- blog.huhu.dev
`-- public

外网访问

路由器上设置

  • 80 (HTTP)
  • 443 (HTTPS)

的端口隐射。映射到部署Docker的服务器上。


这里要注意一下端口回流,因为NAT的原因,内网的主机是无法访问公网IP的内网服务器的。也就是你无法在内网通过域名或公网IP访问到和你在同一个内网下的服务器。简单的来说就是你PC想和路由器(服务器在路由器下)建立链接,但是服务器发现目标IP直接是你内网PC的IP,于是服务器准备直接和你PC进行连接,这连接就无法建立,导致无法访问。

解决方法:
采用简单的解决方法,有些路由器有对端口回流的有处理可以开启,或者在路由器层次上配置DNS,将对应的域名解析为内网服务器的IP地址,更高级的方法就是设置NAT,将源IP进行修改,不过一般路由器无法实现,也较麻烦,这里就不搞了。

Hexo一键部署

通过SFTP进行一键部署,需要安装

1
npm install hexo-deployer-sftp --save

然后编辑Hexo的_config.yml

1
2
3
4
5
6
7
8
9
10
deploy:
type: sftp
host: <host>
user: <user>
pass: <password>
remotePath: [remote path]
port: [port]
privateKey: [path/to/privateKey]
passphrase: [passphrase]
agent: [path/to/agent/socket]
Option Description Default
host Address of remote host
user Username
pass Password
remotePath Root directory of remote host /
port Port 22
privateKey Path to a ssh private key
passphrase Optional passphrase for the private key
agent Path to the ssh-agent socket $SSH_AUTH_SOCK

设置完成后即可通过hexo deploy进行一键部署。如果上面使用了Dockerfile进行部署的话,这里就不能一键部署,需要采用其他方法,因为Dockerfile无法指定挂载Host某一节点。只能挂载匿名盘。所以无法用SFTP进行同步。