# PM2、Nginx 与双层保护

> standalone、环境变量、IPv4、反向代理与 Basic Auth

# 14. PM2 启动

最终版项目根目录已经自带：

```text
ecosystem.config.cjs
```

它已经整合真实部署修复：

```text
node --env-file=/www/wwwroot/graduate-os/.env
NODE_OPTIONS=--dns-result-order=ipv4first
```

第一次启动：

```bash
cd /www/wwwroot/graduate-os
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup
```

`pm2 startup` 会打印一条命令，再复制执行一次，然后：

```bash
pm2 save
```

验证：

```bash
pm2 status
pm2 logs graduate-os --lines 100
curl -I http://127.0.0.1:3000/login
```

必须使用：

```text
node .next/standalone/server.js
```

不要改成：

```text
next start
```

因为 `output: "standalone"` 使用 `next start` 会产生错误。

---


# 15. 为什么 PM2 要显式加载 `.env`

真实部署过程中出现过：

```text
请先配置 SECRET_ENCRYPTION_KEY
```

即使项目根 `.env` 已经填写。

原因是 standalone 运行时不能依赖“碰巧自动读取”项目根 `.env`。

最终 `ecosystem.config.cjs` 已内置：

```text
--env-file=/www/wwwroot/graduate-os/.env
```

所以**不要在部署完成后删除这项**。

---


# 16. 为什么最终版优先 IPv4

真实测试中：

```text
IPv6 → zzshu.cc / Cloudflare → HTTP 403 Blocked
IPv4 → 同一 API → HTTP 200 / OK
```

因此 PM2 已内置：

```text
NODE_OPTIONS=--dns-result-order=ipv4first
```

这不是 API Key 修复，而是第三方 API 网络兼容修复。

如果未来使用其他 OpenAI Compatible 服务，也可以保留该设置。

---


# 17. 配置 Nginx 反向代理

最简单方式：

```text
宝塔 → 网站 → 你的域名 → 反向代理 → 添加反向代理
```

填写：

```text
名称：graduate-os
目标 URL：http://127.0.0.1:3000
发送域名：$host
缓存：关闭
```

然后在反向代理高级配置确认至少有：

```nginx
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;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off;
```

## 私有 PDF 保护

站点中必须保证：

```nginx
location ^~ /storage/ {
    deny all;
    return 404;
}
```

如果宝塔反代已经生成 `location /`，不要再在同一 `server` 内重复写第二个 `location /`。

项目里的 `NGINX.conf.example` 是完整参考，不建议在宝塔已自动管理 SSL 时整段替换站点文件。

---


# 18. 外层 Basic Auth

推荐使用宝塔自带：

```text
网站 → 访问限制 / 访问控制 → 密码访问
```

用户名可以自定义，不必叫 `graduate_outer`。

如果面板版本没有该功能，可以手工：

```bash
apt update
apt install -y apache2-utils
mkdir -p /www/server/pass
htpasswd -c /www/server/pass/graduate-os.htpasswd 你想要的外层用户名
chmod 640 /www/server/pass/graduate-os.htpasswd
```

然后在合适的 Nginx 访问区域使用：

```nginx
auth_basic "Graduate OS Private";
auth_basic_user_file /www/server/pass/graduate-os.htpasswd;
```

### 更换外层用户名

最简单重新创建：

```bash
htpasswd -c /www/server/pass/graduate-os.htpasswd 新用户名
```

然后重载 Nginx。

外层 Basic Auth 和 Graduate OS `/login` 管理员密码必须使用不同密码。

---


# 19. 第一次进入后台

浏览器：

```text
https://xjtu.example.com
```

正确顺序：

```text
外层 Basic Auth
  ↓
Graduate OS /login
  ↓
后台
```

---
