SumikaNya

SumikaNya

Deploy Mix-Space and the front-end Shiro theme using 1Panel

Preface#

Previously, I used Typecho for my blog, and in 2024, I stumbled upon Mix-Space, a personal space with a decoupled front and back end, which I really loved. Therefore, I migrated my blog. It has now been successfully deployed, and I hope it can help others who want to deploy as well. || It's also a reminder for myself qwq ||

This article's tutorial is: the back end is deployed using Docker, while the front end uses Github Action to build Shiro and then push it to the server (of course, you can also choose the more convenient Docker, but the closed-source version is not provided).

Mix Space is a small personal space site program. Unlike traditional blog programs, it adopts a decoupled design, suitable for those who enjoy writing in different styles or types.

Mix Space - An Alternative Personal Space - Mix Space Documentation

Shiro is a personal site front end made by Innei for Mix Space.
A minimalist personal website, embodying the purity of paper and the freshness of snow.

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

Shiro - A New Style, Setting Sail Again - 静かな森

1 Panel Installation#

Refer to the official website's Online Installation - 1Panel Documentation steps. Here, I am using Ubuntu, so just execute the following command:

curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh

Follow the prompts step by step; during this process, you will be asked to enter some account information, passwords, and the default port for 1 Panel, etc. You can just press Enter to use the defaults, which can all be changed in the panel.
Finally, a log will be output, which contains your panel address, username, and password. You can log in to the panel using the address and account provided.

Back-end Core Deployment#

Check and Install Docker#

I deployed the back end using Docker, which is also a recommended method.
Since the 1 Panel panel will install Docker by default, there is no need to check if it is already installed.

Pull and Modify Configuration File#

You can use the command line to pull the file or use the 1 Panel panel to do so; you can modify the storage path as needed.

# Go back to the root directory, create the mx-space/core folder, and switch to the core folder
cd && mkdir -p mx-space/core && cd $_ 
# Pull the docker-compose.yml file
wget https://fastly.jsdelivr.net/gh/mx-space/core@master/docker-compose.yml

Using 1Panel to pull

Modify some content in the docker-compose.yml file.

services:
  app:
    container_name: mx-server
    image: innei/mx-server:latest
    environment:
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - DB_HOST=mongo
      - REDIS_HOST=redis
      # Fill in your blog's domain name, separate multiple with commas, you can also add your local address here to allow them to access themselves
      - ALLOWED_ORIGINS=blog.lolita.best,localhost:*,127.0.0.1:* 
      # Fill in your generated JWT secret key, 16≤key length≤32 characters (be sure to keep your key safe and do not disclose it)
      - JWT_SECRET=xxxxx 
      # Whether to enable encryption-related configurations, the default here is false and empty, if encryption is needed, refer to the official configuration documentation
      - ENCRYPT_ENABLE=false
      - ENCRYPT_KEY=
    volumes:
      - ./data/mx-space:/root/.mx-space
    ports:
      #-'2333:2333' Modified the docker port mapping, not exposing the port
      - '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

Start Core#

After configuration is complete, directly use the following command in the current directory to start Core.

# The same command is used for updating the back end or modifying the yaml configuration and updating the container
docker compose pull && docker compose up -d

After a successful start, you can see the running status of the mx-server, redis, and mongo containers in the 1 Panel.
At this point, you have successfully set up the back end.
image

Reverse Proxy Settings#

After the back end is deployed, you also need to set up a reverse proxy to access it. Here, I will also set up the SSL certificate.

Apply for Website Certificate#

You can directly apply for a certificate in the website/certificate section of the 1 Panel panel.
We first need to create an Acme account; after filling in the email, the rest can be left as default.
image
Next is the verification method for applying for the certificate; I used the DNS account method for verification, which requires creating a DNS account. The panel supports many DNS service providers, so choose your own. I used CloudFlare.

The CloudFlare API token can be applied for in "Manage Account/Account API Tokens," and you can directly use the "Edit Zone DNS" template.
After completing the above steps, the preparation work is done. Click to apply for the certificate and fill in your website domain name.
Certificate after application

Create Website and Set Up Reverse Proxy#

When using 1 Panel for the first time, it will prompt you to install OpenResty from the app store; just install it as prompted.
Choose to create a static website, fill in your domain name, and leave the rest as default.
Click on the web configuration, switch to HTTPS, and select the previously applied certificate, and the SSL is configured.
Next, go to the configuration file, copy the content below and add it at the end, save and reload (remember to modify the commented areas to your own).

Here, a single domain method is used. If you need a dual domain for front and back ends, please refer to the official configuration documentation.

    # WebSocket address
    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 address
    location /api/v2 {
        proxy_pass http://127.0.0.1:2333/api/v2; 
    }
    # Simple read render address
    location /render {
        proxy_pass http://127.0.0.1:2333/render; 
    }
    # Shiro address
    location / {
        proxy_pass http://127.0.0.1:2323; 
    }
    # Background address
    location /proxy {
        proxy_pass http://127.0.0.1:2333/proxy; 
    }
    location /qaqdmin { # If you need to modify your background address, you can change it here
        proxy_pass http://127.0.0.1:2333/proxy/qaqdmin; 
    }

After setting up, you can access the background management address via https://your-domain/qaqdmin, filling in the corresponding content as instructed.
image

Front-end Deployment#

