wtw's Frontend

道阻且长,行则将至💪🏻

0%

hexo next

引言

此博客用于搭建 hexo next 主题的字典工具类博客.基本不会探索深究配置的原因,敬请知悉~

hexo 安装

步骤

首先要用 npm 资源依赖管理工具安装全局命令 hexo-cli.

npm install hexo-cli -g

然后使用 hexo 初始化博客目录,比如 blog 目录,目录名要与后续个人建立的 github 上 hexo 托管代码的 repository 库同名.

hexo init blog

接着 github 建库: 建立一个以 w-t-w(我的账号).github.io 结尾的 repository 库,作为 hexo 托管代码的库, github 默认
.github.io 结尾作为用户的网站二级域名,建立一个新的分支作为创作分支(因为主分支是用来发布呈现网站的); 之后,进入生成的 blog
文件夹,在本地添加与远程 repository 库链接关联的句柄简称,并设置本地句柄简称推送/同步远程库上游的分支,与远程库建立安全关联,最后同步远程最新资源.

git remote add origin git@github.com:w-t-w/w-t-w.github.io.git
git pull --set-upstream origin master

再 npm 下载外部资源依赖包.

npm i/npm install

或者使用 yarn 下载外部资源依赖包.

yarn

最后启动 hexo 自身搭建的服务,生成本地的博客网站服务,默认端口在本地 ip 地址下的 4000 端口,假如你不想启动在 4000 端口,也可使用-p
其他端口号进行配置,比如-p 9777.

hexo server
hexo server -p 9777

PS: 最好先将本地代码上传至远程之后,再执行 hexo 配置.

git add .
git commit 'build:hexo next'
git push

默认的主题风格 theme 是 landscape
,假如你想更换,可以通过https://hexo.io/themes进行筛选,筛选之后进行配置,主题配置见下文
.

hexo 配置

PS: 以下所说的”根目录”指的就是当前创作的 repository 目录,”主题目录”就是根目录下/themes目录下的主题环境.

hexo deploy 发布配置

发布部署可以部署至 Github Page 个人网站,也可以部署至个人申请购买的云服务器中.

一. 部署至 Github Page 个人网站.

步骤

要想部署到 Github Page 个人网站,首先要下载 hexo-deployer-git 插件.

npm install hexo-deployer-git --save

然后在根目录底下的 _config.yml 文件中更改 deploy 发布配置,将 source 目录下面的内容进行构建发布到 repository github
地址的主分支上.

deploy:
    type: git
    #你的个人网站 github 库的链接地址,最好使用 git@ 开头的, https@ 开头的会报错
    repo: git@github.com:w-t-w/w-t-w.github.io.git
    #分支名
    branch: master

二. 部署至个人申请购买的云服务器.

步骤

将生成的网站博客项目直接通过 Filezilla 远程服务文件传输工具放入云服务器中;接着通过 Royal TSX(破解版😜) 远程服务管理工具对服务器进行深度管理,链接成功后,下载 nginx 代理.

# 这里直接使用阿里云CentOS服务器中默认的基于RPM的软件包管理器yum来下载nginx
yum install nginx

下载之后查看全局命令 nginx 是否存在,查看 nginx 的版本.

nginx -v

如果全局命令不存在 nginx,则运行 source ~/.bash_profile,使得配置在修改了环境变量的情况下进行重置.

source ~/.bash_profile

启动 nginx.

nginx
  • 顺便提一下 nginx 的其他命令.重启 nginx.

    nginx -s reload
    
  • 停止 nginx.

    nginx -s stop
    
  • 通过流(pipe)查询全部用户组 nginx 完整的进程状态.

    ps -ef | grep nginx
    
  • 强制杀掉 nginx 进程.

    #28009是 nginx 进程号,通过 ps -ef | grep nginx 可以查询到
    kill -9 28009
    

配置 nginx,将服务器代理 root 页面指向博客网站.

