SumikaNya

SumikaNya

使用 1Panel 部署 Mix-Space 和前端 Shiro 主题

前言#

之前的博客使用的是 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 服务的个人站点前端。
一个极简主义的个人网站,纸的纯净和雪的清新。

GitHub - Innei/Shiro: 📜 A minimalist personal website embodying the purity of paper and freshness of snow.

Shiro - 全新风格,再次起航 - 静かな森

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

使用 1Panel 面板拉取

修改 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 三个容器的运行情况。
至此你已经成功布置好了后端
image

反向代理设置#

后端部署完毕后还需要设置反向代理才可以访问,这里一并把 ssl 证书也一起弄了

申请网站证书#

在 1 Panel 面板内的网站 / 证书内可以直接申请证书
我们需要先创建一个 Acme 账户,邮箱填写完毕后其余默认即可
image
其次是申请证书时的验证方式,这里我使用的是 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 访问后台管理地址了,根据指示填写对应内容即可
在设定的网站设置内填写对应的地址
image

前端部署#

我是用的是 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
    image

配置.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

image

服务器安装必要环境#

自部署 Shiro 主题需要安装 Node.js, npm, pnpm, pm2, sharp
可以在 1 Panel 的终端内输入以下指令进行安装
现在 Node.js 官网 也有安装方式,根据提示来即可
image

# 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 并等待工作流跑完,不报错就成功了
image

管理后台配置 Shiro 主题云函数#

Warning

提示
云函数的配置修改更新并非立刻刷新,需要时间请耐心等待

Shiro 主题还需要配合云函数才可以使用,进入管理后台 / 附加功能 / 配置与云函数内,先导入相关文件
image

再点击右上角的新增按钮,在编辑页面中,填入以下设置

  • 名称: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
      }
    }
  }
}

image

设置 PM2 服务在重启之后还原进程#

pm2 startup
pm2 save

如何手动手动启动 Shiro? 可以进入 shiro 的前端路径并运行以下代码
默认 github 的工作流配置内已经启动过了

pm2 start ecosystem.config.js

可以使用以下指令查看正在运行的进程

pm2 list

image

部署完毕#

前后端都部署成功后你就可以直接访问你的网页查看到当前效果了
image

参考链接#

十分感谢官方以及其他搭建的相关教程

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


加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。