I used the closed-source version of Shiro and automatically deployed it through Github Action. The open-source version should also be able to follow the steps below.

Github Fork Shiroi-deploy-action Repository and Modify Workflows File#

Repository address: GitHub - innei-dev/shiroi-deploy-action
After forking, modify the deploy.yml file under workflows (the parts that need modification are marked with Chinese comments).

name: Build and Deploy

on:
  workflow_dispatch: # Added manual execution of the workflow
  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 # Fill in the repository you need to access, for the open-source version it is 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 # Fill in the repository you need to access, for the open-source version it is 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
      - 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 Removed the line command because it is no longer in use, it will prompt but does not affect, you can leave it unchanged
          script: |
            set -e
            export NVM_DIR=~/.nvm # Define the installation directory of .nvm, otherwise, the terminal will run but cannot find the command
            source ~/.nvm/nvm.sh # Recognize nvm command, otherwise it may report command not found
            source $HOME/.bashrc 
            basedir=$HOME/mx-space/shiro # Fill in the path you want, since my back-end files are in mx-space, I hope my shiro is also in the mx-space folder for easy management
            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 # Your .env file path, I store it in the mx-space/shiro folder for easy management
            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 fail 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 }}

Set Secrets for the Workflow#

  • HOST Server address
  • USER Server username
  • PASSWORD Server password
  • PORT Server SSH port
  • KEY Server SSH Key (optional, either password or key)
  • GH_PAT GitHub Token to access the previous repository
    image

Configure .env File#

Return to 1 Panel and create a .env file in the specified location of your workflow, copying the following content and modifying it to your own.
For example, the above is root/mx-space/shiro

# Back-end API address
NEXT_PUBLIC_API_URL=https://blog.lolita.best/api/v2
# Back-end gateway address
NEXT_PUBLIC_GATEWAY_URL=https://blog.lolita.best
# TMDB information retrieval
TMDB_API_KEY=Fill in your TMDB-API
# GitHub token for retrieving front-end version hash
GH_TOKEN=Fill in your own token

image

Install Necessary Environments on the Server#

To deploy the Shiro theme, you need to install Node.js, npm, pnpm, pm2, sharp.
You can enter the following commands in the terminal of 1 Panel to install them.
Now the Node.js official website also has installation methods; just follow the prompts.
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

# Additional content
npm i -g pm2 # Install pm2
npm i -g sharp # Install sharp

Since the workflow uses unzip for decompression, you need to confirm whether the server has unzip installed; otherwise, it will report an error.

# Update packages and install unzip
sudo apt update && sudo apt install -y unzip 
# Use the following command to verify if it is installed
unzip -v

Start Workflow#

Manually run the workflow in GitHub and wait for it to complete without errors to succeed.
image

Manage Backend Configuration for Shiro Theme Cloud Functions#

Warning

Reminder
The configuration modification updates for cloud functions do not refresh immediately; please be patient.

The Shiro theme also needs to work with cloud functions. Enter the management backend / additional features / configuration and cloud functions, and first import the relevant files.
image

Then click the add button in the upper right corner, and in the editing page, fill in the following settings:

  • Name: shiro
  • Reference: theme
  • Data Type: JSON
  • Data: Copy the following example on the right and change it to your own information.
    Data content reference: Configuration - Mix Space Documentation
{
  "footer": {
    "otherInfo": {
      "date": "2019-{{now}}",
      "icp": {
        "text": "萌ICP备20248012号",
        "link": "https://icp.gov.moe/?keyword=20248012"
      }
    },
    "linkSections": [
      {
        "name": "About",
        "links": [
          {
            "name": "About this site",
            "href": "/about-site"
          },
          {
            "name": "About me",
            "href": "/about",
            "external": true
          }
        ]
      },
      {
        "name": "More",
        "links": [
          {
            "name": "Timeline",
            "href": "/timeline"
          },
          {
            "name": "Friend Links",
            "href": "/friends",
            "external": true
          }
        ]
      },
      {
        "name": "Contact",
        "links": [
          {
            "name": "Leave a message",
            "href": "/message",
            "external": true
          },
          {
            "name": "Send an email",
            "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": "A Lo girl who loves technology and digital ",
            "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": "I hope to leave my mark in this world",
      "Hitokoto": {
        "random": true,
        "custom": "Custom one sentence"
      }
    },
    "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

Set PM2 Service to Restore Processes After Restart#

pm2 startup
pm2 save

How to manually start Shiro? You can enter the front-end path of shiro and run the following code.
By default, the GitHub workflow configuration has already started it.

pm2 start ecosystem.config.js

You can use the following command to view the running processes.

pm2 list

image

Deployment Complete#

After successfully deploying both the front and back ends, you can directly access your webpage to see the current effect.
image

Special thanks to the official and other related tutorials for building

Mix-Space Deploy Latest Front-end Shiro - 华岁云小屋
Trying Github Action Build and Deploy Shiroi - 华岁云小屋
Mix Space - An Alternative Personal Space - Mix Space Documentation
mx-space + Shiro: A Blog as Pure as Paper • Arthals' ink
General Process and Supplement for Shiroi Deployment - Ethan
Migrating Data from WordPress to Mix Space - FoskyM's Blog

This article is synchronized to xLog by Mix Space.
The original link is https://blog.lolita.best/posts/blog/MxspaceDeployShiro


Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.