Featured image of post CT8 上安装 pnpm 并部署 Sub-Store 后端教程

CT8 上安装 pnpm 并部署 Sub-Store 后端教程

CT8 上安装 pnpm 并部署 Sub-Store 后端教程

本文介绍如何在 CT8 Hosting 的 Node.js 环境中,通过 SSH 使用 pnpm 部署 Sub-Store 后端。

本文以以下环境为例:

1
2
3
4
5
6
系统用户:your_username
Node.js:v22.22.2
pnpm:11.0.9
Sub-Store:v2.36.31
Node.js 端口:39439
后端访问前缀:/my_path

最终效果:

1
2
3
4
5
6
7
https://你的域名/my_path
CT8 Node.js
127.0.0.1:39439
Sub-Store Backend

一、准备 CT8 Node.js 环境

首先通过 SSH 登录 CT8。

检查 Node.js:

1
node -v

例如:

1
v22.22.2

检查 npm:

1
npm -v

再检查 Corepack:

1
corepack --version

Corepack 可以管理项目指定的 pnpm 版本。Node.js 官方生态中的 Corepack 会根据项目 package.json 中的 packageManager 字段选择对应的包管理器版本。


二、在 CT8 安装 pnpm

方法一:使用 Corepack

推荐优先尝试:

1
corepack enable

然后:

1
pnpm -v

如果能够显示版本号,说明 pnpm 已经可以使用。


方法二:安装到用户目录

如果 CT8 不允许 corepack enable 修改系统目录,可以安装到自己的 Home 目录:

1
npm install --prefix "$HOME/.local" pnpm

安装完成后检查:

1
find "$HOME" -type f -name pnpm 2>/dev/null | head -20

例如:

1
/home/your_username/.local/bin/pnpm

将用户目录加入 PATH:

1
export PATH="$HOME/.local/bin:$PATH"

然后:

1
pnpm -v

三、解决 CT8 上 pnpm: command not found

CT8 上可能出现这样的情况:

1
pnpm start

可以运行,但是:

1
./start.sh

却提示:

1
pnpm: command not found

这是因为脚本运行时的 PATH 与 SSH 交互式 Shell 不一定完全相同。

可以检查:

1
which pnpm

如果没有输出,再执行:

1
find "$HOME" -type f -name pnpm 2>/dev/null | head -20

假设得到:

1
/home/your_username/.local/bin/pnpm

那么启动脚本中建议直接使用绝对路径:

1
/home/your_username/.local/bin/pnpm

同时加入:

1
export PATH="/home/your_username/.local/bin:$PATH"

这样最稳定。


四、下载 Sub-Store 后端源码

进入 Home 目录:

1
cd ~

克隆官方仓库:

1
git clone https://github.com/sub-store-org/Sub-Store.git

进入 backend:

1
cd ~/Sub-Store/backend

Sub-Store 官方仓库目前将后端源码放在 backend 目录,并使用 pnpm 管理依赖。

如果已经下载过:

1
2
cd ~/Sub-Store/backend
git pull

五、检查 package.json

执行:

1
cat package.json

你应该能看到类似:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "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"
  }
}

这里有两个重要信息:

1
packageManager: pnpm@11.0.9

说明项目期望使用 pnpm 11.0.9。

以及:

1
start

实际执行:

1
2
3
4
5
nodemon
babel-node
src/main.js

因此不要直接:

1
node src/main.js

否则可能遇到:

1
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/core'

因为源码启动依赖项目的 Babel/路径别名配置。


六、安装项目依赖

进入:

1
cd ~/Sub-Store/backend

安装:

1
pnpm install

如果项目已经存在 lockfile,并且希望严格按照 lockfile 安装:

1
pnpm install --frozen-lockfile

安装完成后会生成:

1
node_modules/

七、确认 pnpm 版本

执行:

1
pnpm -v

如果项目要求:

1
11.0.9

可以通过 Corepack 管理指定版本。

例如:

1
corepack prepare pnpm@11.0.9 --activate

然后:

1
pnpm -v

确认版本。

项目在 package.json 中指定 packageManager 后,可以让 Corepack 根据项目配置使用相应的包管理器版本。


八、确认 CT8 分配的 Node.js 端口

CT8 Node.js 应用通常需要使用平台分配的端口。

本文使用:

1
39439

因此 Sub-Store 需要监听:

1
0.0.0.0:39439

或者:

1
:::39439

