wxminiapp-server 部署运维完整教程
适用环境:Ubuntu 20.04+(aarch64 / x86_64)· Node.js 22.5+ · systemd
本文档对应服务器路径:/opt/wxminiapp-server,服务地址 http://<服务器IP>:8787
一、目录文件说明(/opt/wxminiapp-server/ 下每个文件)
| 文件/目录 |
作用 |
能否删除 |
server.mjs |
服务入口。把 Worker 代码包装成普通 Node HTTP 服务;读取 .env、注入 SQLite 数据层、启动 HTTP 监听。 |
❌ 必需 |
db.mjs |
SQLite 数据层。用 Node 内置 node:sqlite 建表并实现所有数据读写(users / orders / products / logs / counters),等价于本地版 Cloudflare D1。 |
❌ 必需 |
src/index.js |
业务主代码(微信小程序后端 + /admin 后台)。包含全部路由、JWT、虚拟支付签名、后台页面。 |
❌ 必需 |
.env |
运行配置。端口、账号密码、密钥、微信/支付参数(见第五节)。 |
❌ 必需 |
data/ |
数据库目录。内含 wxminiapp.db(主库)、wxminiapp.db-shm、wxminiapp.db-wal(SQLite 日志文件)。 |
❌ 必需,备份对象 |
run.log |
运行日志(nohup/setsid 方式启动时写入)。 |
可删,会自动重建 |
README.md |
项目说明文档。 |
✅ 可删 |
tools/ |
开发校验/迁移工具(check-admin.js 等)。 |
✅ 运行不需要 |
schema.sql |
旧版 SQL 说明(已废弃)。 |
✅ 可删 |
wrangler.toml |
Cloudflare Workers 部署配置(仅 CF 云端用)。 |
✅ 本地不需要 |
文件依赖关系:server.mjs → 读 .env + db.mjs + src/index.js(三者为运行最小集)。
二、环境要求
| 项目 |
要求 |
说明 |
| 操作系统 |
Ubuntu 20.04+ / Debian 系 |
其他 Linux 也行 |
| CPU 架构 |
aarch64(ARM64)或 x86_64 |
下载对应 Node 包 |
| Node.js |
22.5+(需 --experimental-sqlite)或 23.4+/24+(免参数) |
本项目零 npm 依赖 |
| systemd |
有(ps -p 1 显示 systemd) |
开机自启需要 |
| 内存/磁盘 |
≥512MB 内存、≥100MB 磁盘 |
实测 646MB 内存可跑 |
三、安装 Node.js(以 aarch64 / Ubuntu 20.04 为例)
3.1 检查环境
1
2
| node --version # 已装则跳过本节
uname -m # aarch64 / x86_64
|
3.2 下载官方二进制(自动匹配最新 v22 LTS 的 ARM64 包)
1
2
| cd /tmp
curl -sL -o node.tar.xz "https://nodejs.org/dist/latest-v22.x/$(curl -s https://nodejs.org/dist/latest-v22.x/SHASUMS256.txt | grep -oP 'node-v[0-9.]+-linux-arm64\.tar\.xz' | head -1)"
|
x86_64 机器把上面 URL 里的 linux-arm64 换成 linux-x64。
3.3 解压安装到 /usr/local
1
| tar -xJf node.tar.xz -C /usr/local --strip-components=1
|
3.4 验证
1
2
| node --version # 应显示 v22.x.x
npm --version # 应显示 10.x
|
本项目零 npm 依赖,不需要 npm install。
3.5 重要:如果你之前用 nvm 安装了 Node.js
如果你是通过 nvm install 装的 Node(而不是上面 3.2~3.3 的官方二进制解压方式),后面 systemd 开机自启会找不到 node,必须注意:
先查 node 实际路径:
1
2
3
4
| which node
# 或者如果 which 没输出(nvm 环境未加载),用:
find /root/.nvm -name 'node' -type f 2>/dev/null
# 典型输出:/root/.nvm/versions/node/v22.23.3/bin/node
|
nvm 安装的 node 路径在 /root/.nvm/versions/node/<版本号>/bin/node,不是 /usr/local/bin/node。
systemd 不加载用户的 nvm 环境变量,直接写 node 或写 /usr/local/bin/node 都会报 203/EXEC 错误。
两种解决方式(任选其一):
方式一:改 systemd 服务文件里的 ExecStart 路径(推荐)
1
2
3
| # 把下面命令里的路径替换成你 which node 查到的实际路径
sed -i 's|/usr/local/bin/node|/root/.nvm/versions/node/v22.23.3/bin/node|' /etc/systemd/system/wxminiapp.service
systemctl daemon-reload
|
方式二:做软链接到 /usr/local/bin
1
2
| ln -s /root/.nvm/versions/node/v22.23.3/bin/node /usr/local/bin/node
ln -s /root/.nvm/versions/node/v22.23.3/bin/npm /usr/local/bin/npm
|
建议:如果是全新部署,优先用上面 3.2~3.3 的官方二进制解压方式装到 /usr/local,省事不踩坑。已经用 nvm 装了也没关系,按上面改路径即可。
四、部署项目文件
4.1 创建目录
1
| mkdir -p /opt/wxminiapp-server/src /opt/wxminiapp-server/data
|
4.2 上传文件(本地 → 服务器)
方式一:scp(在本地电脑执行)
1
2
| scp server.mjs db.mjs root@<服务器IP>:/opt/wxminiapp-server/
scp src/index.js root@<服务器IP>:/opt/wxminiapp-server/src/
|
方式二:sftp(在本地电脑执行)
1
2
3
4
5
6
| sftp root@<服务器IP>
cd /opt/wxminiapp-server
put server.mjs
put db.mjs
cd src
put src/index.js
|
4.3 语法检查(可选但建议)
1
2
| cd /opt/wxminiapp-server
node --check server.mjs && node --check db.mjs && node --check src/index.js && echo ALL OK
|
五、配置 .env(关键!)
5.1 创建 .env
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| cd /opt/wxminiapp-server
cat > .env <<'EOF'
PORT=8787
DB_PATH=./data/wxminiapp.db
APP_NAME=我的小程序
ADMIN_PREFIX=admin
JWT_EXPIRE=2592000
ADMIN_PAGE_SIZE=20
KV_MAX_SCAN=1000
XPAY_MOCK=1
ADMIN_USERNAME=admin
ADMIN_PASSWORD=请改成强密码
JWT_SECRET=请生成随机串
WX_APPID=
WX_SECRET=
XPAY_OFFER_ID=
XPAY_APP_KEY=
XPAY_NOTIFY_SIGN_KEY=
EOF
|
5.2 生成随机密钥
1
2
| openssl rand -hex 24 | head -c 48 # 生成 JWT_SECRET(48 位)
openssl rand -base64 9 | tr -d '/+=' | head -c 12 # 生成后台密码
|
5.3 每个配置项含义
| 变量 |
必填 |
说明 |
PORT |
否 |
监听端口,默认 8787 |
DB_PATH |
否 |
SQLite 文件路径,默认 ./data/wxminiapp.db |
APP_NAME |
否 |
小程序显示名 |
ADMIN_PREFIX |
否 |
后台路径前缀,默认 admin → /admin |
JWT_EXPIRE |
否 |
Token 有效期(秒),默认 2592000=30 天 |
ADMIN_PAGE_SIZE |
否 |
后台每页条数,默认 20 |
KV_MAX_SCAN |
否 |
后台列表最大扫描条数,默认 1000 |
XPAY_MOCK |
否 |
1=模拟支付自测;0=真实支付(上线必须 0) |
ADMIN_USERNAME |
是 |
后台登录账号 |
ADMIN_PASSWORD |
是 |
后台登录密码 |
JWT_SECRET |
是 |
签名密钥,≥40 位随机串,泄漏=所有 token 可伪造 |
WX_APPID |
真实上线 |
小程序 AppID。获取:微信公众平台 mp.weixin.qq.com → 开发管理 → 开发设置 → 开发者ID → 复制 AppID(AppID 公开可见) |
WX_SECRET |
真实上线 |
小程序 AppSecret。获取:同上 开发管理 → 开发设置 → 开发者ID → 点 AppSecret 右侧「生成/重置」后复制。注意:AppSecret 只完整显示一次,需立即保存;属敏感凭证,勿泄露 |
XPAY_OFFER_ID |
真实支付 |
虚拟支付 OfferID |
XPAY_APP_KEY |
真实支付 |
虚拟支付现网 AppKey |
XPAY_NOTIFY_SIGN_KEY |
可选 |
消息推送验签密钥 |
改完 .env 后必须重启服务才生效。
六、启动与验证
6.1 前台启动(测试用)
1
2
| cd /opt/wxminiapp-server
node --experimental-sqlite server.mjs
|
看到 wxminiapp-server on http://0.0.0.0:8787 即成功,Ctrl+C 停止。
6.2 后台常驻启动(推荐,SSH 断开不掉)
1
2
| cd /opt/wxminiapp-server
setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &
|
6.3 验证
1
2
3
4
5
| curl http://127.0.0.1:8787/health
# 期望:{"ok":true,"app":"我的小程序","xpay_mock":true}
curl http://<服务器IP>:8787/health # 内网/公网 IP 验证
curl -o /dev/null -w '%{http_code}\n' http://<服务器IP>:8787/admin # 期望 302
|
七、停止 / 重启服务
先选一种管理方式,不要混用:
- 未配 systemd(当前服务器状态):用本节 setsid 方式;
- 已按第八节配好 systemd:日常一律用
systemctl restart/stop wxminiapp,回到本节用 setsid 会双进程抢端口。
7.1 停止服务
方式一:按 PID
1
2
| ps aux | grep server.mjs | grep -v grep # 找到 PID
kill <PID>
|
方式二:按名字(推荐)
1
| pkill -f 'node --experimental-sqlite server.mjs'
|
方式三:systemd(如果已按第八节配置)
1
| systemctl stop wxminiapp
|
7.2 重启服务
1
2
3
| pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
cd /opt/wxminiapp-server && setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &
sleep 2 && curl -s http://127.0.0.1:8787/health
|
注意:node --experimental-sqlite server.mjs 是 Node 22 的固定写法(SQLite 实验性开关);Node 24+ 可去掉该参数。
八、开机自启动(systemd)
前提:服务器需有 systemd(ps -p 1 显示 systemd 即可,Ubuntu 20.04 默认有)。
当前服务器状态提醒:本机目前服务仍是 setsid 方式运行(无开机自启)。下面的步骤是”切换 + 配置”的完整流程,照做后服务由 systemd 托管:开机自动拉起、崩溃 3 秒后自动重启。
8.1 创建服务文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| cat > /etc/systemd/system/wxminiapp.service <<'EOF'
[Unit]
Description=wxminiapp-server (WeChat MiniApp backend)
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/wxminiapp-server
ExecStart=/usr/local/bin/node --experimental-sqlite server.mjs
Restart=always
RestartSec=3
EnvironmentFile=/opt/wxminiapp-server/.env
StandardOutput=append:/opt/wxminiapp-server/run.log
StandardError=append:/opt/wxminiapp-server/run.log
[Install]
WantedBy=multi-user.target
EOF
|
各字段说明:
| 字段 |
含义 |
ExecStart |
启动命令。必须写 Node 绝对路径,路径用 which node 查出来的实际值填,不要照抄下面的 /usr/local/bin/node: • 官方二进制解压装的 → 一般是 /usr/local/bin/node • nvm 装的 → 一般是 /root/.nvm/versions/node/vXX.X.X/bin/node(见 3.5 节) Node 22 固定带 --experimental-sqlite,Node 24+ 可去掉 |
WorkingDirectory |
工作目录,必须是 /opt/wxminiapp-server(DB_PATH=./data/... 相对路径依赖它) |
EnvironmentFile |
自动注入 .env 全部变量(PORT/WX_APPID/JWT_SECRET 等),与手动 export 等效 |
Restart=always + RestartSec=3 |
进程退出/崩溃后 3 秒自动拉起 |
StandardOutput/Error |
日志追加写入 run.log,与 setsid 方式日志一致 |
.env 里不要给值加引号(systemd 不支持引号包裹),本项目生成格式已满足。
8.2 先停掉旧的 setsid 进程(避免端口冲突)
1
2
| pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
ss -tlnp | grep 8787 || echo "PORT 8787 FREE"
|
如果不先停旧进程,systemctl start 会因端口被占而失败(8787 已被 setsid 进程持有)。
8.3 启用并启动
1
2
3
| systemctl daemon-reload
systemctl enable wxminiapp # 设置开机自启(重启后自动拉起)
systemctl start wxminiapp # 立即启动
|
8.4 验证
1
2
3
4
5
| systemctl status wxminiapp # 期望:active (running)
systemctl is-enabled wxminiapp # 期望:enabled
curl -s http://127.0.0.1:8787/health
# 期望:{"ok":true,"app":"我的小程序","xpay_mock":true}
journalctl -u wxminiapp -n 20 # 看 systemd 视角的启动日志
|
8.5 日常管理命令(systemd 方式)
1
2
3
4
5
| systemctl restart wxminiapp # 重启(改代码/改 .env 后用它)
systemctl stop wxminiapp # 停止
systemctl disable wxminiapp # 取消开机自启(服务仍可手动 start)
systemctl status wxminiapp # 查看运行状态
journalctl -u wxminiapp -f # 实时跟踪日志
|
注意:一旦交给 systemd 管理,日常重启请用 systemctl restart wxminiapp,不要再混用 setsid 启动(会双进程抢端口)。
8.6 排错
| 现象 |
处理 |
systemctl start 报端口占用 |
先 pkill -f 'node --experimental-sqlite server.mjs' 再 start |
start 后立即变 failed |
journalctl -u wxminiapp -n 30 看报错(多为语法错误/路径错误) |
status=203/EXEC 或日志报 No such file or directory |
node 路径不对。which node 查实际路径,改服务文件里的 ExecStart(见 3.5 节) |
| 启动成功但改 .env 不生效 |
必须 systemctl restart wxminiapp |
| 想完全移除 systemd 服务 |
systemctl disable --now wxminiapp && rm /etc/systemd/system/wxminiapp.service |
九、修改端口 / 新增 API
9.1 修改端口
改 .env 里的 PORT:
1
2
| sed -i 's/^PORT=.*/PORT=9000/' /opt/wxminiapp-server/.env
systemctl restart wxminiapp # 或按 7.2 手动重启
|
监听地址已固定在 0.0.0.0(server.mjs 内),改端口即所有网卡生效。注意云服务器需在安全组/防火墙放行新端口。
9.2 新增 API 接口(示例:加一个 GET /api/hello)
编辑 src/index.js,第 1 步:在路由区(约第 120 行 /health 附近)加一行路由:
1
| if (p === '/api/hello' && request.method === 'GET') return apiHello(request, env);
|
第 2 步:在文件里任意位置(建议放在其他 api 函数旁)加处理函数:
1
2
3
| async function apiHello(request, env) {
return json({ ok: true, msg: 'hello from wxminiapp-server', app: env.APP_NAME }, 200);
}
|
第 3 步:语法检查 + 重启 + 验证:
1
2
3
4
5
| node --check src/index.js && echo SYNTAX OK
pkill -f 'node --experimental-sqlite server.mjs'; sleep 1
cd /opt/wxminiapp-server && setsid node --experimental-sqlite server.mjs > run.log 2>&1 < /dev/null &
curl http://127.0.0.1:8787/api/hello
# 期望:{"ok":true,"msg":"hello from wxminiapp-server","app":"我的小程序"}
|
若用 systemd 管理,重启改为 systemctl restart wxminiapp。
9.3 新增 POST 接口(带 JSON 请求体示例)
路由行:
1
| if (p === '/api/echo' && request.method === 'POST') return apiEcho(request, env);
|
处理函数:
1
2
3
4
| async function apiEcho(request, env) {
const body = await request.json().catch(() => ({}));
return json({ ok: true, received: body }, 200);
}
|
测试:
1
| curl -X POST http://127.0.0.1:8787/api/echo -H 'Content-Type: application/json' -d '{"a":1}'
|
十、常见问题排查
| 现象 |
原因 |
解决 |
| 内网 IP 访问不通 |
监听只绑 127.0.0.1 |
server.mjs 中 listen 地址改为 '0.0.0.0',重启 |
SyntaxError |
Node 版本太旧 |
装 Node 22.5+ |
node:sqlite 找不到 |
缺 --experimental-sqlite |
用 node --experimental-sqlite server.mjs |
| 端口被占用 |
有旧进程 |
pkill -f server.mjs 后重启 |
| 后台改配置不生效 |
未重启 |
重启服务(见第七节) |
| 服务进程莫名消失 |
SSH 会话回收 |
用 setsid 启动,或用 systemd(第八节) |
/api/xpay/* 返回 unauthorized |
需登录态/token |
属正常鉴权,先 /api/auth/login |
十一、备份与升级
备份数据库
1
| cp /opt/wxminiapp-server/data/wxminiapp.db /opt/backup-$(date +%F).db
|
定时备份(crontab)
1
2
3
| crontab -e
# 每天 3 点备份,保留 7 天
0 3 * * * cp /opt/wxminiapp-server/data/wxminiapp.db /opt/backup/wxminiapp-$(date +\%F).db && find /opt/backup -name 'wxminiapp-*.db' -mtime +7 -delete
|
升级代码
1
2
| # 重新上传 server.mjs / db.mjs / src/index.js 后:
node --check src/index.js && systemctl restart wxminiapp
|