Hexo 博客集成内容管理系统 Decap

背景

一开始在做个人博客的框架选型时,我考虑过 WordPress、Typecho 等 PHP 框架,它们都自带一个内容管理系统,非常便于管理。但因为不想在服务器上花太多成本,我仍然选择了 Hexo 这种静态博客生成器,因为有很多优秀的静态博客托管平台可以选择。从此开始了一段博客折腾之旅。

曾经我也向朋友推荐 Hexo,列举它的种种优点,现在我也开始觉得 Hexo 写作太繁琐了。

静态博客最主要的缺点,一是不支持评论,二是缺少一个后台。不支持评论,写好的内容,无法与读者交流,是一件挺痛苦的事情。为此我搜遍互联网,最终选择了自己开发 Twikoo 无服务器评论系统,评论这件难题算是解决了。

缺少后台,便无法专注于写作,在 IDE(集成开发环境)里写博客是个很奇妙的体验,总觉得自己写的并不是博客,而是某个程序。插入图片也是个大难题,本来一个 Ctrl + V 快捷键就能插入的图片,在静态博客里需要保存图片,重命名,移动到博客静态资源目录,复制相对路径,粘贴到 ![]() 块中……受不了了,能不能再做个无服务器的内容管理系统?理论可以,但是没那个精力啊……

探索

以下评测的方案,都仅限于 无服务器 方案,有服务器方案不参与评测,原因很简单,我搭建静态博客就是因为它无服务器的优点,如果我有服务器,为什么不直接搭建 Typecho 呢?

试用过 ChenYFan 大佬开发的两款 Hexo 内容管理工具 HexoPlusPlus,HexoPlusPlus 停止开发之后又推出了 Wexagonal,都不算成熟,对多目录支持不够好,小问题不断,最后还是没能用起来。

再后来听说了 Headless CMS 的概念,也就是无头内容管理系统,和普通博客相比,普通博客输出 HTML 网页,无头博客输出 JSON 内容接口。博客具体长什么样就完全由前端决定了。典型代表是 Ghost,具体可以看他们的教程 Build A Custom Publication With Headless Ghost + Hexo。缺点很明显,CMS 只提供接口,我还需要自己开发插件来调用这些接口获取博客内容,这是个不小的改造,就算改造好了,万一以后 Ghost 停止运营了呢?

解决

怎么办?静态博客就没办法做内容管理了吗?

Git-based CMS 来了,它让我直呼这就是静态博客的终极内容管理方案。

Git-based CMS 就是基于 Git 的内容管理系统,原理是通过前端直接调用诸如 GitHub 等源代码托管网站的 API 来实现内容管理,不需要自己准备服务器。常见的 Git-based CMS 有两个,一个是 decap-cms,一个是 tinacms,以下是我总结出的它们的特点。

名称前身特点
Decap CMSNetlify CMS拥有较多用户,界面简洁,配置难度一般,用一个 js 加一个配置文件实现,2022 年开始停止开发
Tina CMSForestry.io后起之秀,编辑器功能更强大,配置难度简单,用专门的中间件实现,截至目前仍在积极开发

我先选择了 Tina,根据官方文档一番操作集成好之后,我遇到了以下问题:

  1. Tina 的 markdown 编辑器不支持 md 内嵌 html,比如 Hexo 依赖 <!-- more --> 来实现摘要和正文的分离,这种语法在 Tina 编辑器中直接报错。
  2. 子目录支持还没有开发好,需要复杂的配置来支持。
  3. 需要项目安装两个包 @tinacms/clitinacms,感觉有点重。

第二、三个问题还好,第一个问题就比较烦人了,正文都编辑不了怎么玩?

再试试 Decap,虽然配置起来复杂了些。但用起来总归没出现什么问题。

总结

虽然两家 CMS 都没有在文档中写 Hexo 的集成方案,但是和同类的静态博客框架集成是差不多的。如果 Tina 能解决 markdown 非法字符的问题,两家 CMS 都是不错的选择。这里简单总结一下 Decap 与 Hexo 的集成步骤。

重要前提:Hexo 博客源码(不是生成好的 html)托管在 GitHub、GitLab、Bitbucket、Azure 四者之一的源代码托管平台,如果你纠结于选哪个,我推荐:除 GitHub 之外,哪个你访问起来快选哪个。如果你的博客源码还在本地,那我建议你推上去再说,许多小博客停止更新的原因,都是源码找不到了。

为啥我不推荐放 GitHub?
因为 Github 的 OAuth 实现 必须要有服务器,这导致配置起来稍微麻烦那么一丢丢,也就一丢丢。如果你已经放到 GitHub 上了,也不用再迁到其他平台的。

还有一个重要前提:博客已经做好了流水线自动部署,如果没有自动部署,你的内容就只会被提交到 Git 仓库,而不会自动发表到网站上。自动部署不是本文重点,你可以看我以前的文章 给Hexo博客配置Azure CI持续集成实现自动部署,或者搜索 Hexo 自动部署,找到各种各样的教程。

Decap 虽然是 Netlify 出品,但并不强依赖 Netlify 的服务,除非你想用 Netlify Identity 做身份认证,或者用 Git Gateway 的后端模式,否则你完全可以不申请 Netlify 账号。

好了,准备好代码仓库和流水线以后,我们正式开始搭建 CMS。

修改 Hexo 配置文件 _config.yml

_config.yml
1
skip_render: admin/**/*

这代表原样输出 source/admin 目录下的文件,不经过 Hexo 的渲染器。如果不配置,后面我们放到 admin 目录下的 yml 配置文件会被 Hexo 转换成 json 文件,影响使用。

如果你的 skip_render 还配置有别的目录,比如 assets,可以这样写