九、设置 Sub-Store 环境变量

本教程使用:

1
2
3
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path

三个变量的作用:

变量作用
PORTNode.js 服务监听端口
SUB_STORE_FRONTEND_BACKEND_PATH前端访问后端时使用的路径前缀
SUB_STORE_BACKEND_PREFIX让后端自身处理该路径前缀

这里最容易遗漏的是:

1
SUB_STORE_BACKEND_PREFIX=/my_path

如果只设置:

1
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path

前端访问:

1
https://你的域名/my_path

可能得到:

1
404 Not Found

而同时设置:

1
SUB_STORE_BACKEND_PREFIX=/my_path

后端也会使用这个前缀。


十、测试启动 Sub-Store

进入:

1
cd ~/Sub-Store/backend

执行:

1
2
3
4
5
export PORT=39439
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path

pnpm start

如果正常,日志应该类似:

1
2
3
4
5
6
7
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

看到:

1
listening on :::39439

就说明 Node.js 已经成功监听 CT8 分配的端口。


十一、检查环境变量是否生效

可以通过 Sub-Store 启动后的信息确认:

1
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path

以及:

1
SUB_STORE_BACKEND_PREFIX=/my_path

确保没有拼写错误。


十二、测试本地后端

另外打开一个 SSH 窗口。

执行:

1
curl -i http://127.0.0.1:39439/

如果返回 HTTP 响应,说明 Node.js 后端正常。

也可以检查端口:

1
ss -lnt | grep 39439

应该看到类似:

1
LISTEN ... 0.0.0.0:39439

或者:

1
LISTEN ... [::]:39439

十三、配置 CT8 网站

假设你的域名是:

1
https://yourdomain.ct8.pl

Node.js 后端实际监听:

1
127.0.0.1:39439

那么最终访问:

1
https://yourdomain.ct8.pl/my_path

应该由 CT8 的 Node.js 网站映射到:

1
127.0.0.1:39439

如果 CT8 Node.js 网站本身已经负责端口转发,就不需要再额外使用 Nginx。

最终结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
浏览器
  │ HTTPS
https://yourdomain.ct8.pl/my_path
CT8 Node.js
39439
Sub-Store

十四、创建启动脚本

为了避免每次输入环境变量,可以创建:

1
2
cd ~/Sub-Store/backend
vim start.sh

写入:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/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

保存后:

1
chmod +x start.sh

以后:

1
./start.sh

即可启动。


十五、为什么推荐 pnpm 绝对路径

不要只写:

1
pnpm start

而推荐:

1
/home/your_username/.local/bin/pnpm start

because CT8 的 cron、后台脚本 and SSH Shell 的 PATH 可能不同。

这样即使:

1
which pnpm

没有结果,脚本仍然可以找到 pnpm.


十六、让 Sub-Store 后台运行

测试确认正常后,可以使用:

1
nohup ./start.sh > sub-store.log 2>&1 &

查看日志:

1
tail -f sub-store.log

查看进程:

1
ps -aux | grep -E "pnpm|nodemon|babel-node" | grep -v grep

查看端口:

1
ss -lnt | grep 39439

十七、增加自动保活

CT8 上可以使用 cron 定时检查。

编辑:

1
crontab -e

例如每 5 分钟检查一次:

1
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1

但是这里有一个重要问题:

如果 start.sh 每次都直接:

1
pnpm start

就会产生多个 Sub-Store 实例。

所以必须增加防重复启动。


十八、推荐的最终保活脚本

start.sh 修改为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/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 判断

有些情况下:

1
curl http://127.0.0.1:39439/

不一定能准确判断 Node.js 是否已经运行。

例如:

  • / 返回非 2xx
  • 后端要求 /my_path
  • 服务刚启动还没有完成初始化
  • Node.js 已监听端口但 HTTP 尚未完全准备好

这时:

1
curl -sf

可能返回失败。

如果 cron 频繁调用脚本,就可能导致:

1
2
3
4
pnpm start
pnpm start
pnpm start
pnpm start

最终产生多个 Node.js 实例。


二十、清理重复的 Sub-Store 进程

如果之前误启动了多个实例,可以先查看:

1
ps -aux | grep "Sub-Store/backend" | grep -v grep

如果确认都是重复的 Sub-Store,可以停止:

1
pkill -f "/Sub-Store/backend.*"

二十一、检查 Sub-Store 是否正常运行

查看进程

