CT8 上安装 pnpm 并部署 Sub-Store 后端教程
CT8 上安装 pnpm 并部署 Sub-Store 后端教程
本文介绍如何在 CT8 Hosting 的 Node.js 环境中,通过 SSH 使用 pnpm 部署 Sub-Store 后端。
本文以以下环境为例:
系统用户:your_username
Node.js:v22.22.2
pnpm:11.0.9
Sub-Store:v2.36.31
Node.js 端口:39439
后端访问前缀:/my_path
最终效果:
https://你的域名/my_path
↓
CT8 Node.js
↓
127.0.0.1:39439
↓
Sub-Store Backend
一、准备 CT8 Node.js 环境
首先通过 SSH 登录 CT8。
检查 Node.js:
node -v例如:
v22.22.2
检查 npm:
npm -v再检查 Corepack:
corepack --versionCorepack 可以管理项目指定的 pnpm 版本。Node.js 官方生态中的
Corepack 会根据项目 package.json 中的
packageManager 字段选择对应的包管理器版本。
二、在 CT8 安装 pnpm
方法一:使用 Corepack
推荐优先尝试:
corepack enable然后:
pnpm -v如果能够显示版本号,说明 pnpm 已经可以使用。
方法二:安装到用户目录
如果 CT8 不允许 corepack enable
修改系统目录,可以安装到自己的 Home 目录:
npm install --prefix "$HOME/.local" pnpm安装完成后检查:
find "$HOME" -type f -name pnpm 2>/dev/null | head -20例如:
/home/your_username/.local/bin/pnpm
将用户目录加入 PATH:
export PATH="$HOME/.local/bin:$PATH"然后:
pnpm -v三、解决 CT8 上
pnpm: command not found
CT8 上可能出现这样的情况:
pnpm start可以运行,但是:
./start.sh却提示:
pnpm: command not found
这是因为脚本运行时的 PATH 与 SSH 交互式 Shell 不一定完全相同。
可以检查:
which pnpm如果没有输出,再执行:
find "$HOME" -type f -name pnpm 2>/dev/null | head -20假设得到:
/home/your_username/.local/bin/pnpm
那么启动脚本中建议直接使用绝对路径:
/home/your_username/.local/bin/pnpm同时加入:
export PATH="/home/your_username/.local/bin:$PATH"这样最稳定。
四、下载 Sub-Store 后端源码
进入 Home 目录:
cd ~克隆官方仓库:
git clone https://github.com/sub-store-org/Sub-Store.git进入 backend:
cd ~/Sub-Store/backendSub-Store 官方仓库目前将后端源码放在 backend
目录,并使用 pnpm 管理依赖。
如果已经下载过:
cd ~/Sub-Store/backend
git pull五、检查 package.json
执行:
cat package.json你应该能看到类似:
{
"name": "sub-store",
"version": "2.36.31",
"main": "src/main.js",
"packageManager": "pnpm@11.0.9",
"scripts": {
"serve": "node sub-store.min.js",
"start": "nodemon -w src -w package.json --exec babel-node src/main.js"
}
}这里有两个重要信息:
packageManager: pnpm@11.0.9
说明项目期望使用 pnpm 11.0.9。
以及:
start
实际执行:
nodemon
↓
babel-node
↓
src/main.js
因此不要直接:
node src/main.js否则可能遇到:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/core'
因为源码启动依赖项目的 Babel/路径别名配置。
六、安装项目依赖
进入:
cd ~/Sub-Store/backend安装:
pnpm install如果项目已经存在 lockfile,并且希望严格按照 lockfile 安装:
pnpm install --frozen-lockfile安装完成后会生成:
node_modules/
七、确认 pnpm 版本
执行:
pnpm -v如果项目要求:
11.0.9
可以通过 Corepack 管理指定版本。
例如:
corepack prepare pnpm@11.0.9 --activate然后:
pnpm -v确认版本。
项目在 package.json 中指定
packageManager 后,可以让 Corepack
根据项目配置使用相应的包管理器版本。
八、确认 CT8 分配的 Node.js 端口
CT8 Node.js 应用通常需要使用平台分配的端口。
本文使用:
39439
因此 Sub-Store 需要监听:
0.0.0.0:39439
或者:
:::39439
九、设置 Sub-Store 环境变量
本教程使用:
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path三个变量的作用:
| 变量 | 作用 |
|---|---|
PORT |
Node.js 服务监听端口 |
SUB_STORE_FRONTEND_BACKEND_PATH |
前端访问后端时使用的路径前缀 |
SUB_STORE_BACKEND_PREFIX |
让后端自身处理该路径前缀 |
这里最容易遗漏的是:
SUB_STORE_BACKEND_PREFIX=/my_path如果只设置:
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path前端访问:
https://你的域名/my_path
可能得到:
404 Not Found
而同时设置:
SUB_STORE_BACKEND_PREFIX=/my_path后端也会使用这个前缀。
十、测试启动 Sub-Store
进入:
cd ~/Sub-Store/backend执行:
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path
pnpm start如果正常,日志应该类似:
Sub-Store -- v2.36.31
[sub-store] INFO: [CORS] allowed origins: * (default:node)
[sub-store] INFO: [BACKEND] body JSON limit: 1mb
[sub-store] INFO: [BACKEND] listening on :::39439
看到:
listening on :::39439
就说明 Node.js 已经成功监听 CT8 分配的端口。
十一、检查环境变量是否生效
可以通过 Sub-Store 启动后的信息确认:
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
以及:
SUB_STORE_BACKEND_PREFIX=/my_path
确保没有拼写错误。
十二、测试本地后端
另外打开一个 SSH 窗口。
执行:
curl -i http://127.0.0.1:39439/如果返回 HTTP 响应,说明 Node.js 后端正常。
也可以检查端口:
ss -lnt | grep 39439应该看到类似:
LISTEN ... 0.0.0.0:39439
或者:
LISTEN ... [::]:39439
十三、配置 CT8 网站
假设你的域名是:
https://yourdomain.ct8.pl
Node.js 后端实际监听:
127.0.0.1:39439
那么最终访问:
https://yourdomain.ct8.pl/my_path
应该由 CT8 的 Node.js 网站映射到:
127.0.0.1:39439
如果 CT8 Node.js 网站本身已经负责端口转发,就不需要再额外使用 Nginx。
最终结构:
浏览器
│
│ HTTPS
▼
https://yourdomain.ct8.pl/my_path
│
▼
CT8 Node.js
│
▼
39439
│
▼
Sub-Store
十四、创建启动脚本
为了避免每次输入环境变量,可以创建:
cd ~/Sub-Store/backend
vim start.sh写入:
#!/bin/bash
export PATH="/home/your_username/.local/bin:$PATH"
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path
cd "$(dirname "$0")" || exit 1
# 使用 pnpm start
exec /home/your_username/.local/bin/pnpm start保存后:
chmod +x start.sh以后:
./start.sh即可启动。
十五、为什么推荐 pnpm 绝对路径
不要只写:
pnpm start而推荐:
/home/your_username/.local/bin/pnpm startbecause CT8 的 cron、后台脚本 and SSH Shell 的 PATH 可能不同。
这样即使:
which pnpm没有结果,脚本仍然可以找到 pnpm。
十六、让 Sub-Store 后台运行
测试确认正常后,可以使用:
nohup ./start.sh > sub-store.log 2>&1 &查看日志:
tail -f sub-store.log查看进程:
ps -aux | grep -E "pnpm|nodemon|babel-node" | grep -v grep查看端口:
ss -lnt | grep 39439十七、增加自动保活
CT8 上可以使用 cron 定时检查。
编辑:
crontab -e例如每 5 分钟检查一次:
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1
但是这里有一个重要问题:
如果 start.sh 每次都直接:
pnpm start就会产生多个 Sub-Store 实例。
所以必须增加防重复启动。
十八、推荐的最终保活脚本
把 start.sh 修改为:
#!/bin/bash
export PATH="/home/your_username/.local/bin:$PATH"
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path
cd "$(dirname "$0")" || exit 1
# 检查 Sub-Store 主进程
if pgrep -f "/Sub-Store/backend.*babel-node.*src/main.js" >/dev/null 2>&1; then
echo "[Sub-Store] 已经在运行,无需启动。"
exit 0
fi
# 检查 nodemon
if pgrep -f "/Sub-Store/backend.*nodemon.*src" >/dev/null 2>&1; then
echo "[Sub-Store] nodemon 已经在运行,无需启动。"
exit 0
fi
# 检查 39439 端口
if ss -lnt 2>/dev/null | grep -q ':39439 '; then
echo "[Sub-Store] 39439 端口已经被占用,无需启动。"
exit 0
fi
echo "[Sub-Store] 服务未运行,正在启动..."
nohup /home/your_username/.local/bin/pnpm start >> sub-store.log 2>&1 &
echo "[Sub-Store] 启动命令已执行,PID: $!"十九、为什么不要只用 curl 判断
有些情况下:
curl http://127.0.0.1:39439/不一定能准确判断 Node.js 是否已经运行。
例如:
/返回非 2xx- 后端要求
/my_path - 服务刚启动还没有完成初始化
- Node.js 已监听端口但 HTTP 尚未完全准备好
这时:
curl -sf可能返回失败。
如果 cron 频繁调用脚本,就可能导致:
pnpm start
pnpm start
pnpm start
pnpm start
最终产生多个 Node.js 实例。
因此保活脚本同时检查:
进程
+
nodemon
+
端口
更加适合这个场景。
二十、清理重复的 Sub-Store 进程
如果之前误启动了多个实例,可以先查看:
ps -aux | grep "Sub-Store/backend" | grep -v grep如果确认都是重复的 Sub-Store,可以停止:
pkill -f "/Sub-Store/backend.*"然后再次确认:
ps -aux | grep "Sub-Store/backend" | grep -v grep没有输出后,再:
./start.sh二十一、检查 Sub-Store 是否正常运行
查看进程
ps -aux | grep -E "pnpm|nodemon|babel-node" | grep -v grep查看端口
ss -lnt | grep 39439查看日志
tail -100 sub-store.log实时查看:
tail -f sub-store.log测试本地接口
curl -i http://127.0.0.1:39439/测试网站
浏览器打开:
https://你的域名/my_path
二十二、常见问题
问题
1:pnpm: command not found
执行:
find "$HOME" -type f -name pnpm 2>/dev/null | head如果找到:
/home/your_username/.local/bin/pnpm
使用:
export PATH="$HOME/.local/bin:$PATH"或者直接使用:
/home/your_username/.local/bin/pnpm问题 2:直接
node src/main.js 报错
例如:
Cannot find package '@/core'
不要直接:
node src/main.js使用:
pnpm startbecause pnpm start 会通过项目配置启动:
nodemon
→ babel-node
→ src/main.js
问题 3:启动后监听 3000
如果看到:
listen EPERM: operation not permitted :::3000
说明项目没有使用你设置的端口,或者 PORT
没有被正确读取。
检查项目源码中实际使用的端口变量:
grep -RniE "3000|PORT|listen\(" src | head -100对于当前 Sub-Store 版本,实际部署时应以项目自身支持的环境变量为准。
官方仓库的开发说明也使用
SUB_STORE_BACKEND_API_PORT
指定开发后端端口,例如:
SUB_STORE_BACKEND_API_PORT=3000 pnpm esbuild:dev因此不同启动脚本可能使用不同的端口变量,不能一概而论。
问题 4:/my_path
返回 404
检查:
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path然后重新启动:
./start.sh同时确认 CT8 Node.js 应用确实将域名请求转发到了:
39439
问题 5:重复运行 start.sh
如果出现多个:
pnpm start
或者多个:
nodemon
不要继续运行脚本。
先:
ps -aux | grep "Sub-Store/backend" | grep -v grep确认后清理:
pkill -f "/Sub-Store/backend.*"然后只启动一个:
./start.sh二十三、完整部署流程
以后重新部署 Sub-Store,可以直接按照下面流程操作。
1. 登录 CT8
ssh 用户名@服务器2. 检查 Node.js
node -v3. 检查 pnpm
pnpm -v如果找不到:
export PATH="$HOME/.local/bin:$PATH"4. 获取源码
cd ~
git clone https://github.com/sub-store-org/Sub-Store.git5. 进入 backend
cd ~/Sub-Store/backend6. 安装依赖
pnpm install7. 创建启动脚本
vim start.sh内容:
#!/bin/bash
export PATH="/home/your_username/.local/bin:$PATH"
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path
cd "$(dirname "$0")" || exit 1
if pgrep -f "/Sub-Store/backend.*babel-node.*src/main.js" >/dev/null 2>&1; then
echo "[Sub-Store] 已经在运行,无需启动。"
exit 0
fi
if pgrep -f "/Sub-Store/backend.*nodemon.*src" >/dev/null 2>&1; then
echo "[Sub-Store] nodemon 已经在运行,无需启动。"
exit 0
fi
if ss -lnt 2>/dev/null | grep -q ':39439 '; then
echo "[Sub-Store] 39439 端口已经被占用,无需启动。"
exit 0
fi
echo "[Sub-Store] 服务未运行,正在启动..."
nohup /home/your_username/.local/bin/pnpm start >> sub-store.log 2>&1 &
echo "[Sub-Store] 启动命令已执行,PID: $!"8. 添加执行权限
chmod +x start.sh9. 启动
./start.sh10. 检查
ss -lnt | grep 3943911. 设置 cron
crontab -e加入:
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1
最终形成:
CT8
│
▼
Node.js v22
│
▼
pnpm 11.x
│
▼
~/Sub-Store/backend
│
▼
pnpm start
│
┌──────┴──────┐
▼ ▼
nodemon babel-node
│ │
└──────┬──────┘
▼
Sub-Store
│
▼
:39439
│
▼
/my_path
│
▼
https://你的域名/my_path
二十四、更新 Sub-Store
以后更新源码:
cd ~/Sub-Store
git pull进入后端:
cd backend重新安装依赖:
pnpm install然后重启:
pkill -f "/Sub-Store/backend.*"
./start.sh查看:
tail -f sub-store.log二十五、最终推荐配置
对于 CT8 + Sub-Store 后端,推荐保持:
Node.js
v22.22.2
pnpm
11.0.9
项目
~/Sub-Store/backend
端口
39439
前端/后端路径
/my_path
环境变量:
PORT=39439
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
SUB_STORE_BACKEND_PREFIX=/my_path启动:
./start.sh后台:
nohup ./start.sh > sub-store.log 2>&1 &保活:
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1
这样就可以在 CT8 上完成一个比较完整的 Sub-Store 后端部署。
参考资料
- Sub-Store 官方 GitHub:sub-store-org/Sub-Store
- pnpm 安装文档:pnpm Installation
- Corepack 文档:Corepack on npm
- Sub-Store 官方链接参数说明:Sub-Store Wiki:链接参数说明
| 本文由 BOSH 的博客助手 HerMes 整理 🚀 |
|---|
| 原文链接:CT8 上安装 pnpm 并部署 Sub-Store 后端教程 |