前言#
之前的博客使用的是 Typecho,在 2024 年偶然发现 Mix-Space 这个前后端分离的个人空间,十分喜爱,于是对博客进行了迁移。目前已经成功部署完毕了,希望能帮助到同样想要部署的大家。|| 同时也是给自己留个备忘 qwq||
本篇文章教程为:后端采用 Docker 部署,前端则是利用 Github Action 构建 Shiro 然后推送到服务器上 (当然你也可以选择更加方便的 Docker ,不过闭源版本并未提供)
Mix-Space 与 Shiro 主题相关链接#
Mix Space 是一个小型个人空间站点程序。不同于传统的博客程序,采用前后端分离设计, 适合那些喜欢写不同风格或类型的写作爱好者。
Mix Space - An Alternative Personal Space - Mix Space 文档
Shiro 是 Innei 制作的为 Mix Space 服务的个人站点前端。
一个极简主义的个人网站,纸的纯净和雪的清新。
1 Panel 安装#
参考官网的 在线安装 - 1Panel 文档 步骤即可,这里我使用的是 Ubuntu 系统,所以执行以下命令即可
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
根据提示一步一步往下即可,期间会让你输入一些账号,密码,1 Panel 的默认端口等,直接回车使用默认也可以,都可以在面板内更改。
最终输出一份 log ,上面有你的面板地址,用户名以及密码,使用里面的地址与账户即可登录面板啦。
后端 Core 部署#
检查并安装 Docker#
我是用的是 Docker 的方式部署的后端,也是较为推荐的方式。
因为 1 Panel 面板会默认安装 docker 所以就不用检查是否已经安装了。
拉取并修改配置文件#
你可以使用命令行拉取文件,也可以使用 1 panel 面板进行拉取,存放路径可以自行修改
#回到根目录并创建mx-space/core的文件夹,并跳转至core文件夹下
cd && mkdir -p mx-space/core && cd $_
# 拉取 docker-compose.yml 文件
wget https://fastly.jsdelivr.net/gh/mx-space/core@master/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=blog.lolita.best,localhost:*,127.0.0.1:*
#填写你自己生成的JWT密钥,16≤密钥长度≤32 个字符(务必保存好自己的密钥不要泄露)
- JWT_SECRET=xxxxx
#是否开启加密的相关配置,这边默认为false与空即可,如需加密可以参考官方配置文档
- ENCRYPT_ENABLE=false
- ENCRYPT_KEY=
volumes:
- ./data/mx-space:/root/.mx-space
ports:
#-'2333:2333' 修改了docker的端口映射,不开放端口
- '127.0.0.1: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
启动 Core#
配置完毕后直接在当前目录使用以下指令启动 Core
#后续如果需要更新后端或者yaml配置修改更新容器也是一样的指令
docker compose pull && docker compose up -d
启动成功后你可以在 1 Panel 的容器内看到 mx-server,redis,mongo 三个容器的运行情况。
至此你已经成功布置好了后端
反向代理设置#
后端部署完毕后还需要设置反向代理才可以访问,这里一并把 ssl 证书也一起弄了
申请网站证书#
在 1 Panel 面板内的网站 / 证书内可以直接申请证书
我们需要先创建一个 Acme 账户,邮箱填写完毕后其余默认即可
其次是申请证书时的验证方式,这里我使用的是 DNS 账号的方式验证,需要先创建 DNS 账户,面板内支持了很多 DNS 的服务商,选择自己的即可,这里我使用的是 CloudFLare 。
CloudFlare 的 API 令牌在 “管理账户 / 账户 API 令牌处申请即可”,直接使用 “编辑区域 DNS” 的模板即可
上述步骤做完后准备工作就做好了,点击申请证书,填写你自己的网站域名即可。
创建网站并设置反向代理#
初次使用 1 Panel 会让你去应用商店内安装 OpenResty,根据提示直接安装即可。
选择创建静态网站,填写你的域名,其他默认即可
点击网页配置,跳转到 HTTPS,选择之前申请好的证书,SSL 就配置好啦。
接着来到配置文件,复制下方的内容并添加在最后保存并重载即可(记得注释的地方修改成自己的)
这里使用的是单域名的方式,如需前后端双域名请查看官方配置文档
# WebSocket 地址
location /socket.io {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2333/socket.io;
}
# API 地址
location /api/v2 {
proxy_pass http://127.0.0.1:2333/api/v2;
}
# 简读 render 地址
location /render {
proxy_pass http://127.0.0.1:2333/render;
}
# Shiro 地址
location / {
proxy_pass http://127.0.0.1:2323;
}
# 后台地址
location /proxy {
proxy_pass http://127.0.0.1:2333/proxy;
}
location /qaqdmin { #如果需要修改自己的后台地址可以改这里
proxy_pass http://127.0.0.1:2333/proxy/qaqdmin;
}
# RSS 地址
location ~* \/(feed|sitemap|atom.xml) {
proxy_pass http://127.0.0.1:2333/$1;
}
设置完毕后,这时候你就可以通过 https://你的域名/qaqdmin
访问后台管理地址了,根据指示填写对应内容即可
在设定的网站设置内填写对应的地址
前端部署#
我是用的是 Shiro 的闭源版本,并通过 Github Action 的方式自动部署,开源版本的应该也可以按照以下步骤进行
Github Fork Shiroi-deploy-action 仓库并修改 workflows 文件#
仓库地址:GitHub - innei-dev/shiroi-deploy-action
Fork 后修改 workflows 下的 deploy.yml
文件。(需要修改的地方加了中文注释)
name: Build and Deploy
on:
workflow_dispatch: #增加了工作流的手动运行
push:
branches:
- main
# schedule:
# - cron: '0 3 * * *'
repository_dispatch:
types: [trigger-workflow]
permissions: write-all
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
PNPM_VERSION: 9.x.x
HASH_FILE: build_hash
jobs:
prepare:
name: Prepare
runs-on: ubuntu-latest
if: ${{ github.event.head_commit.message != 'Update hash file' }}
outputs:
hash_content: ${{ steps.read_hash.outputs.hash_content }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Read HASH_FILE content
id: read_hash
run: |
content=$(cat ${{ env.HASH_FILE }}) || true
echo "hash_content=$content" >> "$GITHUB_OUTPUT"
check:
name: Check Should Rebuild
runs-on: ubuntu-latest
needs: prepare
outputs:
canceled: ${{ steps.use_content.outputs.canceled }}
steps:
- uses: actions/checkout@v4
with:
repository: innei-dev/shiroi #填写你需要访问的仓库,如开源版为innei/shiro
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
fetch-depth: 0
lfs: true
- name: Use content from prev job and compare
id: use_content
env:
FILE_HASH: ${{ needs.prepare.outputs.hash_content }}
run: |
file_hash=$FILE_HASH
current_hash=$(git rev-parse --short HEAD)
echo "File Hash: $file_hash"
echo "Current Git Hash: $current_hash"
if [ "$file_hash" == "$current_hash" ]; then
echo "Hashes match. Stopping workflow."
echo "canceled=true" >> $GITHUB_OUTPUT
else
echo "Hashes do not match. Continuing workflow."
fi
build:
name: Build artifact
runs-on: ubuntu-latest
needs: check
if: ${{needs.check.outputs.canceled != 'true'}}
strategy:
matrix:
node-version: [20.x]
outputs:
sha_short: ${{ steps.store.outputs.sha_short }}
branch: ${{ steps.store.outputs.branch }}
steps:
- uses: actions/checkout@v4
with:
repository: innei-dev/shiroi #填写你需要访问的仓库,如开源版为innei/shiro
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
fetch-depth: 0
lfs: true
- name: Checkout LFS objects
run: git lfs checkout
- uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- uses: jongwooo/next-cache@v1
- name: Install dependencies
run: pnpm install
- name: Build project
run: |
sh ./ci-release-build.sh
# - uses: actions/upload-artifact@v4
# with:
# name: dist
# path: assets/release.zip
# retention-days: 7
- name: Cache Build Artifacts
id: cache-primes
uses: actions/cache/save@v4
with:
path: assets
key: ${{ github.run_number }}-release
- name: Store artifact commit version
shell: bash
id: store
run: |
sha_short=$(git rev-parse --short HEAD)
branch_name=$(git rev-parse --abbrev-ref HEAD)
echo "sha_short=$sha_short" >> "$GITHUB_OUTPUT"
echo "branch=$branch_name" >> "$GITHUB_OUTPUT"
deploy:
name: Deploy artifact
runs-on: ubuntu-latest
needs: build
steps:
# - name: Download artifact
# uses: actions/download-artifact@v4
# with:
# name: dist
- name: Restore cached Build Artifacts
id: cache-primes-restore
uses: actions/cache/restore@v4
with:
path: |
assets
key: ${{ github.run_number }}-release
- name: Move assets to root
run: mv assets/release.zip release.zip
- name: copy file via ssh password
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
password: ${{ secrets.PASSWORD }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
source: 'release.zip'
target: '/tmp/shiro'
- name: Exec deploy script with SSH
uses: appleboy/ssh-action@master
with:
command_timeout: 5m
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
password: ${{ secrets.PASSWORD }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
#script_stop: true 移除了改行指令,因为该指令已经停止使用了会提示但不影响,不改也行
script: |
set -e
export NVM_DIR=~/.nvm #定义.nvm的安装目录,否则终端运行时明明安装了但找不到命令
source ~/.nvm/nvm.sh #识别nvm命令,否则可能会报command not found
source $HOME/.bashrc
basedir=$HOME/mx-space/shiro #填写你自己希望的路径,因为我的后端文件在mx-space下,所以我希望我的shiro也在mx-space文件夹下,方便管理
workdir=$basedir/${{ github.run_number }}
mkdir -p $workdir
mkdir -p $basedir/.cache
mv /tmp/shiro/release.zip $workdir/release.zip
rm -r /tmp/shiro
cd $workdir
unzip -qq -o $workdir/release.zip
rm -rf $workdir/standalone/.env
ln -s $HOME/mx-space/shiro/.env $workdir/standalone/.env #你的.env文件路径,我存放在mx-space/shiro文件夹内,方便管理
export NEXT_SHARP_PATH=$(npm root -g)/sharp
# copy workdir ecosystem.config.js to basedir if not exists
if [ ! -f $basedir/ecosystem.config.js ]; then
cp $workdir/standalone/ecosystem.config.js $basedir/ecosystem.config.js
fi
# https://github.com/Unitech/pm2/issues/3054
# symlink workdir node entry file to basedir
ln -sf $workdir/standalone/server.js $basedir/server.js
mkdir -p $workdir/standalone/.next
rm -rf $workdir/standalone/.next/cache
ln -sf $basedir/.cache $workdir/standalone/.next/cache
cd $basedir
pm2 reload ecosystem.config.js --update-env
rm $workdir/release.zip
pm2 save
echo "Deployed successfully"
- name: After deploy script
run: |
hash=${{ needs.build.outputs.sha_short }}
# curl -X "POST" "https://mx.innei.in/api/v2/fn/shiro/new-version-hook" -H 'Content-Type: application/json' -d "{\"hash\": \"$hash\", \"key\": \"\"}"
${{ secrets.AFTER_DEPLOY_SCRIPT }}
store:
name: Store artifact commit version
runs-on: ubuntu-latest
needs: [deploy, build]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
# Get the commit version from the build job
- name: Use outputs from build
env:
SHA_SHORT: ${{ needs.build.outputs.sha_short }}
BRANCH: ${{ needs.build.outputs.branch }}
run: |
echo "SHA Short from build: $SHA_SHORT"
echo "Branch from build: $BRANCH"
- name: Write hash to file
env:
SHA_SHORT: ${{ needs.build.outputs.sha_short }}
run: echo $SHA_SHORT > ${{ env.HASH_FILE }}
- name: Commit files
run: |
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add ${{ env.HASH_FILE }}
git commit -a -m "Update hash file"
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.ref }}
设置工作流的 Secrets#
HOST
服务器地址USER
服务器用户名PASSWORD
服务器密码PORT
服务器 SSH 端口KEY
服务器 SSH Key(可选,密码 key 二选一)GH_PAT
可访问之前 repository 内填写仓库的 Github Token
配置.env 文件#
返回到 1 Panel,在你的工作流内指定好的地方创建 .env
文件并将以下内容复制修改成自己的内容
如上方是 root/mx-space/shiro
# 后端API地址
NEXT_PUBLIC_API_URL=https://blog.lolita.best/api/v2
# 后端网关地址
NEXT_PUBLIC_GATEWAY_URL=https://blog.lolita.best
# TMDB信息获取
TMDB_API_KEY=填写你的TMDB-API
# GitHub token,用来获取前端版本哈希
GH_TOKEN=填你自己的token
服务器安装必要环境#
自部署 Shiro 主题需要安装 Node.js
, npm
, pnpm
, pm2
, sharp
可以在 1 Panel 的终端内输入以下指令进行安装
现在 Node.js 官网 也有安装方式,根据提示来即可
# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# In lieu of restarting the shell
\. "$HOME/. Nvm/nvm. Sh"
# Download and install Node. Js:
Nvm install 23
# Verify the Node. Js version:
Node -v # Should print "v 23.8.0".
Nvm current # Should print "v 23.8.0".
# Download and install pnpm:
Corepack enable pnpm
# Verify pnpm version:
Pnpm -v
#下方为额外补充内容
npm i -g pm2 #安装pm2
npm i -g sharp #安装sharp
由于 workflow 中使用了 unzip 进行解压操作,需要确认服务器是否安装 unzip,否则会报错
#更新软件包并安装unzip
sudo apt update && sudo apt install -y unzip
#使用以下命令验证是否已安装
unzip -v
启动 workflow#
在 Github 内手动 Run workflow 并等待工作流跑完,不报错就成功了
管理后台配置 Shiro 主题云函数#
Warning
提示
云函数的配置修改更新并非立刻刷新,需要时间请耐心等待
Shiro 主题还需要配合云函数才可以使用,进入管理后台 / 附加功能 / 配置与云函数内,先导入相关文件
再点击右上角的新增按钮,在编辑页面中,填入以下设置
- 名称:shiro
- 引用:theme
- 数据类型:JSON
- 数据:右侧复制以下示例,更改为自己的信息
数据内容参考:配置 - Mix Space 文档
{
"footer": {
"otherInfo": {
"date": "2019-{{now}}",
"icp": {
"text": "萌ICP备20248012号",
"link": "https://icp.gov.moe/?keyword=20248012"
}
},
"linkSections": [
{
"name": "关于",
"links": [
{
"name": "关于本站",
"href": "/about-site"
},
{
"name": "关于我",
"href": "/about",
"external": true
}
]
},
{
"name": "更多",
"links": [
{
"name": "时间线",
"href": "/timeline"
},
{
"name": "友链",
"href": "/friends",
"external": true
}
]
},
{
"name": "联系",
"links": [
{
"name": "写留言",
"href": "/message",
"external": true
},
{
"name": "发邮件",
"href": "mailto:[email protected]",
"external": true
},
{
"name": "GitHub",
"href": "https://github.com/Sumikanya",
"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": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36",
"faviconDark": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36"
},
"hero": {
"title": {
"template": [
{
"type": "h1",
"text": "Hi, I'm ",
"class": "font-light text-4xl"
},
{
"type": "h1",
"text": "星野纯夏",
"class": "font-medium mx-2 text-4xl"
},
{
"type": "h1",
"text": "💕。",
"class": "font-light text-4xl"
},
{
"type": "br"
},
{
"type": "h1",
"text": "一个喜欢科技数码的Lo娘 ",
"class": "font-light text-4xl"
},
{
"type": "code",
"text": "🦋<INFP>🦋",
"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": "希望在这个世界上可以留下属于我的痕迹",
"Hitokoto": {
"random": true,
"custom": "自定义一言"
}
},
"module": {
"activity": {
"enable": true,
"endpoint": "/fn/ps/update"
},
"donate": {
"enable": false,
"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": 19781
}
}
}
}
设置 PM2
服务在重启之后还原进程#
pm2 startup
pm2 save
如何手动手动启动 Shiro? 可以进入 shiro 的前端路径并运行以下代码
默认 github 的工作流配置内已经启动过了
pm2 start ecosystem.config.js
可以使用以下指令查看正在运行的进程
pm2 list
部署完毕#
前后端都部署成功后你就可以直接访问你的网页查看到当前效果了
参考链接#
十分感谢官方以及其他搭建的相关教程
Mix-Space 部署最新前端 Shiro - 华岁云小屋
尝试 Github Action 构建与部署 Shiroi - 华岁云小屋
Mix Space - An Alternative Personal Space - Mix Space 文档
mx-space + Shiro:如纸一般纯净的新博客・Arthals' ink
Shiroi 部署的大致流程和补充 - Ethan
从 WordPress 迁移数据到 Mix Space - FoskyM's Blog
此文由 Mix Space 同步更新至 xLog
原始链接为 https://blog.lolita.best/posts/blog/MxspaceDeployShiro