# 2026-04-29 dev 环境 NEXT_PUBLIC_API_URL 漂移导致登录 404

## 现象

`https://workspace.dev.faradayfuturecn.com/login` 提交登录 → `POST /api/auth/login` 返回 404。CI smoke test 全绿、后端健康检查 200。

## 根因

dev 服务器 `/srv/apps/ffworkspace-dev/.env.dev`：

```
NEXT_PUBLIC_API_URL=https://workspace.dev.faradayfuturecn.com/api   ← 缺 /v1
```

后端 `app.setGlobalPrefix('api/v1')`（`backend/src/main.ts:48`），路由都在 `/api/v1/*`。前端 axios 用这个 env 当 baseURL，build 时把字符串字面量塞进 bundle，于是请求打到 `/api/auth/login` → 后端无此路由 → 404。

## 为什么 CI 漏

`.gitea/workflows/deploy-dev.yml` 的 smoke test：
- `curl /` → 200
- `curl /api/v1/health` → 200（**绝对路径，不经前端 env**）

两条都不依赖 `NEXT_PUBLIC_API_URL`，所以前端 bundle 里 base URL 配错完全测不出来。

## 排查路径

1. `grep -rn 'setGlobalPrefix' backend/src/main.ts` → 确认后端 prefix = `/api/v1`
2. SSH dev 服务器 `grep ^NEXT_PUBLIC_API_URL .env.dev` → 直接看到漂移
3. `curl -X POST /api/v1/auth/login` → 400（路由存在）vs `curl /api/auth/login` → 404 → 确认是 base URL 错位
4. 修完后 `grep -roh 'workspace\.dev\.faradayfuturecn\.com/api[^"]*' frontend/.next/static` → 验证 bundle 里确实有 `/api/v1`

## 修复

```bash
# 改 .env.dev（dev 服务器上）
sed -i 's|/api$|/api/v1|' /srv/apps/ffworkspace-dev/.env.dev
# 重 build frontend（NEXT_PUBLIC_* 是 build-time 注入，必须重 build）
cd /srv/apps/ffworkspace-dev/frontend && npm run build && pm2 reload ffws-dev-frontend --update-env
```

## 可复用知识

### 1. NEXT_PUBLIC_* 是 build-time 注入

改完 `.env.*` 后**必须重新 build frontend**，光 reload PM2 不生效——值已被 webpack 替换为字符串字面量。验证方法：`grep` `frontend/.next/static` 看实际字面量。

### 2. Smoke test 只测后端绝对路径不够

健康检查走 `/api/v1/health` 这种绝对路径绕开了前端 env，所以"后端 health 200" 不代表"用户能登录"。验证前端打的 base URL 至少要：
- 测前端**会打的**路径 `/api/auth/login` 期望 404（如果 env 对，前端不会请求这个路径，但用 curl 主动验证 nginx 没把它兜回 200）
- 测**应当打的**路径 `/api/v1/auth/login` 期望 400/401（路由存在）
- 或直接 grep `.next/static` 验证 bundle 内的 base URL

已开 issue #185 跟进 deploy-{dev,uat,production}.yml 的 smoke test 改进。

### 3. 远程 ssh 跑 Node 命令记得 source nvm

非交互 SSH 不会加载 `~/.nvm/nvm.sh`，直接 `npm`/`pm2` 会找不到或用错版本（系统 Node 18 < Next.js 要求的 20）。正确姿势：

```bash
ssh host 'export NVM_DIR=$HOME/.nvm && . $NVM_DIR/nvm.sh && nvm use 20 && <command>'
```

`bash -lc` 不一定行——取决于 `~/.bashrc` 是否在非交互下也 source nvm。显式 source 最稳。

### 4. deploy.sh 在 git pull 失败时硬退出

Gitea HTTP 504 期间整个 deploy 流程卡死。如果改动只在服务器侧 env、不需要拉新代码，可绕开 deploy.sh 直接手动 `npm run build` + `pm2 reload`——但要确保 `frontend/.env` 软链已指向新 env 文件。

## 风险

dev 是发现的，但 `.env.uat` / `.env.pro` 同样的字段，下一次填 env 时同样可能漂。issue #185 提的 smoke test 改进应该覆盖三个环境。