_config.yml
1
skip_render: (admin|assets)/**/*

创建一个文件 source/admin/index.html,这是内容管理系统的入口文件。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex" />
<title>博客管理</title>
</head>
<body>
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
</body>
</html>

再创建一个文件 source/admin/config.yml,这是内容管理系统的配置文件。

配置文件要按照自己博客的实际情况改写,以下是本博客的配置文件,仅供参考:

config.yml
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
# https://decapcms.org/docs/configuration-options/
backend:
name: bitbucket
repo: iMaeGoo/blog
branch: edit
auth_type: implicit
app_id: GeCbq2xd5d2s3mQvcN
media_folder: "source/gallery/2023"
public_folder: "/gallery/2023"
site_url: "https://www.imaegoo.com"
logo_url: "https://www.imaegoo.com/images/logo.png"
locale: "zh_Hans"
common_col_conf: &common_col_conf
create: true
slug: "{{fields.filename}}"
sortable_fields:
- "commit_date"
- "title"
- "date"
- "updated"
# https://decapcms.org/docs/widgets/
fields:
- label: "文件名"
name: "filename"
widget: "string"
- label: "标题"
name: "title"
widget: "string"
- label: "发表日期"
name: "date"
widget: "datetime"
format: "YYYY-MM-DD HH:mm:ss"
date_format: "YYYY-MM-DD"
time_format: "HH:mm:ss"
- label: "更新日期"
name: "updated"
widget: "datetime"
format: "YYYY-MM-DD HH:mm:ss"
date_format: "YYYY-MM-DD"
time_format: "HH:mm:ss"
required: false
- label: "封面"
name: "cover"
widget: "image"
required: false
- label: "标签"
name: "tags"
widget: "select"
multiple: true
required: false
options:
- "amazon"
- "android"
- "angularjs"
- "azure"
- "cdn"
- "chartjs"
- "chrome"
- "csharp"
- "css"
- "devops"
- "diary"
- "docker"
- "edge"
- "git"
- "github"
- "hexo"
- "html"
- "icarus"
- "java"
- "js"
- "life"
- "material"
- "mysql"
- "nodejs"
- "onedrive"
- "oneindex"
- "php"
- "restapi"
- "security"
- "serverless"
- "shadowdefender"
- "tool"
- "twikoo"
- "ubuntu"
- "vagrant"
- "vb"
- "vite"
- "vue"
- "webpack"
- "windows"
- "xlsx"
- "小程序"
- label: "分类"
name: "categories"
widget: "select"
multiple: true
required: false
options:
- "Diary"
- "Tool"
- "Tech"
- "FrontEnd"
- "BackEnd"
- "Windows"
- "Android"
- "Linux"
- "Serverless"
- label: "正文"
name: "body"
widget: "markdown"
- label: "原创"
name: "toc"
widget: "boolean"
default: true
- label: "评论"
name: "comments"
widget: "boolean"
default: true
collections:
- name: "2023"
label: "2023"
folder: "source/_posts/2023"
preview_path: "2023/{{filename}}/"
<<: *common_col_conf
- name: "2022"
label: "2022"
folder: "source/_posts/2022"
preview_path: "2022/{{filename}}/"
<<: *common_col_conf
- name: "2021"
label: "2021"
folder: "source/_posts/2021"
preview_path: "2021/{{filename}}/"
<<: *common_col_conf
- name: "2020"
label: "2020"
folder: "source/_posts/2020"
preview_path: "2020/{{filename}}/"
<<: *common_col_conf
- name: "2019"
label: "2019"
folder: "source/_posts/2019"
preview_path: "2019/{{filename}}/"
<<: *common_col_conf
- name: "2018"
label: "2018"
folder: "source/_posts/2018"
preview_path: "2018/{{filename}}/"
<<: *common_col_conf
- name: "pages"
label: "Pages"
files:
- name: "friends"
label: "友链"
file: "source/friends/index.md"
preview_path: "friends/"
<<: *common_col_conf
- name: "about"
label: "关于"
file: "source/about/index.md"
preview_path: "about/"
<<: *common_col_conf

有关配置文件的介绍,官方文档 已经讲得非常详细了。我再简单介绍一下配置文件的几个部分。

  • backend 部分是博客托管平台的配置,在我的示例中用的是 bitbucket
  • media_folder 和 public_folder 是图片资源的存放位置和链接前缀
  • site_url 和 logo_url 是网站地址和 LOGO 的地址
  • locale 是内容管理系统的显示语言
  • common_col_conf 是我自己定义的一个块,里面是文章 front-matter 元数据的描述,比如标题、发表日期等
  • collections 是集合,如果你的博客文章都放到一个目录里,这里就只需要配置一个,如果是像我一样分开放的,那就一个一个都配置上

配置文件配好之后,我们还需要创建一个 OAuth 应用来支持 Decap 访问到我们的博客源码仓库,登录自己的博客源码托管平台,GitHub 参考这个GitLab 参考这个Azure 参考这个Bitbucket 参考这个,以 Bitbucket 为例:

名称描述随意,Callback URL 填写 https://博客地址/admin/,权限选择 Account-ReadPull requests-Write,创建好的 OAuth 应用是这样的:

把回显的 Key 填到 config.yml 的 backend.app_id 里,Secret 不用管。

然后……就没啦!部署博客,然后访问 https://博客地址/admin/ 用 Bitbucket 登录,就可以愉快地进行创作了。

Hexo 博客集成内容管理系统 Decap

https://www.imaegoo.com/2023/hexo-decap/

作者

iMaeGoo

发布于

2023-03-10

更新于

2023-03-10

许可协议

CC BY 4.0

评论