Mix Space 博客搭建指南

Reno 于 2025-02-21 发布

入门篇:Docker 部署

推荐指数:★★★★★

难度指数:★

使用 Docker 部署,前后端不做分离,部署要求如下:

注意:后端默认不带主题,运行服务后还需登入后台管理界面 https://www.blog.com/proxy/qaqdmin 参照 云函数 配置主题

配置文件

文件目录结构如下,.envdocker-compose.yml需要用户创建,data 目录在容器运行后自动生成

📂 ~/mix-space
│
├── 📜 .env
├── 📜 docker-compose.yml
└── 📂 data
    ├── 📂 mongo
    ├── 📂 redis
    └── 📂 core

.env

注意:API 相关配置 一般不需修改

# 💻 前端配置
# 🌐 API 相关配置
NEXT_PUBLIC_API_URL=http://core:2333/api/v2
NEXT_PUBLIC_GATEWAY_URL=http://core:2333/

# 🔑 认证 & API 密钥
TMDB_API_KEY=
GH_TOKEN=

# 🖥️ 后端配置
# 🔐 安全性配置
JWT_SECRET=G7xJpQzW8mV4L2YfK9N6T1RbX3dMC0H(32位)
ALLOWED_ORIGINS=
ENCRYPT_KEY=593f62860255feb0a914534a43814b9809cc7534da7f5485cd2e3d3c8609acab(64位)
ENCRYPT_ENABLE=false

# 🚀 缓存相关配置
CDN_CACHE_HEADER=true
FORCE_CACHE_HEADER=false

# 🛢 数据库配置
MONGO_CONNECTION=

# ⚡ 请求限流(Throttle)
THROTTLE_TTL=10
THROTTLE_LIMIT=20

dockers-compose.yml

services:
  shiro:
    container_name: shiro
    image: innei/shiro:latest
    volumes:
      - ./.env:/app/.env
    restart: always
    environment:
      - NEXT_SHARP_PATH=/usr/local/lib/node_modules/sharp
    ports:
      - "127.0.0.1:2323:2323"
    depends_on:
      - core
    networks:
      - mix-space

  core:
    container_name: core
    image: innei/mx-server:latest
    environment:
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - DB_HOST=mongo
      - REDIS_HOST=redis
    volumes:
      - ./data/core:/root/.mx-space
    ports:
      - "127.0.0.1:2333:2333"
    depends_on:
      - mongo
      - redis
    networks:
      - mix-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/mongo:/data/db
    networks:
      - mix-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:
      - mix-space
    restart: unless-stopped

networks:
  mix-space:
    driver: bridge

nginx反代

请在域名(以www.blog.com为例)添加一条 A记录指向服务器 IP,然后添加以下 server 块

server {
    listen 80;
    listen 443 ssl http2 ; 
   
    server_name www.blog.com; 
    index index.html; 
    proxy_set_header Host $host; 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header X-Forwarded-Host $server_name; 
    proxy_set_header Upgrade $http_upgrade; 
    proxy_set_header Connection "upgrade"; 
    error_log /www/sites/www.example.com/log/error.log;
    access_log /www/sites/www.example.com/log/access.log; 
    location /socket.io {
        proxy_set_header Upgrade $http_upgrade; 
        proxy_set_header Connection "Upgrade"; 
        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; 
    }
    location /api/v2 {
        proxy_pass http://127.0.0.1:2333/api/v2; 
    }
    location /render {
        proxy_pass http://127.0.0.1:2333/render; 
    }
    location / {
        proxy_pass http://127.0.0.1:2323; 
    }
    location /qaqdmin {
        proxy_pass http://127.0.0.1:2333/proxy/qaqdmin;
    }
    location /proxy {
        proxy_pass http://127.0.0.1:2333/proxy;
    }
 
    location ~* \/(feed|sitemap|atom.xml) {
        proxy_pass http://127.0.0.1:2333/$1; 
    }
    ssl_certificate /www/sites/www.example.com/ssl/fullchain.pem; 
    ssl_certificate_key /www/sites/www.example.com/ssl/privkey.pem; 
    ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1; 
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; 
    ssl_prefer_server_ciphers on; 
    ssl_session_cache shared:SSL:10m; 
    ssl_session_timeout 10m; 
    error_page 497 https://$host$request_uri; 
    limit_conn perserver 300; 
    limit_conn perip 25; 
    limit_rate 512k; 
}

进阶篇:传统方法部署

推荐指数:★★★

难度指数:★★★

构建部署,前后端分离,部署要求如下:

注意:后端默认不带主题,运行服务后还需登入后台管理界面 https://backend.blog.com/proxy/qaqdmin 参照 云函数 配置主题

构建后端

准备环境

简单部署

后端仓库:Mix Space/core

手动部署

构建前端

准备环境

简单部署

自动部署

Name Secret Description
HOST example.com SSH 登录地址
PORT 22 SSH 登录端口
USER admin SSH 登录用户名
PASSWORD P@ssw0rd SSH 登录密码,与 KEY 二选一
KEY ssh-rsa***** SSH 私钥,与 PASSWORD 二选一

配置 nginx

特别篇一:render 部署

推荐指数:★★★★

难度指数:★★★★

准备工作

  1. 注册 mongoDB
  2. 注册 github
  3. 注册 render
  4. 注册 vercel

搭建框架

📦 项目结构
├── 🗄️ 数据库 (MongoDB)
│   ├── 📂 用户数据
│   ├── 📂 文章数据
│   ├── 📂 其他业务数据
│
├── 🔥 缓存层 (Redis - Render 部署)
│   ├── ⚡ 存储会话信息
│   ├── ⚡ 存储热点数据
│   ├── ⚡ 降低数据库查询压力
│
├── 🎨 前端 (Shiro 主题 - Vercel 托管)
│   ├── 📦 依赖: Next.js / React
│   ├── 🎨 UI 组件
│   ├── 🔗 调用后端 API
│   ├── ⚙️ 状态管理
│
├── 🚀 后端 (Mix Space - Render 部署)
│   ├── 📦 依赖: Nest.js
│   ├── 🔗 连接 MongoDB
│   ├── 🔗 连接 Redis 缓存
│   ├── 🔒 认证 & 授权
│   ├── 📡 API 处理
│
└── 🌐 部署
    ├── 📤 前端部署到 Vercel
    ├── 📤 后端 & Redis 部署到 Render
    ├── 🛠️ CI/CD 自动化

注意:后端默认不带主题,运行服务后还需登入后台管理界面 https://www.blog.com/proxy/qaqdmin 参照教程最后 云函数 配置主题

开始搭建

  1. 数据库:MongoDB

    创建步骤参考 抱脸部署LibreChat教程 , 这里不再赘述

  2. 缓存:Redis

    • 登陆 render,依次点击右上角 +Key Value
    • Name 填写 redis
    • Project 选择 Production
    • Region 选择 Singapore
    • Instance Type 选择 Free
    • 点击 Create Key Value Instance 创建
    • 下拉在 Connections 下复制 Internal Key Value URL,形如 redis://red-cvdd4eofnakc738i3f00:6379,粘贴到剪贴板后只保留中间部分red-cvdd4eofnakc738i3f00 作为 REDIS_HOST
  3. 前端:Shiro

    • 点击创建,名称填写 shiro,修改环境变量后点击 Deploy 部署

      Name Value Description
      NEXT_PUBLIC_API_URL https://backend.blog.com/api/v2 后端API接口地址
      NEXT_PUBLIC_GATEWAY_URL https://backend.blog.com/ 后端域名
    • 修改区域,在 Settings - Functions - Function Region 选择 Asia - Hong Kong,注意先取消勾选再选择

    • 自定义域,注意 vercel 分配的域名国内无法直连需要添加自定义域:

      1. 在 cloudflare 需要绑定的域名添加一条 A 记录指向 76.223.126.88
      2. 进入 vercel 的 shiro 项目依次点击 Settings - Domains - Add
      3. 输入绑定的域名,然后点击 Add Domain,注意勾选第三项后点击 Add 完成域名自定义

      这里假定添加的自定义域为 frontend.blog.com

  4. 后端:Mix Space

    • 登录 render,依次点击右上角 +Web Service

    • 选择 Existing imageImage URL 输入 innei/mx-server:latest

    • 稍等几秒,点击 Connect

    • Name 修改为 core

    • Project 选择 Production

    • Region 选择 Singapore

    • Instance Type 选择 Free

    • Environment Variables 下方点击 Add from .env,修改以下环境变量后粘贴到输入框

      ALLOWED_ORIGINS=frontend.blog.com
      REDIS_HOST=red-cvdd4eofnakc738i3f00
      DB_CONNECTION_STRING=mongodb+srv://<DB_USER>:<DB_PASSWORD>@<DB_USER>
      JWT_SECRET=************************
      

      其中JWT_SECRET可通过以下命令快速生成

      openssl rand -base64 24 | cut -c1-32
      
    • 点击 Deploy Web Service 部署

    • 下滑在 Custom Domains 下方点击 + Add Custom Domain,输入自定义域,格式如 backend.blog.com

    • 按提示前往域名托管处,如 cloudflare,添加一条 CNAME 记录后返回点击 verify 验证