1
ps -aux | grep -E "pnpm|nodemon|babel-node" | grep -v grep

查看端口

1
ss -lnt | grep 39439

查看日志

1
tail -100 sub-store.log

实时查看:

1
tail -f sub-store.log

测试本地接口

1
curl -i http://127.0.0.1:39439/

测试网站

浏览器打开:

1
https://你的域名/my_path

二十二、常见问题

问题 1:pnpm: command not found

执行:

1
find "$HOME" -type f -name pnpm 2>/dev/null | head

如果找到:

1
/home/your_username/.local/bin/pnpm

使用:

1
export PATH="$HOME/.local/bin:$PATH"

或者直接使用:

1
/home/your_username/.local/bin/pnpm

问题 2:直接 node src/main.js 报错

例如:

1
Cannot find package '@/core'

不要直接:

1
node src/main.js

使用:

1
pnpm start

because pnpm start 会通过项目配置启动:

1
2
3
nodemon
→ babel-node
→ src/main.js

问题 3:启动后监听 3000

如果看到:

1
listen EPERM: operation not permitted :::3000

说明项目没有使用你设置的端口,或者 PORT 没有被正确读取。

检查项目源码中实际使用的端口变量:

1
grep -RniE "3000|PORT|listen\(" src | head -100

对于当前 Sub-Store 版本,实际部署时应以项目自身支持的环境变量为准。

官方仓库的开发说明也使用 SUB_STORE_BACKEND_API_PORT 指定开发后端端口,例如:

1
SUB_STORE_BACKEND_API_PORT=3000 pnpm esbuild:dev

因此不同启动脚本可能使用不同的端口变量,不能一概而论。


问题 4:/my_path 返回 404

检查:

1
2
export SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
export SUB_STORE_BACKEND_PREFIX=/my_path

然后重新启动:

1
./start.sh

同时确认 CT8 Node.js 应用确实将域名请求转发到了:

1
39439

问题 5:重复运行 start.sh

如果出现多个:

1
pnpm start

或者多个:

1
nodemon

不要继续运行脚本。

先:

1
ps -aux | grep "Sub-Store/backend" | grep -v grep

确认后清理:

1
pkill -f "/Sub-Store/backend.*"

然后只启动一个:

1
./start.sh

二十三、完整部署流程

以后重新部署 Sub-Store,可以直接按照下面流程操作。

1. 登录 CT8

1
ssh 用户名@服务器

2. 检查 Node.js

1
node -v

3. 检查 pnpm

1
pnpm -v

如果找不到:

1
export PATH="$HOME/.local/bin:$PATH"

4. 获取源码

1
2
cd ~
git clone https://github.com/sub-store-org/Sub-Store.git

5. 进入 backend

1
cd ~/Sub-Store/backend

6. 安装依赖

1
pnpm install

7. 创建启动脚本

1
vim start.sh

内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/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. 添加执行权限

1
chmod +x start.sh

9. 启动

1
./start.sh

10. 检查

1
ss -lnt | grep 39439

11. 设置 cron

1
crontab -e

加入:

1
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1

最终形成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
                    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

以后更新源码:

1
2
cd ~/Sub-Store
git pull

进入后端:

1
cd backend

重新安装依赖:

1
pnpm install

然后重启:

1
2
pkill -f "/Sub-Store/backend.*"
./start.sh

查看:

1
tail -f sub-store.log

二十五、最终推荐配置

对于 CT8 + Sub-Store 后端,推荐保持:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Node.js
v22.22.2

pnpm
11.0.9

项目
~/Sub-Store/backend

端口
39439

前端/后端路径
/my_path

环境变量:

1
2
3
PORT=39439
SUB_STORE_FRONTEND_BACKEND_PATH=/my_path
SUB_STORE_BACKEND_PREFIX=/my_path

启动:

1
./start.sh

后台:

1
nohup ./start.sh > sub-store.log 2>&1 &

保活:

1
*/5 * * * * /home/your_username/Sub-Store/backend/start.sh >> /home/your_username/Sub-Store/backend/watchdog.log 2>&1

这样就可以在 CT8 上完成一个比较完整的 Sub-Store 后端部署。


参考资料


本文由 BOSH 的博客助手 HerMes 整理 🚀

原文链接:[CT8 上安装 pnpm 并部署 Sub-Store 后端教程]

热爱生活 学无止境
使用 Hugo 构建
主题 StackJimmy 设计