#通过测试 nginx,查询 nginx 配置文件所在目录
nginx -t
vim /etc/nginx/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
#...
server {
listen 80 default_server;
listen [::]:80 default_server;
#域名配置,需要申请购买备案(国内域名需要备案,国外域名则不需要)
server_name localhost;
#修改root页面指向网站博客项目
root /root/w-t-w;

# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

location / {
index index.html index.htm;
# 将 router 路由中不存在的页面重定向至 index.html.
try_files $uri $uri/ /index.html;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
#...

通过阿里云平台向云服务器添加安全组,开放 80(HTTP) 端口和 443(HTTPS) 端口.

配置完之后,基本可以通过 ip 来进行访问博客网站.但要想实现通过域名来访问还需要实行域名解析,通过 A记录 将域名指向一个 IPV4 地址,也就是购买的阿里云服务器 ECS ip.

'@' 是源域名(主域名 white-than-wood.zone).
'www' 则是 www.white-than-wood.zone.

这样就可以实现通过域名来访问博客网站,但并没有开启 SSL 证书,也就是并没有启动 https,要想完整实现开启 SSL 证书的域名还需要申请 SSL 证书以及再次配置 nginx.

一. SSL 证书

通过阿里云平台购买或者免费申请,可以获得 SSL 证书,这里申请的是一年期限的普通版 DV SSL 证书.

将申请好的 SSL 证书下载并通过 Filezilla 传输至远程服务器中,接着配置 nginx.

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
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
# 这里比较简单粗暴的将用户组置为 root root,使得 nginx 可以访问带有 root 权限的文件以及文件夹.
user root;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

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

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;

# http 配置
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;
root /root/w-t-w;

# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

location / {
index index.html;
# 将 router 路由中不存在的页面重定向至 index.html.
try_files $uri /$uri /index.html;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

# Settings for a TLS enabled server.
# https 配置,添加 SSL 证书.
server {
# https 访问端口一般为 443 端口
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
# 开启 SSL 证书
ssl on;
# SSL 证书绑定的域名
server_name white-than-wood.zone www.white-than-wood.zone;
root /root/w-t-w;

# SSL 证书具体配置
# 证书文件配置,证书文件位置目录必须为绝对路径
ssl_certificate "/etc/nginx/cert/xxxx_white-than-wood.zone.pem";
ssl_certificate_key "/etc/nginx/cert/xxxx_white-than-wood.zone.key";
# SSL 证书缓存有效期
ssl_session_timeout 5m;
# 安全链接可选的加密协议
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 安全链接加密算法
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
# 使用服务器端的首选算法
ssl_prefer_server_ciphers on;

# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

location / {
index index.html;
# 将 router 路由中不存在的页面重定向至 index.html.
try_files $uri $uri/ /index.html;
}

location = /login/oauth/access_token {
add_header Access-Control-Allow-Origin 'https://white-than-wood.zone/';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass https://github.com/;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}

重启一下 nginx,最后直接访问 https://white-than-wood.zone ~ Bingo!!

hexo theme 主题配置

示例

筛选好个人选中的主题之后,就需要在项目里面进行配置更换。首先需要下载远程 github 库里面的主题项目到项目根目录里面的 themes
文件夹底下,比如本人用的是 next 主题的 hexo.

# 注意 hexo 6.x 之前下载 next 主题远程 github 链接与 hexo 6.x 之后不同!
# hexo 6.x 之前
git clone https://github.com/iissnan/hexo-theme-next.git themes/next
# hexo 6.x 之后
git clone https://github.com/theme-next/hexo-theme-next themes/next

随后在根目录里面的 _config.yml 文件中更改 theme 配置.

theme: next

接着选择主题展示的方式,需要在主题目录下,在 _config.yml 文件中更改 Scheme 配置,比如 next 主题底下的 Scheme 配置.

scheme:
    #scheme: Muse
    #scheme: Mist
    scheme: Pisces
    #scheme: Gemini

hexo language 语言配置

步骤

每个主题的语言都是根据博客作者的母语来配置的,要想配置开发者个人国家的语言,还是在项目根目录底下的 _config.yml 文件中更改
language 配置.

language: zh-CN

再在主题目录底下的 _config.yml 文件中更改 language 配置.

language: zh-CN

hexo page 页面配置

步骤

hexo 提供了几个可供筛选的页面,常用的有这么几个:home(首页)、tags(标签)、about(关于我)、archives(档案)和categories(分类)
,首先要创建页面.

hexo new page tags
hexo new page about
hexo new page archives
hexo new page categories

接着在主题目录下,在 _config.yml 文件中更改 menu 配置,当然每个主题的配置不尽相同.

menu:
    home: / || fa fa-home
    about: /about/ || fa fa-user
    tags: /tags/ || fa fa-tags
    categories: /categories/ || fa fa-th
    archives: /archives/ || fa fa-archive

随后更改 source 目录底下创建的页面配置,以 tags 为例.

----------------------------
    title: 标签
    date: 2018-04-24 17:57:26
    type: "tags"
    comments: false
----------------------------

hexo avatar 头像配置

步骤

配置个人博客网站的头像,需要更改主题目录底下的 _config.yml 文件,添加 avatar 配置.

#头像url链接
# Sidebar Avatar
avatar:
  # Replace the default image and set the url here.
  url: https://avatars.githubusercontent.com/u/112366447?v=4
  # If true, the avatar will be dispalyed in circle.
  rounded: true
  # If true, the avatar will be rotated with the cursor.
  rotated: true

hexo search 全站搜索配置

步骤

假如想要配置 hexo 个人博客网站的全站搜索配置,首先要下载 hexo search 外部依赖包.

npm install hexo-generator-search --save
npm install hexo-generator-searchdb --save

更改根目录底下的 _config.yml 文件,添加 search 配置.

search:
    path: search.xml
    field: post
    format: html
    limit: 10000

开启主题目录底下的 _config.yml 文件中的 local_search 配置.

local_search:
    enable: true

hexo 博客文件配置

步骤

假如想要更改每一篇博客的文件名称,不再是默认的:title.md的文件名,需要在根目录底下的 _config.yml 文件中更改 new_post_name
配置.

new_post_name: :year-:month-:day-:title.md

hexo auto_excerpt 阅读全文配置

步骤

博客文章一般都会很长的,所以在首页要对博客进行超长省略,要想看所有的内容,点击阅读全文或者文章标题进入全文查看.在
hexo-theme-next 主题版本 7.6 之前需要再主题目录底下的 _config.yml 文件中更改 auto_excerpt 配置.

auto_excerpt:
    enable: true
    length: 200

在 hexo-theme-next 主题版本 7.6 之后,由于 auto_excerpt
这种超长省略不应该是主题插件应该做的,作者将此配置移除,并给出新的专门针对此配置的插件
hexo-excerpt
.

excerpt:
    #显示的 markdown 代码块层数
    depth: 5
    excerpt_excludes: []
    more_excludes: []
    #设置为true: 显示超长省略,只展示部分,隐藏全文
    #设置为false: 展示全文
    hideWholePostExcerpts: true
    excerpt_description: true
    #是否显示阅读全文按钮
    read_more_btn: true

hexo browsersync 热加载配置

步骤

在创作博客时,需要每次手动刷新页面才能看到修改后的结果,感觉非常没有效率,如果存在类hrm热加载这种插件就太爽了!
hexo-browsersync
可以直接解决这个问题.

npm install hexo-browsersync --save

此插件原理基于 browser-sync ,与 hexo 建立关联,当创作的文件内容发生改变时, browser-sync
就会监听到并刷新浏览器整个页面的内容,做到不需手动刷新,大大提高了创作效率.

#更改根目录底下的 _config.yml 文件,添加 browsersync 属性
#设置监听 watch 属性为 true 就可以了!
browsersync:
    watch: true
    logLevel: "warn"

hexo busuanzi_count 卜算子统计配置

步骤

上线之后,需要对个人博客进行管理,阅读人数以及次数对于创作者来说是很重要反馈点.统计配置需要更改主题目录底下的 _config.yml
文件,配置 busuanzi_count 卜算子统计.

#可配置查看个人博客的阅读人数、次数以及每篇博客文章的次数
busuanzi_count:
    enable: true
    total_visitors: true
    total_visitors_icon: fa fa-user
    total_views: true
    total_views_icon: fa fa-eye
    post_views: true
    post_views_icon: fa fa-eye

hexo baidu_analytics 百度统计配置

步骤

卜算子统计有时候会出现一些异常,比如pv莫名会加100,uv不再区分单个ip.为了追求更精确、更智能,这里采用百度统计.
统计配置需要更改主题目录底下的 _config.yml 文件,配置 baidu_analytics 百度统计.

根据
百度统计-使用配置-代码获取
,将 hm.js 后方自动生成的 id 配置至 baidu_analytics.

# Baidu Analytics
# See: https://tongji.baidu.com
baidu_analytics: 31f07c2ec89d10385ec28e8eea5bbc3a

hexo Gitalk 留言板配置

步骤

留言板 comments 是创作者与阅读者进行互动反馈的窗口,可以使阅读者与创作者共同进步.对于 hexo 来说,支持的留言板模式有很多种: ‘disqus | disqusjs | changyan | livere | gitalk | utterances’,比较常见、用户量比较大且与 github 关联性比较强的就是 gitalk 以及 disqus,disqusjs 需要’梯子’🐶才能评论,那么还是选用 gitalk .留言板配置需要更改主题目录下的 _config.yml 文件.

配置comments:

# Multiple Comment System Support
comments:
    # Available values: tabs | buttons
    style: tabs
    # Choose a comment system to be displayed by default.
    # Available values: disqus | disqusjs | changyan | livere | gitalk | utterances
    active: gitalk
    # Setting `true` means remembering the comment system selected by the visitor.
    storage: true
    # Lazyload all comment systems.
    lazyload: false
    # Modify texts or order for any naves, here are some examples.
    nav:
        gitalk:
            order: -2
        disqus:
            text: Load Disqus
            order: -1

配置gitalk:

# Gitalk
# For more information: https://gitalk.github.io
gitalk:
    enable: true
    github_id: w-t-w # GitHub repo owner
    repo: w-t-w.github.io # Repository name to store issues
    client_id: 1191ab5290535c1acb09 # GitHub Application Client ID
    client_secret: 8d2cbebac1ae1230f84d1f9f7a36f8008a42c14b # GitHub Application Client Secret
    admin_user: w-t-w # GitHub repo owner and collaborators, only these guys can initialize gitHub issues
    distraction_free_mode: true # Facebook-like distraction free mode
    # When the official proxy is not available, you can change it to your own proxy address
    proxy: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
    # proxy: login/oauth/access_token # This is official proxy address
    # Gitalk's display language depends on user's browser or system environment
    # If you want everyone visiting your site to see a uniform language, you can set a force language value
    # Available values: en | es-ES | fr | ru | zh-CN | zh-TW
    language: zh-CN

上面的配置项中有几个需要说明的:

  • client_id 与 client_secret 是 Github 的 OAuth 认证(下一个部分会介绍).
  • github_id 与 admin_user 这里建议填一样,填成个人的 github 账号名(不是邮箱,也不是用户名).
  • proxy 默认即是上面的地址,它其实会回调到这里 https://github.com/login/oauth/access_token.
  • 无论是部署至 GitHub Page 个人网站,还是部署至个人申请购买的云服务器,上面三点是必须要实行的,proxy 403 问题主要是在部署至个人申请购买的云服务器中时会遇到,而部署至 GitHub Page 个人网站不能进行自定义配置反向代理,所以只能使用 Gitalk 官方搭建的代理.

一. Github 的 OAuth 认证.

前提是得有一个 github 账号,才能注册 OAuth application ,这是 OAuth 应用注册地址.

PS: 如果有自定义域名(如个人申请的阿里云域名),则在上图中填入自定义域名.

注册之后,点击下图中’Generate a new client secret’按钮,在个人账号下的Settings -> Developer settings -> OAuth Apps下面可以查看 OAuth 认证 client_id 与 client_secret.

二. Gitalk 自动初始化.

原理: 通过 sitemap 中的信息,请求 github 开放 api 达到自动产生 issues 的目的.
基本的要求: github API 需要请求 token.

  • 申请 github Token.

    需要使用 Personal access tokens 方式,这种方式限制每小时5000次,结合缓存功能,基本满足需求.
    从 Github 的 Personal access tokens 页面,点击 Generate new token.

  • 安装 npm 依赖项.

    npm i -D md5 moment request xml-parser
    npm i -S hexo-generator-sitemap
    
  • 配置 sitemap.

    在根目录中创建 sitemap_template.xml.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    {% for post in posts %}
    <url>
    <loc>{{ post.permalink | uriencode }}</loc>
    {% if post.updated %}
    <lastmod>{{ post.updated.toISOString() }}</lastmod>
    {% elif post.date %}
    <lastmod>{{ post.date.toISOString() }}</lastmod>
    {% endif %}
    <date>{{ post.date }}</date>
    <title>{{ post.title + ' | ' + config.title }}</title>
    {# nunjucks 模版语法 https://github.com/mozilla/nunjucks #}
    <desc>{{ post.description | default(post.excerpt) | default(post.content) | default(config.description) | striptags | truncate(200, true, '') }}</desc>
    </url>
    {% endfor %}
    </urlset>
  • 修改根目录下的 _config.yml.

    #Sitemap
    sitemap:
      path: sitemap.xml
      template: ./sitemap_template.xml
      rel: false
      tag: true
      category: false
    
  • 生成 sitemap.xml 生产环境文件.

    hexo clean && hexo generate
    
  • 根目录添加 talk-auto-init.js (注意还是不要将此文件置于github中,建议ignore).

    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
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    const fs = require('fs');
    const path = require('path');
    const url = require('url');

    const request = require('request');
    const xmlParser = require('xml-parser');
    const md5 = require('md5');

    // 配置信息
    const config = {
    username: 'w-t-w', // GitHub repository 所有者,可以是个人或者组织。对应 Gitalk 配置中的 owner
    repo: "w-t-w.github.io", // 储存评论 issue 的 github 仓库名,仅需要仓库名字即可。对应 Gitalk 配置中的 repo
    token: 'ghp_EuXHDkOxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 前面申请的 personal access token
    sitemap: path.join(__dirname, './public/sitemap.xml'), // 个人站点的 sitemap 文件地址
    cache: true, // 是否启用缓存,启用缓存会将已经初始化的数据写入配置的 gitalkCacheFile 文件,下一次直接通过缓存文件判断
    gitalkCacheFile: path.join(__dirname, './gitalk-init-cache.json'), // 用于保存 gitalk 已经初始化的 id 列表
    gitalkErrorFile: path.join(__dirname, './gitalk-init-error.json'), // 用于保存 gitalk 初始化报错的数据
    };

    const api = 'https://api.github.com/repos/' + config.username + '/' + config.repo + '/issues';

    /**
    * 读取 sitemap 文件
    * 远程 sitemap 文件获取可参考 https://www.npmjs.com/package/sitemapper
    */
    const sitemapXmlReader = (file) => {
    try {
    const data = fs.readFileSync(file, 'utf8');
    const sitemap = xmlParser(data);
    let ret = [];
    sitemap.root.children.forEach(function (url) {
    const loc = url.children.find(function (item) {
    return item.name === 'loc';
    });
    if (!loc) {
    return false;
    }
    const title = url.children.find(function (item) {
    return item.name === 'title';
    });
    const desc = url.children.find(function (item) {
    return item.name === 'desc';
    });
    const date = url.children.find(function (item) {
    return item.name === 'date';
    });
    ret.push({
    url: loc.content,
    title: title.content,
    desc: desc.content,
    date: date.content,
    });
    });
    return ret;
    } catch (e) {
    return [];
    }
    };

    // 获取 gitalk 使用的 id
    const getGitalkId = ({
    url: u,
    date
    }) => {
    const link = url.parse(u);
    // 链接不存在,不需要初始化
    if (!link || !link.pathname) {
    return false;
    }
    if (!date) {
    return false;
    }
    return md5(link.pathname);
    };

    /**
    * 通过以请求判断是否已经初始化
    * @param {string} gitalk 初始化的id
    * @return {[boolean, boolean]} 第一个值表示是否出错,第二个值 false 表示没初始化, true 表示已经初始化
    */
    const getIsInitByRequest = (id) => {
    const options = {
    headers: {
    'Authorization': 'token ' + config.token,
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    'Accept': 'application/json'
    },
    url: api + '?labels=' + id + ',Gitalk',
    method: 'GET'
    };
    return new Promise((resolve) => {
    request(options, function (err, response, body) {
    if (err) {
    return resolve([err, false]);
    }
    if (response.statusCode != 200) {
    return resolve([response, false]);
    }
    const res = JSON.parse(body);
    if (res.length > 0) {
    return resolve([false, true]);
    }
    return resolve([false, false]);
    });
    });
    };

    /**
    * 通过缓存判断是否已经初始化
    * @param {string} gitalk 初始化的id
    * @return {boolean} false 表示没初始化, true 表示已经初始化
    */
    const getIsInitByCache = (() => {
    // 判断缓存文件是否存在
    let gitalkCache = false;
    try {
    gitalkCache = require(config.gitalkCacheFile);
    } catch (e) {}
    return function (id) {
    if (!gitalkCache) {
    return false;
    }
    if (gitalkCache.find(({
    id: itemId
    }) => (itemId === id))) {
    return true;
    }
    return false;
    };
    })();

    // 根据缓存,判断链接是否已经初始化
    // 第一个值表示是否出错,第二个值 false 表示没初始化, true 表示已经初始化
    const idIsInit = async (id) => {
    if (!config.cache) {
    return await getIsInitByRequest(id);
    }
    // 如果通过缓存查询到的数据是未初始化,则再通过请求判断是否已经初始化,防止多次初始化
    if (getIsInitByCache(id) === false) {
    return await getIsInitByRequest(id);
    }
    return [false, true];
    };

    // 初始化
    const gitalkInit = ({
    url,
    id,
    title,
    desc
    }) => {
    //创建issue
    const reqBody = {
    'title': title,
    'labels': [id, 'Gitalk'],
    'body': url + '\r\n\r\n' + desc
    };

    const options = {
    headers: {
    'Authorization': 'token ' + config.token,
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    'Accept': 'application/json',
    'Content-Type': 'application/json;charset=UTF-8'
    },
    url: api,
    body: JSON.stringify(reqBody),
    method: 'POST'
    };
    return new Promise((resolve) => {
    request(options, function (err, response, body) {
    if (err) {
    return resolve([err, false]);
    }
    if (response.statusCode != 201) {
    return resolve([response, false]);
    }
    return resolve([false, true]);
    });
    });
    };


    /**
    * 写入内容
    * @param {string} fileName 文件名
    * @param {string} content 内容
    */
    const write = async (fileName, content, flag = 'w+') => {
    return new Promise((resolve) => {
    fs.open(fileName, flag, function (err, fd) {
    if (err) {
    resolve([err, false]);
    return;
    }
    fs.writeFile(fd, content, function (err) {
    if (err) {
    resolve([err, false]);
    return;
    }
    fs.close(fd, (err) => {
    if (err) {
    resolve([err, false]);
    return;
    }
    });
    resolve([false, true]);
    });
    });
    });
    };

    const init = async () => {
    const urls = sitemapXmlReader(config.sitemap);
    // 报错的数据
    const errorData = [];
    // 已经初始化的数据
    const initializedData = [];
    // 成功初始化数据
    const successData = [];
    for (const item of urls) {
    const {
    url,
    date,
    title,
    desc
    } = item;
    const id = getGitalkId({
    url,
    date
    });
    if (!id) {
    console.log(`id: 生成失败 [ ${id} ] `);
    errorData.push({
    ...item,
    info: 'id 生成失败',
    });
    continue;
    }
    const [err, res] = await idIsInit(id);
    if (err) {
    console.log(`Error: 查询评论异常 [ ${title} ] , 信息:`, err || '无');
    errorData.push({
    ...item,
    info: '查询评论异常',
    });
    continue;
    }
    if (res === true) {
    // console.log(`--- Gitalk 已经初始化 --- [ ${title} ] `);
    initializedData.push({
    id,
    url,
    title,
    });
    continue;
    }
    console.log(`Gitalk 初始化开始... [ ${title} ] `);
    const [e, r] = await gitalkInit({
    id,
    url,
    title,
    desc
    });
    if (e || !r) {
    console.log(`Error: Gitalk 初始化异常 [ ${title} ] , 信息:`, e || '无');
    errorData.push({
    ...item,
    info: '初始化异常',
    });
    continue;
    }
    successData.push({
    id,
    url,
    title,
    });
    console.log(`Gitalk 初始化成功! [ ${title} ] - ${id}`);
    continue;
    }

    console.log(''); // 空输出,用于换行
    console.log('--------- 运行结果 ---------');
    console.log(''); // 空输出,用于换行

    if (errorData.length !== 0) {
    console.log(`报错数据: ${errorData.length} 条。参考文件 ${config.gitalkErrorFile}。`);
    await write(config.gitalkErrorFile, JSON.stringify(errorData, null, 2));
    }

    console.log(`本次成功: ${successData.length} 条。`);

    // 写入缓存
    if (config.cache) {
    console.log(`写入缓存: ${(initializedData.length + successData.length)} 条,已初始化 ${initializedData.length} 条,本次成功: ${successData.length} 条。参考文件 ${config.gitalkCacheFile}。`);
    await write(config.gitalkCacheFile, JSON.stringify(initializedData.concat(successData), null, 2));
    } else {
    console.log(`已初始化: ${initializedData.length} 条。`);
    }
    };

    init();
  • 修改 package.json 中 scripts 中的脚本,添加 “gitalk”:”node talk-auto-init.js”.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "scripts": {
    "clean": "hexo clean",
    "publish": "git push && yarn run clean && hexo generate -d",
    "server": "hexo server",
    "start": "yarn run clean && yarn run server -p 9777",
    "gitalk": "node talk-auto-init.js"
    }
    }
  • 测试 gitalk.

    yarn run gitalk
    
  • 已经缓存过后的结果:

    hexo-site@0.0.0 gitalk
    node talk-auto-init.js
    --------- 运行结果 ---------
    本次成功: 0 条。
    写入缓存: 7 条,已初始化 7 条,本次成功: 0 条。参考文件 /Users/wtw/frontEnd/w-t-w.github.io/gitalk-init-cache.json.
    

proxy 403错误

部署至个人申请购买的云服务器时,由于跨域问题,会出现 proxy 403 forbidden.解决它主要有这么几个方案.

  • nginx 反向代理解决方案(推荐).

    在云服务器 nginx 的博客配置中加入如下内容(注意个人博客的域名必须经过 https ssl 安全证书备案).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    server {
    listen 443 ssl http2 default_server;
    #...
    #这里省略的部分都是对于 ssl http2 证书的配置
    location = /login/oauth/access_token {
    add_header Access-Control-Allow-Origin 'https://white-than-wood.zone/'; //这里改成个人的域名,并删除注释
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
    if ($request_method = 'OPTIONS') {
    return 204;
    }
    proxy_pass https://github.com/;
    }
    #...
    }

    PS: nginx 反向代理之后,要将 proxy 指向代理域名下的链接回调进行修改.

    proxy: https://white-than-wood.zone/https://github.com/login/oauth/access_token
    
  • 自建一个 workers.

    地址: CloudFlare Workers.
    参考文章: 使用 CloudFlare Workers 实现 CORS Anywhere.

  • 使用其他人搭建的代理.

    比如这个 issues 介绍到的:

     proxy: https://shielded-brushlands-08810.herokuapp.com/https://github.com/login/oauth/access_token
    
  • 使用 Gitalk 官方搭建的代理.

    据gitalk issue gitalk 授权登录后报错 403,直接将使用 Gitalk 官方搭建的代理就可解决 403 forbidden 的问题,之前 Gitalk 官方搭建的代理由于太多人接入导致被官方限制使用,用户经常发现不能正常使用,现在已经对代理进行优化改进,不会出现限制使用的情况.

     https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
    
  • 更新 gitalk 版本至1.7.2.

    据gitalk issue gitalk 授权登录后报错 403,直接将 gitalk 版本升级至 1.7.2 就可解决 403 forbidden 的问题.

hexo GitHub Page 个人网站自定义域名

部署至 GitHub Page 个人网站配置自定义域名时,需要将个人申请购买的域名(最好是通过 https ssl 安全证书备案)进行域名解析,GitHub Page 也需要 CNAME 配置.

一. 阿里云域名解析.

步骤

域名解析简单来说实际上就是通过配置,将域名解析至某一个 IP 地址或者域名.首先进入阿里云服务管理平台,对已经申请购买的域名进行解析.

添加记录或者对某一个列表项进行修改.可以使用 A记录 也可以使用 CNAME.

A记录 是将域名指向一个 IPV4 地址.
CNAME 是将域名指向另外一个域名.

这里使用的是 A记录,将 w-t-w.github.io 的 IP 地址作为记录值.

ping w-t-w.github.io

主机记录,解析后的域名基本上就两种,‘@’和’www’.

'@' 是源域名(主域名 white-than-wood.zone).
'www' 则是 www.white-than-wood.zone.

二. GitHub Page个人网站CNAME配置.

在 source 目录下新建 CNAME 文件(不带任何后缀),在文件内写入你的主域名.

1
white-than-wood.zone

或者在 Github Page 个人配置下设置自定义域名,Github 会自动添加 CNAME 文件.

这两种方式这里是全部使用的.全部搞定之后,就可以通过 white-than-wood.zone 访问 w-t-w.github.io GitHub Page 个人网站.

延伸

既然已经通过阿里云服务 A记录 或者 CNAME 解析域名,为什么还需要在 GitHub Page 个人网站上配置 CNAME ?

解: 云服务 A记录 或者 CNAME 域名解析至 GitHub Page,通过 white-than-wood.zone 可以访问 GitHub Page 个人网站,那原来的 w-t-w.github.io 域名怎么办呢? GitHub Page 也通过 CNAME 域名解析至 white-than-wood.zone. 使得 github.io 域名也可以直接访问个人申请购买的域名.

hexo 其他配置

示例

在根目录底下的 _config.yml 文件中更改网站title(标题)、author(作者)、keywords(关键字)、description(描述)配置,比如本人的配置.

title: wtw's Frontend
author: wtw(比木白)
keywords: hexo,hexo blog,webpack
subtitle: 道阻且长,行则将至
description: 前端领域内所不知道的都探索于此!

再主题目录底下的 _config.yml 文件重设置个人的github(github托管代码网址)、google(google个人资料网址)、gmail(gmail邮箱网址)
、twitter(twitter个人微博网址)等等,比如本人的配置.

social:
  GitHub: https://github.com/w-t-w || fab fa-github
  E-Mail: https://dreamthen.00@gmail.com || fa fa-envelope
  Google: https://plus.google.com/u/0/103833130011211353424 || fab fa-google

发布到 GitHub Page 上之后,你会发现 README.md 莫名的消失,主要是因为 hexo 构建发布到远程的目录只是 source 目录,所以只要将
README.md copy 一份放到 source 目录下,并且配置渲染时需要跳过的文件就可以了.

#hexo 构建渲染时会将 source 目录下 markdown 类型文件转译为 html,不需要将 README.md 转译,所以直接 skip render
#更改根目录底下的 _config.yml 文件,修改 skip_render 属性
skip_render: README.md

当创作中图片过小,阅读者无法看清的情况下,放大配置就已然成为必要 .fancybox
配置可以点击使图片放大,并可同时查看全文的图片.更改主题目录下的 _config.yml 文件 fancybox 属性.

# FancyBox is a tool that offers a nice and elegant way to add zooming functionality for images.
# For more information: https://fancyapps.com/fancybox/
fancybox: true