# 项目安装

> 源码、.env、依赖、Prisma、管理员与构建

# 7. 上传最终源码

进入：

```text
宝塔 → 文件 → /www/wwwroot/graduate-os
```

上传最终源码 ZIP，解压到当前目录。

最终必须直接看到：

```text
/www/wwwroot/graduate-os/package.json
/www/wwwroot/graduate-os/package-lock.json
/www/wwwroot/graduate-os/app/
/www/wwwroot/graduate-os/components/
/www/wwwroot/graduate-os/lib/
/www/wwwroot/graduate-os/prisma/
/www/wwwroot/graduate-os/.env.example
/www/wwwroot/graduate-os/ecosystem.config.cjs
```

错误：

```text
/www/wwwroot/graduate-os/Graduate-OS/package.json
```

正确：

```text
/www/wwwroot/graduate-os/package.json
```

如果多套一层目录，用宝塔文件管理器把内层文件移动到项目根目录。

---


# 8. 创建运行目录

终端：

```bash
cd /www/wwwroot/graduate-os
mkdir -p storage/papers logs
chmod 750 storage storage/papers logs
```

不要：

```bash
chmod -R 777 storage
```

---


# 9. 创建 `.env`

宝塔文件管理器复制：

```text
.env.example
```

为：

```text
.env
```

也可以：

```bash
cd /www/wwwroot/graduate-os
cp .env.example .env
```

然后在宝塔文件编辑器打开：

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

推荐模板：

```dotenv
DATABASE_URL="mysql://graduate_os:你的数据库密码@127.0.0.1:3306/graduate_os"

# 官方 OpenAI 可留空；如果从网站后台配置 Provider，不必在这里填写真实 Key。
OPENAI_API_KEY=""
OPENAI_MODEL="gpt-5.6"
OPENAI_PDF_DETAIL="high"

# 用于加密数据库中的 AI / SerpApi Key。生成一次后永久保留。
SECRET_ENCRYPTION_KEY="填写64位十六进制字符串"

# 后台配置 SerpApi 时可以留空；这里保留兼容回退。
SERPAPI_API_KEY=""

ADMIN_USERNAME="你的系统管理员用户名"
ADMIN_PASSWORD="首次创建管理员时使用的强密码"

SESSION_SECRET="填写随机长字符串"
APP_URL="https://xjtu.example.com"
UPLOAD_DIR="/www/wwwroot/graduate-os/storage/papers"
MAX_PDF_SIZE_MB="40"

AI_ENABLED="true"
SCHOLAR_ENABLED="true"
RADAR_MAX_RESULTS_PER_KEYWORD="20"

PORT="3000"
BACKUP_DIR="/backup/graduate-os"
```

## 9.1 生成 SESSION_SECRET

```bash
openssl rand -hex 48
```

复制输出到：

```dotenv
SESSION_SECRET="..."
```

## 9.2 生成 SECRET_ENCRYPTION_KEY

```bash
openssl rand -hex 32
```

复制输出到：

```dotenv
SECRET_ENCRYPTION_KEY="..."
```

### 这个值以后不要重新生成

后台保存的：

- OpenAI API Key
- OpenAI Compatible / Sub2API Key
- SerpApi Key

会使用它加密。更换该密钥会导致已经保存的密钥无法解密。

## 9.3 `true` 拼写必须正确

正确：

```dotenv
AI_ENABLED="true"
SCHOLAR_ENABLED="true"
```

错误：

```dotenv
AI_ENABLED="ture"
SCHOLAR_ENABLED="ture"
```

`ture` 会被程序视为未启用。

## 9.4 权限

```bash
cd /www/wwwroot/graduate-os
chmod 600 .env
```

---


# 10. 安装 Node 依赖

```bash
cd /www/wwwroot/graduate-os
npm ci
```

最终整合包已经把真实部署中缺失过的：

```text
server-only@0.0.1
```

正式写入 `package.json` 与 `package-lock.json`，因此新部署**不需要再手动 `npm install server-only`**。

检查：

```bash
npm ls server-only --depth=0
```

应看到：

```text
server-only@0.0.1
```

如果 `npm ci` 只出现 `npm WARN`，通常不是失败；真正失败会出现 `npm ERR!`。

---


# 11. 初始化 Prisma 数据库

执行：

```bash
cd /www/wwwroot/graduate-os
npx prisma generate
npx prisma migrate deploy
npx prisma migrate status
```

当前最终包包含：

```text
20260824000000_init
20260826000000_phase2_task1_ai_provider
20260826010000_phase2_task2_search_provider
20260826020000_phase2_task4_interview_link
20260826030000_phase2_task5b_question_analysis
20260826040000_phase2_task6_question_practice
```

最后应看到数据库 schema 已是最新。

## 常见数据库错误

### P1000 / Authentication failed

数据库账号或密码错误。

检查 `.env`：

```dotenv
DATABASE_URL="mysql://graduate_os:正确密码@127.0.0.1:3306/graduate_os"
```

### P1001

MySQL 未启动、地址/端口错误。

### P1045

数据库用户名或密码错误。

**绝对不要为了解决连接错误执行：**

```bash
npx prisma migrate reset
npx prisma db push --force-reset
```

---


# 12. 创建唯一管理员

执行：

```bash
cd /www/wwwroot/graduate-os
npm run admin:create
```

创建成功后，在 `.env` 中把：

```dotenv
ADMIN_PASSWORD=""
```

清空，避免首次密码长期留在配置中。

系统不开放注册，只有一个管理员。

---


# 13. 构建 standalone

```bash
cd /www/wwwroot/graduate-os
npm run typecheck
npm run lint
npm run build
```

`npm run build` 已包含：

```text
Prisma generate
Next.js build
standalone 资源整理
public/static 同步
```

检查：

```bash
test -f .next/standalone/server.js && echo "server OK"
test -d .next/standalone/.next/static && echo "static OK"
test -d .next/standalone/public && echo "public OK"
```

应该看到：

```text
server OK
static OK
public OK
```

---
