Qexo博客后端搭建

引言

在博客自动化的基础上加入 Qexo:一个快速、强大、漂亮的在线 Hexo 编辑器。

特色功能

  • 自定义图床上传图片

  • 在线配置编辑

  • 在线页面管理

  • 开放 API

  • 自动检查更新

  • 实验性的在线更新

  • 自动填充 date 模板

  • 基于时间戳的 abbrlink

快速上手

Vercel 部署

申请 MongoDB

注册 MongoDB 账号并创建免费 MongoDB 数据库,区域一定要选择 AWS / N. Virginia (us-east-1)。

在 Clusters 页面点击 CONNECT,按步骤设置允许所有 IP 地址的连接,创建数据库用户,并记录数据库连接信息,密码即为你所设置的值。

一键部署

点击开始部署,部署到 Vercel

第一次部署会出现报错,原因在于没有设置环境变量(记得多看文档!):

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
[15:50:22.190] Cloning github.com/Yousazoe/Qexo (Branch: main, Commit: e9f1dc4)
[15:50:22.766] Cloning completed: 575.787ms
[15:50:24.432] Looking up build cache...
[15:50:24.715] Build Cache not found
[15:50:25.105] Running "vercel build"
[15:50:25.540] Vercel CLI 24.2.5-canary.2 build (beta) — https://vercel.com/feedback
[15:50:25.635] ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
[15:50:25.635] │ WARN! Due to `builds` existing in your configuration file, the Build and Development Settings defined in your Project Settings will not apply. Learn More: https://vercel.link/unused-build-settings │
[15:50:25.636] └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
[15:50:26.204] Collecting asgiref==3.4.1
[15:50:26.259] Downloading asgiref-3.4.1-py3-none-any.whl (25 kB)
[15:50:26.298] Collecting autopep8==1.5.7
[15:50:26.311] Downloading autopep8-1.5.7-py2.py3-none-any.whl (45 kB)
[15:50:26.323] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 45.0/45.0 kB 4.3 MB/s eta 0:00:00
[15:50:26.363] Collecting beautifulsoup4==4.10.0
[15:50:26.376] Downloading beautifulsoup4-4.10.0-py3-none-any.whl (97 kB)
[15:50:26.392] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.4/97.4 kB 6.2 MB/s eta 0:00:00
[15:50:26.444] Collecting boto==2.49.0
[15:50:26.457] Downloading boto-2.49.0-py2.py3-none-any.whl (1.4 MB)
[15:50:26.497] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 36.1 MB/s eta 0:00:00
[15:50:26.965] Collecting boto3==1.20.23
[15:50:26.980] Downloading boto3-1.20.23-py3-none-any.whl (131 kB)
[15:50:26.988] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 131.8/131.8 kB 26.6 MB/s eta 0:00:00
[15:50:27.537] Collecting botocore==1.23.23
[15:50:27.552] Downloading botocore-1.23.23-py3-none-any.whl (8.4 MB)
[15:50:27.647] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.4/8.4 MB 91.5 MB/s eta 0:00:00
[15:50:27.717] Collecting certifi==2021.5.30
[15:50:27.732] Downloading certifi-2021.5.30-py2.py3-none-any.whl (145 kB)
[15:50:27.739] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 145.5/145.5 kB 31.7 MB/s eta 0:00:00
[15:50:27.954] Collecting cffi==1.15.0
[15:50:27.968] Downloading cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (444 kB)
[15:50:27.978] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 444.3/444.3 kB 59.5 MB/s eta 0:00:00
[15:50:28.015] Collecting charset-normalizer==2.0.4
[15:50:28.028] Downloading charset_normalizer-2.0.4-py3-none-any.whl (36 kB)
[15:50:28.096] Collecting click==8.0.1
[15:50:28.110] Downloading click-8.0.1-py3-none-any.whl (97 kB)
[15:50:28.116] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.4/97.4 kB 22.1 MB/s eta 0:00:00
[15:50:28.150] Collecting colorama==0.4.4
[15:50:28.164] Downloading colorama-0.4.4-py2.py3-none-any.whl (16 kB)
[15:50:28.201] Collecting Deprecated==1.2.13
[15:50:28.215] Downloading Deprecated-1.2.13-py2.py3-none-any.whl (9.6 kB)
[15:50:28.242] Collecting dj-database-url==0.5.0
[15:50:28.256] Downloading dj_database_url-0.5.0-py2.py3-none-any.whl (5.5 kB)
[15:50:28.377] Collecting Django==3.0.5
[15:50:28.391] Downloading Django-3.0.5-py3-none-any.whl (7.5 MB)
[15:50:28.476] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.5/7.5 MB 91.8 MB/s eta 0:00:00
[15:50:28.554] Collecting djongo==1.3.6
[15:50:28.571] Downloading djongo-1.3.6.tar.gz (331 kB)
[15:50:28.580] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 331.6/331.6 kB 47.4 MB/s eta 0:00:00
[15:50:28.605] Preparing metadata (setup.py): started
[15:50:28.777] Preparing metadata (setup.py): finished with status 'done'
[15:50:28.820] Collecting django-cors-headers==3.10.1
[15:50:28.834] Downloading django_cors_headers-3.10.1-py3-none-any.whl (12 kB)
[15:50:28.866] Collecting dnspython==1.16.0
[15:50:28.889] Downloading dnspython-1.16.0-py2.py3-none-any.whl (188 kB)
[15:50:28.897] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 188.4/188.4 kB 37.4 MB/s eta 0:00:00
[15:50:28.939] Collecting gunicorn==20.1.0
[15:50:28.956] Downloading gunicorn-20.1.0-py3-none-any.whl (79 kB)
[15:50:28.962] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.5/79.5 kB 19.5 MB/s eta 0:00:00
[15:50:28.992] Collecting idna==3.2
[15:50:29.008] Downloading idna-3.2-py3-none-any.whl (59 kB)
[15:50:29.014] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 59.6/59.6 kB 14.2 MB/s eta 0:00:00
[15:50:29.048] Collecting jmespath==0.10.0
[15:50:29.062] Downloading jmespath-0.10.0-py2.py3-none-any.whl (24 kB)
[15:50:29.099] Collecting prettytable==2.2.0
[15:50:29.114] Downloading prettytable-2.2.0-py3-none-any.whl (23 kB)
[15:50:29.177] Collecting pyasn1==0.4.8
[15:50:29.192] Downloading pyasn1-0.4.8-py2.py3-none-any.whl (77 kB)
[15:50:29.198] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 77.1/77.1 kB 17.4 MB/s eta 0:00:00
[15:50:29.232] Collecting pycodestyle==2.7.0
[15:50:29.245] Downloading pycodestyle-2.7.0-py2.py3-none-any.whl (41 kB)
[15:50:29.250] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.7/41.7 kB 10.0 MB/s eta 0:00:00
[15:50:29.276] Collecting pycparser==2.21
[15:50:29.292] Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB)
[15:50:29.298] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 118.7/118.7 kB 25.1 MB/s eta 0:00:00
[15:50:29.342] Collecting PyGithub==1.55
[15:50:29.359] Downloading PyGithub-1.55-py3-none-any.whl (291 kB)
[15:50:29.369] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 291.7/291.7 kB 41.7 MB/s eta 0:00:00
[15:50:29.414] Collecting PyJWT==2.3.0
[15:50:29.428] Downloading PyJWT-2.3.0-py3-none-any.whl (16 kB)
[15:50:29.883] Collecting pymongo==3.12.0
[15:50:29.902] Downloading pymongo-3.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (531 kB)
[15:50:29.913] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 531.9/531.9 kB 61.4 MB/s eta 0:00:00
[15:50:29.970] Collecting PyNaCl==1.4.0
[15:50:29.988] Downloading PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl (961 kB)
[15:50:30.003] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 961.1/961.1 kB 77.6 MB/s eta 0:00:00
[15:50:30.065] Collecting python-dateutil==2.8.2
[15:50:30.079] Downloading python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
[15:50:30.087] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 247.7/247.7 kB 48.6 MB/s eta 0:00:00
[15:50:30.111] Collecting python-decouple==3.4
[15:50:30.126] Downloading python_decouple-3.4-py3-none-any.whl (9.5 kB)
[15:50:30.212] Collecting pytz==2021.1
[15:50:30.233] Downloading pytz-2021.1-py2.py3-none-any.whl (510 kB)
[15:50:30.244] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 510.8/510.8 kB 58.8 MB/s eta 0:00:00
[15:50:30.307] Collecting requests==2.26.0
[15:50:30.321] Downloading requests-2.26.0-py2.py3-none-any.whl (62 kB)
[15:50:30.327] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.3/62.3 kB 13.6 MB/s eta 0:00:00
[15:50:30.361] Collecting rsa==4.7.2
[15:50:30.376] Downloading rsa-4.7.2-py3-none-any.whl (34 kB)
[15:50:30.413] Collecting s3transfer==0.5.0
[15:50:30.427] Downloading s3transfer-0.5.0-py3-none-any.whl (79 kB)
[15:50:30.433] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.2/79.2 kB 15.4 MB/s eta 0:00:00
[15:50:30.464] Collecting six==1.16.0
[15:50:30.480] Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
[15:50:30.521] Collecting soupsieve==2.2.1
[15:50:30.534] Downloading soupsieve-2.2.1-py3-none-any.whl (33 kB)
[15:50:30.567] Collecting sqlparse==0.2.4
[15:50:30.583] Downloading sqlparse-0.2.4-py2.py3-none-any.whl (38 kB)
[15:50:30.612] Collecting tcping==0.1.1rc1
[15:50:30.626] Downloading tcping-0.1.1rc1.tar.gz (4.1 kB)
[15:50:30.631] Preparing metadata (setup.py): started
[15:50:30.800] Preparing metadata (setup.py): finished with status 'done'
[15:50:30.832] Collecting toml==0.10.2
[15:50:30.845] Downloading toml-0.10.2-py2.py3-none-any.whl (16 kB)
[15:50:30.964] Collecting ujson==4.1.0
[15:50:30.979] Downloading ujson-4.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl (179 kB)
[15:50:30.987] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 179.5/179.5 kB 31.3 MB/s eta 0:00:00
[15:50:31.011] Collecting Unipath==1.1
[15:50:31.025] Downloading Unipath-1.1.tar.gz (30 kB)
[15:50:31.034] Preparing metadata (setup.py): started
[15:50:31.200] Preparing metadata (setup.py): finished with status 'done'
[15:50:31.259] Collecting urllib3==1.26.7
[15:50:31.272] Downloading urllib3-1.26.7-py2.py3-none-any.whl (138 kB)
[15:50:31.280] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 138.8/138.8 kB 27.9 MB/s eta 0:00:00
[15:50:31.313] Collecting wcwidth==0.2.5
[15:50:31.326] Downloading wcwidth-0.2.5-py2.py3-none-any.whl (30 kB)
[15:50:31.367] Collecting whitenoise==5.3.0
[15:50:31.382] Downloading whitenoise-5.3.0-py2.py3-none-any.whl (19 kB)
[15:50:31.520] Collecting wrapt==1.13.3
[15:50:31.533] Downloading wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (81 kB)
[15:50:31.539] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.3/81.3 kB 21.7 MB/s eta 0:00:00
[15:50:31.576] Collecting Markdown==3.3.6
[15:50:31.591] Downloading Markdown-3.3.6-py3-none-any.whl (97 kB)
[15:50:31.597] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.8/97.8 kB 22.1 MB/s eta 0:00:00
[15:50:31.626] Collecting html2text==2020.1.16
[15:50:31.640] Downloading html2text-2020.1.16-py3-none-any.whl (32 kB)
[15:50:31.987] Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.9/site-packages (from gunicorn==20.1.0->-r requirements.txt (line 18)) (58.1.0)
[15:50:32.524] Collecting importlib-metadata>=4.4
[15:50:32.538] Downloading importlib_metadata-4.11.4-py3-none-any.whl (18 kB)
[15:50:32.616] Collecting zipp>=0.5
[15:50:32.633] Downloading zipp-3.8.0-py3-none-any.whl (5.4 kB)
[15:50:32.691] Using legacy 'setup.py install' for djongo, since package 'wheel' is not installed.
[15:50:32.691] Using legacy 'setup.py install' for tcping, since package 'wheel' is not installed.
[15:50:32.692] Using legacy 'setup.py install' for Unipath, since package 'wheel' is not installed.
[15:50:32.914] Installing collected packages: wcwidth, Unipath, sqlparse, pytz, python-decouple, pyasn1, dj-database-url, certifi, boto, zipp, wrapt, whitenoise, urllib3, ujson, toml, soupsieve, six, rsa, pymongo, PyJWT, pycparser, pycodestyle, prettytable, jmespath, idna, html2text, gunicorn, dnspython, colorama, click, charset-normalizer, asgiref, tcping, requests, python-dateutil, importlib-metadata, Django, Deprecated, cffi, beautifulsoup4, autopep8, PyNaCl, Markdown, djongo, django-cors-headers, botocore, s3transfer, PyGithub, boto3
[15:50:32.961] Running setup.py install for Unipath: started
[15:50:33.168] Running setup.py install for Unipath: finished with status 'done'
[15:50:35.005] Running setup.py install for tcping: started
[15:50:35.215] Running setup.py install for tcping: finished with status 'done'
[15:50:37.169] Running setup.py install for djongo: started
[15:50:37.439] Running setup.py install for djongo: finished with status 'done'
[15:50:38.307] Successfully installed Deprecated-1.2.13 Django-3.0.5 Markdown-3.3.6 PyGithub-1.55 PyJWT-2.3.0 PyNaCl-1.4.0 Unipath-1.1 asgiref-3.4.1 autopep8-1.5.7 beautifulsoup4-4.10.0 boto-2.49.0 boto3-1.20.23 botocore-1.23.23 certifi-2021.5.30 cffi-1.15.0 charset-normalizer-2.0.4 click-8.0.1 colorama-0.4.4 dj-database-url-0.5.0 django-cors-headers-3.10.1 djongo-1.3.6 dnspython-1.16.0 gunicorn-20.1.0 html2text-2020.1.16 idna-3.2 importlib-metadata-4.11.4 jmespath-0.10.0 prettytable-2.2.0 pyasn1-0.4.8 pycodestyle-2.7.0 pycparser-2.21 pymongo-3.12.0 python-dateutil-2.8.2 python-decouple-3.4 pytz-2021.1 requests-2.26.0 rsa-4.7.2 s3transfer-0.5.0 six-1.16.0 soupsieve-2.2.1 sqlparse-0.2.4 tcping-0.1.1rc1 toml-0.10.2 ujson-4.1.0 urllib3-1.26.7 wcwidth-0.2.5 whitenoise-5.3.0 wrapt-1.13.3 zipp-3.8.0
[15:50:38.307] WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
[15:51:09.211] Traceback (most recent call last):
[15:51:09.211] File "/vercel/path0/manage.py", line 22, in <module>
[15:51:09.211] main()
[15:51:09.212] File "/vercel/path0/manage.py", line 18, in main
[15:51:09.212] execute_from_command_line(sys.argv)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
[15:51:09.212] utility.execute()
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
[15:51:09.212] self.fetch_command(subcommand).run_from_argv(self.argv)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 328, in run_from_argv
[15:51:09.212] self.execute(*args, **cmd_options)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 369, in execute
[15:51:09.212] output = self.handle(*args, **options)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 83, in wrapped
[15:51:09.212] res = handle_func(*args, **kwargs)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/makemigrations.py", line 101, in handle
[15:51:09.212] loader.check_consistent_history(connection)
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/loader.py", line 283, in check_consistent_history
[15:51:09.212] applied = recorder.applied_migrations()
[15:51:09.212] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 76, in applied_migrations
[15:51:09.213] if self.has_table():
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 56, in has_table
[15:51:09.213] return self.Migration._meta.db_table in self.connection.introspection.table_names(self.connection.cursor())
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 48, in table_names
[15:51:09.213] return get_names(cursor)
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 43, in get_names
[15:51:09.213] return sorted(ti.name for ti in self.get_table_list(cursor)
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/djongo/introspection.py", line 47, in get_table_list
[15:51:09.213] for c in cursor.db_conn.list_collection_names()
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/pymongo/database.py", line 880, in list_collection_names
[15:51:09.213] for result in self.list_collections(session=session, **kwargs)]
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/pymongo/database.py", line 842, in list_collections
[15:51:09.213] return self.__client._retryable_read(
[15:51:09.213] File "/usr/local/lib/python3.9/site-packages/pymongo/mongo_client.py", line 1514, in _retryable_read
[15:51:09.214] server = self._select_server(
[15:51:09.214] File "/usr/local/lib/python3.9/site-packages/pymongo/mongo_client.py", line 1346, in _select_server
[15:51:09.214] server = topology.select_server(server_selector)
[15:51:09.214] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 244, in select_server
[15:51:09.214] return random.choice(self.select_servers(selector,
[15:51:09.214] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 202, in select_servers
[15:51:09.214] server_descriptions = self._select_servers_loop(
[15:51:09.214] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 218, in _select_servers_loop
[15:51:09.214] raise ServerSelectionTimeoutError(
[15:51:09.214] pymongo.errors.ServerSelectionTimeoutError: connection closed,connection closed,connection closed, Timeout: 30s, Topology Description: <TopologyDescription id: 62971a4e82b1c0159350eafa, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('cluster0-shard-00-00.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>, <ServerDescription ('cluster0-shard-00-01.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>, <ServerDescription ('cluster0-shard-00-02.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>]>
[15:51:40.368] Traceback (most recent call last):
[15:51:40.368] File "/vercel/path0/manage.py", line 22, in <module>
[15:51:40.368] main()
[15:51:40.368] File "/vercel/path0/manage.py", line 18, in main
[15:51:40.368] execute_from_command_line(sys.argv)
[15:51:40.368] File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
[15:51:40.368] utility.execute()
[15:51:40.368] File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
[15:51:40.369] self.fetch_command(subcommand).run_from_argv(self.argv)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 328, in run_from_argv
[15:51:40.369] self.execute(*args, **cmd_options)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 369, in execute
[15:51:40.369] output = self.handle(*args, **options)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 83, in wrapped
[15:51:40.369] res = handle_func(*args, **kwargs)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 86, in handle
[15:51:40.369] executor = MigrationExecutor(connection, self.migration_progress_callback)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/executor.py", line 18, in __init__
[15:51:40.369] self.loader = MigrationLoader(self.connection)
[15:51:40.369] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/loader.py", line 49, in __init__
[15:51:40.369] self.build_graph()
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/loader.py", line 212, in build_graph
[15:51:40.370] self.applied_migrations = recorder.applied_migrations()
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 76, in applied_migrations
[15:51:40.370] if self.has_table():
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 56, in has_table
[15:51:40.370] return self.Migration._meta.db_table in self.connection.introspection.table_names(self.connection.cursor())
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 48, in table_names
[15:51:40.370] return get_names(cursor)
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 43, in get_names
[15:51:40.370] return sorted(ti.name for ti in self.get_table_list(cursor)
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/djongo/introspection.py", line 47, in get_table_list
[15:51:40.370] for c in cursor.db_conn.list_collection_names()
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/pymongo/database.py", line 880, in list_collection_names
[15:51:40.370] for result in self.list_collections(session=session, **kwargs)]
[15:51:40.370] File "/usr/local/lib/python3.9/site-packages/pymongo/database.py", line 842, in list_collections
[15:51:40.371] return self.__client._retryable_read(
[15:51:40.371] File "/usr/local/lib/python3.9/site-packages/pymongo/mongo_client.py", line 1514, in _retryable_read
[15:51:40.371] server = self._select_server(
[15:51:40.371] File "/usr/local/lib/python3.9/site-packages/pymongo/mongo_client.py", line 1346, in _select_server
[15:51:40.371] server = topology.select_server(server_selector)
[15:51:40.371] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 244, in select_server
[15:51:40.371] return random.choice(self.select_servers(selector,
[15:51:40.371] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 202, in select_servers
[15:51:40.371] server_descriptions = self._select_servers_loop(
[15:51:40.371] File "/usr/local/lib/python3.9/site-packages/pymongo/topology.py", line 218, in _select_servers_loop
[15:51:40.371] raise ServerSelectionTimeoutError(
[15:51:40.372] pymongo.errors.ServerSelectionTimeoutError: connection closed,connection closed,connection closed, Timeout: 30s, Topology Description: <TopologyDescription id: 62971a6e87ecd8382d3ed4fa, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('cluster0-shard-00-00.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>, <ServerDescription ('cluster0-shard-00-01.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>, <ServerDescription ('cluster0-shard-00-02.sw6pb.mongodb.net', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('connection closed')>]>
[15:51:40.836] Error! Command "./migrate.sh" exited with 1
[15:51:40.936] Error: Command "vercel build" exited with 1

重新进入项目,在项目设置界面添加环境变量 Environment Variables:

在 Deployments 点击 Redeploy 开始部署,若没有 Error 信息即可打开域名进入初始化引导。

友链管理

这个教程将帮助你在几分钟内利用 Qexo 为博客接入友链系统。

须知

友链功能要求 Qexo >= 1.5.0 且用户浏览器必须支持文件上传。

添加友链
  1. 在 Qexo 侧边栏找到 友链 点击进入

  2. 点击右上角 新增友链 输入站点名称、链接等数据,其中链接及图片链接必须包含http协议头

  3. 点击 确定 按键保存友链数据

接入博客
  1. 在根目录打开命令行,输入命令创建页面:
1
hexo new page links
  1. 打开 source/links/index.md 修改页面配置

  2. 在页面内引入 Qexo-Friends 将其中的 ${SITE} 改为你的 Qexo 链接,例如 https://admin.mysite.com

1
2
3
4
<div id="qexo-friends"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/qexo-static@1.1.3/hexo/friends/friends.css"/>
<script src="https://cdn.jsdelivr.net/npm/qexo-static@1.1.3/hexo/friends/friends.js"></script>
<script>loadQexoFriends("qexo-friends", "${SITE}")</script>
  1. 将博客推送至你的 Github 仓库
友链申请

由 @Fgaoxing 适配的友链申请 API:

1
2
3
<div id="friends-api"></div>
<script src="https://cdn.jsdelivr.net/gh/Fgaoxing/blog-cdn@main/source/js/friends-api.js"></script>
<script>qexo_friend_api("friends-api","Qexo域名");</script>

配置

Github 配置

如果部署中遇到问题,可以访问 HPP校验助手 自检配置,若确认无误,可检查仓库内是否有已经发布的文章

Github 仓库

您 Hexo 自动化部署所在的仓库:

1
username/repo
项目分支

您 Hexo 自动化部署所在仓库的分支:

1
master
Github 密钥

Github 设置 生成的 Token 需要 Repo 下的至少读取和写入权限,不建议给出所有权限:

1
wrq_P8sYPlYA9fjMlOPEYSKA84xxxxxxxxxxxxxx

仓库路径

您 Hexo 自动化部署所在仓库的路径 若为根目录请留空:

1
path/

自定义图床配置

Qexo 提供了强大的自定义图床功能,在配置完成图床设置后即可在文章/页面编辑界面上传图片。

API 地址

图床图片上传的 API:

1
https://7bu.top/api/upload
POST 参数名

图床图片上传 API 参数中图片文件的参数名:

1
image

JSON 路径

图床 API 返回数据中图片 URL 所在的路径,若为整个返回值请留空。示例:

1
data.url
自定义请求头

POST 请求时附带的请求头,需要标准 JSON 格式,若不需要请留空。

1
{"key":"value"}
自定义 BODY

POST 请求时额外的请求主体,需要标准 JSON 格式,若不需要请留空。

1
{"key":"value"}
自定义前缀

返回 URL 所需要添加的前缀,若不需要请留空。

1
some_text_or_url

Vercel 相关配置

VERCEL_TOKEN

您的 Vercel 账户密钥 在 此处 生成:

1
xxxxxxxxxxxxxxxxxxxxxxxx

PROJECT_ID

您 Qexo 部署所在项目的 ID 位于 Project Settings -> General -> Project ID

1
prj_xxxxxxx

常见问题

什么是 API 密钥

在您完成初始化之后可在设置界面修改/创建 API 密钥,用于 Webhook 中的身份验证。若留空系统会随机生成一个 API 密钥。

Webhook 是什么

Qexo 中的 Webhook 指 /api/webhook 用于自动化操作,目前可用于自动清除缓存。

安装后出现 504 Time out

  1. 您的数据库没有正确配置或没有允许所有 IP 白名单访问,可在 MongoDB 控制台进行修改,修改完成后一定要重新部署。

  2. 删除并重建数据库,注意区域一定要选择 AWS / N. Virginia (us-east-1)。

安装/更新后出现 5xx 错误

Qexo 每个 Release 都经过 Dev 分支的测试,一般情况下不会出现较大问题,如果你遇到了500等错误,请尝试以下步骤:

  1. 检查数据库配置

  2. 清除浏览器缓存

  3. 在高级设置中点击“修复”按钮

  4. 若无法登录请使用API: yoursite.com/pub/fix?token={$APIKEY}

  5. 保留数据库配置的环境变量并重新 Fork 仓库部署

  6. 重新部署整个程序

  7. 尝试 Dev 分支

AssertionError(“xxx object … its id attribute is set to None.”)

请检查你是否曾使用过 0.01 或 0.1 版本,这两个版本有严重问题,请重新创建数据库并部署。

Github 配置校验错误

如果配置中遇到问题,可以访问 HPP校验助手 自检配置,若确认无误,可检查仓库内是否有已经发布的文章。

注意:Github 仓库一定为您 Hexo 自动化部署 所在的仓库。

Vercel 用量问题

Vercel 的无服务器函数用量对于 Qexo 来说是充裕的,但这依然抵挡不住有心之人的攻击行为,所以要保护好自己后台地址,不过好在 Vercel 不会随意扣费,所以在资源用完之后并不会产生费用,若依然不放心可以考虑部署在自己的服务器上。

在线更新失败了

检查高级设置中的 VERCEL_TOKENPROJECT_ID 是否正确为 Qexo 的部署项目。

其他问题

如果还有问题,可以发 issue 或加入 HexoPlusPlus交流群 询问。