Mix-Space + Shiro 搭建
Mix Space 是一个小型个人空间站点程序。不同于传统的博客程序,采用前后端分离设计, 适合那些喜欢写不同风格或类型的写作爱好者。
Note
准备工作
- 服务器 * 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
配置反向代理
以下是一个示例文件
/etc/nginx/sites-enabled/mx-space.confserver { 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
中引用此文件/etc/nginx/nginx.confhttp { ... 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)
- 初始化环境
curl -fsSL https://deb.nodesource.com/setup_current.x | sudo bash - sudo apt-get install -y nodejs npm install -g pm2 curl -fsSL https://get.pnpm.io/install.sh | sh -
重新登录服务器
拉取仓库 & 安装依赖
cd ~/mx-space git clone https://github.com/Innei/Shiro.git cd Shiro git submodule init && git submodule update pnpm i
设置主题配置
进入 Mix Space 后台,进入「配置与云函数」页面,点击右上角的新增按钮,在编辑页面中,填入以下设置:
Important
- 名称:
shiro
- 引用:
theme
- 数据类型:
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 } } } }
保存修改
- 名称:
修改环境变量
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
好了 暂时也没想到什么新的小技巧 就到此为止吧