> [!NOTE] > 此文章仅适用于单服务器部署前后端 > > 并且前后端均使用同一子域名 > > 如果您使用 SaaS / 服务器面板 请参阅 [其他社区教程](https://mx-space.js.org/docs/core/community)Mix Space 是一个小型个人空间站点程序。不同于传统的博客程序,采用前后端分离设计, 适合那些喜欢写不同风格或类型的写作爱好者。
准备工作#
-
服务器 * 1
- Kernel > 4.18
- RAM > 1G (推荐 2G 以上,Next 编译很吃内存 太低可能 OOM)
- SWAP > 2G (可选 内存够大可忽略)
-
域名 * 1
-
SSL 证书 * 1
Note
对于 CDN 用户:
如果您在源服务器端使用自签证书 那么您需要将自签证书的 CA 加入到系统 CA 库或手动信任证书 否则可能会导致前端请求后端失败
后端#
-
拉取 Docker Compose 配置文件模板
cd && mkdir -p mx-space/core && cd $_ curl https://https://raw.githubusercontent.com/mx-space/core/refs/heads/master/docker-compose.yml -o docker-compose.yml
-
编辑刚刚下载下来的
docker-compose.yml
services: app: container_name: mx-server image: innei/mx-server:latest environment: - TZ=Asia/Shanghai - NODE_ENV=production - DB_HOST=mongo - REDIS_HOST=redis - ALLOWED_ORIGINS=localhost # 这里加上你的域名 用逗号分隔 - JWT_SECRET=YOUR_SUPER_SECURED_JWT_SECRET_STRING # 这里用 openssl 生成一个 - ENCRYPT_ENABLE=false # 是否启用加密 一般不动它 - ENCRYPT_KEY=069d51c5e6f09e1d806e4dedb1c7bc620c29a67c91a984547911be8a846cf6cb # 示例值 volumes: - ./data/mx-space:/root/.mx-space ports: - '2333:2333' depends_on: - mongo - redis networks: - mx-space restart: unless-stopped healthcheck: test: ['CMD', 'curl', '-f', 'http://127.0.0.1:2333/api/v2/ping'] interval: 1m30s timeout: 30s retries: 5 start_period: 30s mongo: container_name: mongo image: mongo volumes: - ./data/db:/data/db networks: - mx-space restart: unless-stopped redis: image: redis:alpine container_name: redis volumes: - ./data/redis:/data healthcheck: test: ['CMD-SHELL', 'redis-cli ping | grep PONG'] start_period: 20s interval: 30s retries: 5 timeout: 3s networks: - mx-space restart: unless-stopped networks: mx-space: driver: bridge
Important
若开启加密,则需注意密钥长度必须为 64 位且只有小写字母和数字,不然会在初始化时报错。注意这是不可逆的,务必保存自己的秘钥。所以并不是非常推荐使用,除非你真的需要加密 API Key.
密钥可以通过 openssl rand -hex 32
命令生成。请务必牢记。
参见 Key 加密与安全性
-
启动后端
docker compose up -d
-
配置反向代理
以下是一个示例文件
server { listen 80; listen 443 ssl; listen 443 quic; # 仅 nginx 1.25.0 以上版本可用 其他版本请删去这行 server_name YOUR_SERVER_NAME; #改成你的域名 ssl_certificate path/to/certificate; # SSL 证书路径 ssl_certificate_key path/to/key; # SSL 证书密钥路径 # Reverse proxy # This is a example for nginx configure if you host mx-space manually location / { proxy_pass http://127.0.0.1:2323; proxy_set_header Host $host; proxy_intercept_errors on; } location ^~ /api/v2 { proxy_pass http://127.0.0.1:2333/api/v2; proxy_set_header Host $host; proxy_redirect off; proxy_buffering off; } location /proxy/qaqdmin { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/proxy/qaqdmin; } location ^~ /proxy/ { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/proxy/; } location ^~ /render/ { proxy_set_header Host $host; proxy_pass http://127.0.0.1:2333/render/; } location ^~ /socket.io { proxy_pass http://127.0.0.1:2333/socket.io; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_buffering off; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; }
}
在 `nginx.conf` 中引用此文件
```nginx filename="/etc/nginx/nginx.conf"
http {
...
include /etc/nginx/sites-enabled/*;
...
}
- 初始化后端
- 设置好域名解析
- 打开
https://<YOUR_DOMAIN>/proxy/qaqdmin
- 根据提示完成初始化
- 前往 设定 > 网站设置 按如下格式设置
项目 | 值 |
---|---|
前端地址 | https://<YOUR_DOMAIN> |
管理后台地址 | https://<YOUR_DOMAIN>/proxy/qaqdmin |
API 地址 | https://<YOUR_DOMAIN>/api/v2 |
Gateway 地址 | https://<YOUR_DOMAIN> |
至此后端大功告成
前端 (Shiro)#
- 初始化环境
cd ~/mx-space
git clone https://github.com/Innei/Shiro.git
cd Shiro
git submodule init && git submodule update
pnpm i
- 设置主题配置
进入 Mix Space 后台,进入「配置与云函数」页面,点击右上角的新增按钮,在编辑页面中,填入以下设置:
Important
请注意,这份配置你需要自行修改成符合你的需求的配置。直接使用下面的配置可能会导致你的博客无法按照你的预期运行。
下面的配置可能不全,更多配置项的信息请移步 配置项 了解
此外,配置也可写成 yaml 格式,此时数据类型应选择 YAML
。
关于 ” 我的动态 “ 功能 详见官方文档
- 名称:`shiro`
- 引用:`theme`
- 数据类型:`JSON`
- 数据:(点击下方的按钮复制)
```json
{
"footer": {
"otherInfo": {
"date": "2020-{{now}}",
"icp": {
"text": "萌 ICP 备 20236136 号",
"link": "https://icp.gov.moe/?keyword=20236136"
}
},
"linkSections": [
{
"name": "关于",
"links": [
{
"name": "关于本站",
"href": "/about-site"
},
{
"name": "关于我",
"href": "/about"
},
{
"name": "关于此项目",
"href": "https://github.com/innei/Shiro",
"external": true
}
]
},
{
"name": "更多",
"links": [
{
"name": "时间线",
"href": "/timeline"
},
{
"name": "友链",
"href": "/friends"
},
{
"name": "监控",
"href": "https://status.innei.in/status/main",
"external": true
}
]
},
{
"name": "联系",
"links": [
{
"name": "写留言",
"href": "/message"
},
{
"name": "发邮件",
"href": "mailto:[email protected]",
"external": true
},
{
"name": "GitHub",
"href": "https://github.com/innei",
"external": true
}
]
}
]
},
"config": {
"color": {
"light": [
"#33A6B8",
"#FF6666",
"#26A69A",
"#fb7287",
"#69a6cc",
"#F11A7B",
"#78C1F3",
"#FF6666",
"#7ACDF6"
],
"dark": [
"#F596AA",
"#A0A7D4",
"#ff7b7b",
"#99D8CF",
"#838BC6",
"#FFE5AD",
"#9BE8D8",
"#A1CCD1",
"#EAAEBA"
]
},
"bg": [
"https://github.com/Innei/static/blob/master/images/F0q8mwwaIAEtird.jpeg?raw=true",
"https://github.com/Innei/static/blob/master/images/IMG_2111.jpeg.webp.jpg?raw=true"
],
"custom": {
"css": [],
"styles": [],
"js": [],
"scripts": []
},
"site": {
"favicon": "/innei.svg",
"faviconDark": "/innei-dark.svg"
},
"hero": {
"title": {
"template": [
{
"type": "h1",
"text": "Hi, I'm ",
"class": "font-light text-4xl"
},
{
"type": "h1",
"text": "Innei",
"class": "font-medium mx-2 text-4xl"
},
{
"type": "h1",
"text": "👋。",
"class": "font-light text-4xl"
},
{
"type": "br"
},
{
"type": "h1",
"text": "A NodeJS Full Stack ",
"class": "font-light text-4xl"
},
{
"type": "code",
"text": "<Developer />",
"class": "font-medium mx-2 text-3xl rounded p-1 bg-gray-200 dark:bg-gray-800/0 hover:dark:bg-gray-800/100 bg-opacity-0 hover:bg-opacity-100 transition-background duration-200"
},
{
"type": "span",
"class": "inline-block w-[1px] h-8 -bottom-2 relative bg-gray-800/80 dark:bg-gray-200/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 group-hover:animation-blink"
}
]
},
"description": "An independent developer coding with love."
},
"module": {
"activity": {
"enable": true,
"endpoint": "/fn/ps/update"
},
"donate": {
"enable": true,
"link": "https://afdian.net/@Innei",
"qrcode": [
"https://cdn.jsdelivr.net/gh/Innei/img-bed@master/20191211132347.png",
"https://cdn.innei.ren/bed/2023/0424213144.png"
]
},
"bilibili": {
"liveId": 1434499
}
}
}
}
```
保存修改
4. 修改环境变量
nano .env
示例:
NEXT_PUBLIC_API_URL=http://localhost:2333
NEXT_PUBLIC_GATEWAY_URL=http://localhost:2333
NEXT_PUBLIC_API_URL=https://<YOUR_DOMAIN>/api/v2
NEXT_PUBLIC_GATEWAY_URL=https://<YOUR_DOMAIN>
# 以下可选
TMDB_API_KEY=
GH_TOKEN=
- 编译
pnpm run build
-
启动
pnpm prod:pm2
至此前后端全部完成
一些小技巧#
加速前后端之间的连接#
背景#
对于 CDN 用户而言 默认情况下前端会经过 CDN 来请求后端接口 这增加了延迟 并且提高了编写 WAF 规则的难度 带来了不便
解决#
由于我们的前后端位于同一台服务器 因此我们可以通过在服务器端将域名解析为 localhost
或 127.0.0.1
的方式 使请求不经过 CDN 直达后端来达到加速的目的
echo -e "127.0.0.1 <YOUR_DOMAIN>" | sudo tee -a /etc/hosts
Important
请仔细检查你的 hosts 文件是否由 cloud-init
管辖
如果是 你需要更改 cloud-init
的模板而非直接更改 /etc/hosts
关于 favicon#
背景#
虽然可以通过配置文件来修改 favicon
但是在某些情况下 (比如 Cloudflare 质询) 获取到的 favicon 是 Shiro 默认 favicon
所以我有点难受
解决#
我决定使用 git alias
git submodule
和 git hooks
来解决
首先 新建一个 Git 仓库用来存 favicon
示例
删除原来的 public
文件夹 并添加子模块
rm -rf {SHIRO_PATH}/public && git rm -rfq public
git submodule --quiet add --force {YOUR_REPO} public
git submodule --quiet update --init --remote
git submodule --quiet sync
设置 git alias
git config --global alias.pull-clean '!git fetch && test $(git rev-list --count HEAD..@{upstream}) -gt 0 && git reset --hard && git clean -fd && git pull'
添加 post-merge
Git Hook
#!/bin/sh
. git-sh-setup
echo "Replacing public folder"
rm -rf public && git rm -rfq public
git submodule --quiet add --force https://github.com/nyaruta/Shiro-Assets.git public
echo "Syncing submodules"
git submodule --quiet update --init --remote
git submodule --quiet sync
添加执行权限
chmod +x .git/hooks/post-merge
这样每次拉取仓库时 把 git pull
替换为 git pull-clean
即可在不产生冲突的情况下执行我们对 favicon 的更改
Important
添加完 Git Hooks 之后执行 pnpm install
需要加 --ignore-scripts
参数 不然会自动执行 prepare
脚本覆盖掉我们的 hook
好了 暂时也没想到什么新的小技巧 就到此为止吧
此文由 Mix Space 同步更新至 xLog
原始链接为 https://lar.moe/posts/deploy/mx-space