特别篇二:serv00 部署

推荐指数:★★★

难度指数:★★★★★

我的电铺现已上架一键安装脚本,简化了安装过程,能够更快速地完成安装,欢迎发电支持 https://afdian.com/a/ireno?tab=shop

注意:后端默认不带主题,运行服务后还需登入后台管理界面 https://后端域名/proxy/qaqdmin 参照教程最后 云函数 配置主题

alias node=node20
alias npm=npm20

mkdir -p ~/.npm-global && npm config set prefix "$HOME/.npm-global" && echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.profile && source ~/.profile && npm install -g pm2 && pm2

mkdir ~/.mx-space
npm install sharp@0.32.5 --prefix ~/.mx-space

devil mongo db add core

devil port add tcp 2323
devil port add tcp 2333
devil port add tcp 5555
cd ~/domains/***.serv00.net

fetch https://raw.githubusercontent.com/redis/redis/7.4/redis.conf

sed -i '' "s/^port .*/port 5555/" redis.conf
sed -i '' -E "s/^# requirepass .*/requirepass secure_pass/" redis.conf
sed -i '' 's/^appendonly no$/appendonly yes/' redis.conf

mkdir -p ~/domains/***.serv00.net/mix-space/core && cd $_
curl -L -O https://github.com/mx-space/core/releases/latest/download/release-linux.zip
unzip release-linux.zip
rm release-linux.zip

mkdir -p ~/domains/***.serv00.net/mix-space/shiro && cd $_
curl -L -O https://github.com/Innei/Shiro/releases/latest/download/release.zip
unzip release.zip
rm release.zip

cat > ~/domains/***.serv00.net/ecosystem.config.js <<EOF
const { execSync } = require('node:child_process');
const nodePath = execSync(`npm root --quiet -g`, { encoding: 'utf-8' }).trim();
module.exports = {
  apps: [
    {
      name: 'shiro',
      script: 'server.js',
      autorestart: true,
      watch: false,
      max_memory_restart: '500M',
      env: {
        PORT: 2323,
        NODE_ENV: 'production',
        NEXT_SHARP_PATH: process.env.NEXT_SHARP_PATH,
	    NEXT_PUBLIC_API_URL: 'http://127.0.0.1:2323/api/v2',
	    NEXT_PUBLIC_GATEWAY_URL: 'http://127.0.0.1:2323'
      },
      log_date_format: 'YYYY-MM-DD HH:mm:ss',
    },  
    {
      name: 'redis',
      script: 'redis-server',
      args: '~/domains/***.serv00.net/mix-space/core/redis.conf',
      autorestart: true,
      watch: false,
    },  
    {
      name: 'core',
      script: 'index.js',
      autorestart: true,
      watch: false,
      max_memory_restart: '500M',
      args: [
        '--color',
        '--encrypt_enable',
        '--encrypt_key ********************(64位)',
        '--redis_host 127.0.0.1',
        '--redis_port 5555',
        '--redis_password ********',
        '--db_host mongo15.serv00.com',
        '--collection_name mix-space',
        '--db_user mix-space',
        '--db_password ********',
        '--port 2333',
        '--allowed_origins example1.com,example2.com,localhost',
        '--jwt_secret *************(32位)',
      ].join(' '),
      env: {
        NODE_ENV: 'production',
        NODE_PATH: nodePath,
      },
    },
  ],
};
EOF

pm2 start ~/domains/***.serv00.net/ecosystem.config.js

云函数

Shiro 主题

PS 状态插件

server_config:
  endpoint: "https://backend.example.com/api/v2/fn/ps/update" # https://api.example.com/api/v2/fn/ps/update
  token: "YOUR-KEY-HERE" # 设置的key
  report_time: 5 # 上报时间间隔,单位秒
rules: # 软件名的替换规则
  - match_application: WeChat
    replace:
      application: 微信
      description: 一个小而美的办公软件
  - match_application: QQ
    replace:
      application: QQ
      description: 一个多功能的通讯软件
  - match_application: Netease Cloud Music
    replace:
      application: 网易云音乐
      description: 一个音乐播放和分享的平台          

编辑好后 CTRL + S 保存并关闭,然后再运行 Kizuna,就会定时往定义的端点上传你的活动数据

参考链接

Mix Space 官方文档

Mix-Space部署最新前端Shiro

Mix-Space 搭建从入门到入土