光隙第一版部署文档

设计

光隙博客 — 完整部署文档

版本 1.0.0 | 2026-07-20 | Next.js 16 standalone + PM2 + Nginx


目录

1. 部署概览

架构图

浏览器 (HTTPS)
    │
    ▼
┌─────────────────────────────────┐
│  Nginx (:443)                   │
│  /storage/* → 静态文件直接读取    │
│  /_next/static/* → 代理 + 缓存   │
│  / → proxy_pass 127.0.0.1:3000 │
└────────────┬────────────────────┘
             │
             ▼
┌─────────────────────────────────┐
│  PM2 → node server.js (:3000)   │
│  Next.js 16 standalone 模式      │
│  ┌─────┐ ┌──────┐ ┌──────────┐ │
│  │前台  │ │后台   │ │ API 路由  │ │
│  └──┬──┘ └──┬───┘ └────┬─────┘ │
│     └───────┼───────────┘       │
│        ┌────▼────┐              │
│        │ Prisma   │              │
│        └────┬────┘              │
│        ┌────▼────┐              │
│        │ SQLite  │              │
│        └─────────┘              │
└─────────────────────────────────┘

核心策略

原则说明
本地构建,服务器只运行2G 内存跑 next build 会 OOM
standalone 模式自包含部署包,解决 Windows → Linux 跨平台兼容
一键打包脚本powershell -File scripts/pack.ps1 全自动
Nginx 静态托管/storage/ 图片直接读盘,不经过 Node.js
PM2 内存保护500MB 上限自动重启

命令速查

场景命令
本地打包powershell -File scripts/pack.ps1
上传scp guangxi-deploy.tar.gz root@IP:/var/www/guangxi/
服务器首次部署第4章
服务器更新第5章

2. 服务器环境准备

目标:阿里云 2核2G / Alibaba Cloud Linux 3.2104 LTS 64位 / 已装宝塔面板

2.1 安装 Node.js 20 LTS

宝塔 → 软件商店 → 搜索 "Node.js版本管理器" → 安装 → 选择 v20 LTS

2.2 安装 PM2

npm install -g pm2

2.3 创建应用目录结构

# 一次性全部创建
mkdir -p /var/www/guangxi/app/{data,logs,storage/{originals,display,thumbs}}
chmod -R 755 /var/www/guangxi/app/storage
chmod -R 755 /var/www/guangxi/app/data

2.4 配置环境变量

这一步必须在 prisma 操作前完成,否则 DATABASE_URL 读不到会报错。

cd /var/www/guangxi/app

# 生成强随机密钥
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

创建 .env(替换 <密钥><域名>):

cat > .env << 'EOF'
DATABASE_URL="file:./data/prod.db?connection_limit=1&timeout=5000"
SESSION_SECRET="<粘贴上一步生成的64位十六进制字符串>"
UPLOAD_DIR="./storage"
NEXT_PUBLIC_SITE_URL="https://你的域名"
EOF

3. 本地一键打包

在 Windows 电脑上执行。每次改完代码只需这一条命令。

powershell -File scripts/pack.ps1

脚本自动完成以下操作:

步骤操作
1. 构建npm run build
2. 整理.next/standalone/ 提取 server.js / node_modules / .next
3. 补充复制 .next/static、public、prisma、scripts、ecosystem.config.js、package.json
4. 修正ecosystem.config.js 中的 script 路径改为 server.js
5. 清理删除本地 prod.db
6. 打包生成 guangxi-deploy.tar.gz

打包产物结构

guangxi-deploy.tar.gz
├── server.js              # standalone 入口(node server.js 直接启动)
├── node_modules/          # 最小运行依赖(含 Prisma Linux 引擎)
├── .next/
│   ├── static/            # CSS/JS 静态资源
│   └── server/            # 服务端 chunks
├── ecosystem.config.js    # PM2 配置(script: "server.js")
├── package.json           # 依赖清单
├── prisma/
│   └── schema.prisma      # 数据模型 + binaryTargets
├── scripts/
│   ├── seed.ts            # 种子脚本
│   └── init.ts            # 交互式初始化
└── public/                # 静态文件

4. 首次部署

4.1 上传

# Windows 本地执行
scp guangxi-deploy.tar.gz root@你的IP:/var/www/guangxi/

4.2 服务器解压

cd /var/www/guangxi/app
tar -xzf ../guangxi-deploy.tar.gz

4.3 生成 Prisma Linux 引擎

关键步骤:Windows 构建的包中 Prisma 引擎不完整,必须在服务器上重新生成。

cd /var/www/guangxi/app
npx prisma generate

4.4 初始化数据库

# 创建表结构
npx prisma db push

# 种子数据:管理员账号 + 默认设置 + 默认分类
npx tsx scripts/seed.ts

种子脚本创建的默认账号:admin / admin123首次登录后立即修改密码

4.5 PM2 启动

cd /var/www/guangxi/app
pm2 start ecosystem.config.js
pm2 save
pm2 startup   # 复制输出的命令执行,实现开机自启

验证:

curl http://127.0.0.1:3000   # 返回 HTML 即正常
pm2 status                    # 应显示 online

5. 后续更新部署

每次改完代码后执行,保留数据库、图片、日志和环境变量

5.1 本地打包 + 上传

# Windows 本地(一条命令)
powershell -File scripts/pack.ps1
# 上传
scp guangxi-deploy.tar.gz root@你的IP:/var/www/guangxi/

5.2 服务器更新

cd /var/www/guangxi/app

# 停止服务
pm2 stop guangxi-blog

# 删除旧代码(保留 data/ storage/ logs/ .env)
rm -rf server.js node_modules .next ecosystem.config.js package.json prisma scripts public

# 解压新包
tar -xzf ../guangxi-deploy.tar.gz

# 重新生成 Prisma Linux 引擎
npx prisma generate

# 如果 schema 有变更,同步数据库
# npx prisma db push

# 启动
pm2 start ecosystem.config.js

# 验证
curl http://127.0.0.1:3000

5.3 常用 PM2 命令

命令说明
pm2 status查看进程状态
pm2 logs guangxi-blog查看实时日志
pm2 logs guangxi-blog --err只看错误日志
pm2 restart guangxi-blog重启
pm2 stop guangxi-blog停止
pm2 delete guangxi-blog从列表删除

6. Nginx 站点配置

6.1 创建站点

宝塔 → 网站 → 添加站点 → 填入你的域名

宝塔会自动申请 Let's Encrypt SSL 证书。

6.2 修改配置文件

宝塔 → 网站 → 点击域名 → 配置文件

完整替换(替换 你的域名 为实际域名):

server {
    listen 80;
    server_name 你的域名;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name 你的域名;

    ssl_certificate     /www/server/panel/vhost/cert/你的域名/fullchain.pem;
    ssl_certificate_key /www/server/panel/vhost/cert/你的域名/privkey.pem;

    client_max_body_size 50m;

    # 图片由 Nginx 直接读盘,不经过 Node.js
    location /storage/ {
        alias /var/www/guangxi/app/storage/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Next.js 静态资源长缓存
    location /_next/static/ {
        proxy_pass http://127.0.0.1:3000;
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    # 其他请求代理到 Next.js
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

7. 验证部署

# 1. 进程状态
pm2 status
# → guangxi-blog | online

# 2. 本地响应
curl http://127.0.0.1:3000
# → 200 + 完整 HTML

# 3. 错误日志
pm2 logs guangxi-blog --lines 20 --err

# 4. 浏览器访问
# https://你的域名          → 首页
# https://你的域名/admin    → 后台
# https://你的域名/notes    → 学习记录

验收清单

  • pm2 status 显示 online
  • curl http://127.0.0.1:3000 返回 200
  • 首页正常显示(雪山背景 + 个人卡片 + 三模块入口)
  • 后台 /admin 可登录
  • 后台 → 站点设置 → 修改密码 功能正常
  • 上传照片后 /storage/ 路径可直接浏览器访问
  • 日常感悟 /thoughts 未登录跳转登录页
  • 邀请码注册流程正常
  • pm2 logs 无持续报错

8. 故障排查

8.1 应用无法启动 / 页面报错

pm2 logs guangxi-blog --lines 50 --err
# 或手动运行看完整报错
cd /var/www/guangxi/app && node server.js

8.2 Prisma 引擎报错

Prisma Client could not locate the Query Engine for runtime "rhel-openssl-1.1.x"

原因:Windows 构建时未下载 Linux 引擎。

解决

cd /var/www/guangxi/app
npx prisma generate
pm2 restart guangxi-blog

8.3 npm run build 本地 OOM

Next.js 构建在 Windows 上偶尔也会内存不足。

解决:关闭其他应用释放内存,或在 WSL 中构建。

8.4 SQLITE_BUSY

SQLite 写入并发冲突。

pm2 restart guangxi-blog   # 释放连接即可

8.5 图片上传 413

Nginx client_max_body_size 不够大。检查是否设为 50m

Cookies can only be modified in a Server Action or Route Handler

auth.ts 中的 getCurrentUser() 已改为纯只读,不会触发此错误。如果仍然出现,检查是否有其他页面在渲染时调用了 session 写入操作。


9. 附录

A. 服务器完整目录结构

/var/www/guangxi/
  app/                          # 应用根目录
    server.js                   # standalone 入口
    node_modules/               # 运行依赖
    .next/                      # Next.js 构建产物
      BUILD_ID
      static/                   # CSS/JS 资源
      server/                   # 服务端 chunks
    ecosystem.config.js         # PM2 配置
    package.json
    prisma/schema.prisma        # 数据模型
    scripts/seed.ts             # 种子脚本
    public/                     # 静态文件
    .env                        # 环境变量(不提交 Git,不被更新覆盖)
    data/prod.db                # SQLite 数据库(不被更新覆盖)
    storage/                    # 图片(不被更新覆盖)
      originals/YYYY/MM/        # 原图(不公开)
      display/YYYY/MM/          # 展示图(Nginx 托管)
      thumbs/YYYY/MM/           # 缩略图(Nginx 托管)
    logs/                       # PM2 日志(不被更新覆盖)

不会被更新覆盖的目录data/ storage/ logs/ .env

B. Prisma binaryTargets

generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "rhel-openssl-1.1.x", "rhel-openssl-3.0.x"]
}
目标系统
nativeWindows 开发机
rhel-openssl-1.1.x阿里云 Linux 3 (OpenSSL 1.1)
rhel-openssl-3.0.x新版 Linux (OpenSSL 3.0)

服务器上必须执行 npx prisma generate 才能生成 Linux 引擎。

C. .gitignore

node_modules/
.next/
data/
storage/
*.db
*.db-journal
.env
.env.local
.env.production