Qexo博客后端搭建

引言

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

特色功能

  • 自定义图床上传图片

  • 在线配置编辑

  • 在线页面管理

  • 开放 API

  • 自动检查更新

  • 实验性的在线更新

  • 自动填充 date 模板

  • 基于时间戳的 abbrlink

快速上手

Vercel 部署

申请 MongoDB

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

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

一键部署

点击开始部署,部署到 Vercel

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

[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. 在根目录打开命令行,输入命令创建页面:
hexo new page links
  1. 打开 source/links/index.md 修改页面配置

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

<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:

<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 自动化部署所在的仓库:

username/repo
项目分支

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

master
Github 密钥

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

wrq_P8sYPlYA9fjMlOPEYSKA84xxxxxxxxxxxxxx

仓库路径

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

path/

自定义图床配置

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

API 地址

图床图片上传的 API:

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

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

image

JSON 路径

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

data.url
自定义请求头

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

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

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

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

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

some_text_or_url

Vercel 相关配置

VERCEL_TOKEN

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

xxxxxxxxxxxxxxxxxxxxxxxx

PROJECT_ID

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

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交流群 询问。