Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ WEBHOOK_SECRET=changeme_to_random_webhook_secret
# CORS 配置
# ============================================

# 允许的源 (逗号分隔)
CORS_ORIGINS=http://localhost:3000
# 允许的源 (逗号分隔,支持多个可信域名或 * )
CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000

# 允许的方法
CORS_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS
Expand All @@ -169,19 +169,19 @@ CORS_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS
# LLM 服务配置
# ============================================

# LLM 提供商 (openai, anthropic, deepseek, etc.)
# LLM 提供商(用于初始化默认 LLM 配置;更多配置可在设置页新增和切换)
LLM_PROVIDER=openai

# LLM API 密钥
# LLM API 密钥(用于初始化默认 LLM 配置)
LLM_API_KEY=your_llm_api_key

# LLM 模型名称
# LLM 模型名称(用于初始化默认 LLM 配置)
LLM_MODEL=gpt-4

# LLM API 端点 (如果使用自定义端点)
# LLM API 端点如果使用自定义端点,用于初始化默认 LLM 配置)
LLM_API_BASE_URL=

# LLM 最大重试次数
# LLM 最大重试次数(用于初始化默认 LLM 配置)
LLM_MAX_RETRIES=3

# ============================================
Expand Down
43 changes: 43 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CodaGraph-lite Project Memory

## Required Workflow

- Reproduce the exact bug on the current branch before changing code.
- For interaction bugs, inspect browser behavior first: focus, active element, DOM replacement, network request, and backend log.
- Do not claim a fix based on a different branch, worktree, or prior patch. Verify the current branch contains the change.
- Do not report "fixed" until the same user path has been re-tested in the browser or with a direct request.

## Known Failure Modes

### Settings Password Inputs

- Symptom: on `/dashboard/settings`, the admin password fields only accept one character, then lose focus or appear to clear.
- Real root cause: helper components defined inside `SettingsPage()` can cause subtree remounts on every `setPasswordForm`.
- First things to check:
- whether local helper components are declared inside the page component
- whether the active input node/id changes after one keystroke
- whether password form updates use functional state updates
- Current fix location: `web/app/dashboard/settings/page.tsx`

### Failed Login Returning 500

- Symptom: wrong admin password returns `500 {"details":"FOREIGN KEY constraint failed"}` instead of `401`.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 文档中暴露了内部错误信息格式,可能被用于探测系统

错误消息 {"details":"FOREIGN KEY constraint failed"} 暴露了数据库约束细节和内部表关系。攻击者可利用此信息:(1) 确认用户账号是否存在(返回500而非401);(2) 推断数据库表结构。虽然这是对现有 bug 的文档化记录,但文档本身无需包含完整的 JSON 响应体示例。

建议:将 L24 改为:'Symptom: wrong admin password returns 500 with internal database error details instead of 401.' 移除具体的 JSON 响应体示例,避免在文档中固化敏感的错误响应格式。

- Real root cause: failed login activity log attempted to insert an invalid `admin_id`.
- First things to check:
- `POST /api/auth/login` response code
- backend log stack under `server/src/auth/routes.ts`
- `server/src/models/ActivityLog.ts`
- Current fix locations:
- `server/src/auth/routes.ts`
- `server/src/models/ActivityLog.ts`

### Local Frontend / Backend Validation

- Prefer `http://localhost:3000`, not `http://127.0.0.1:3000`, when validating the real app flow.
- Reason: the backend CORS configuration commonly allows `http://localhost:3000`; using `127.0.0.1` can create a false negative during login and settings verification.

## Response Discipline

- Separate symptom from root cause.
- Fix one proven root cause at a time.
- If a patch only addresses an adjacent issue, do not present it as the root fix.
17 changes: 11 additions & 6 deletions deploy/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,19 @@ main() {
((attempt++))
sleep 2

# 检查前端
if curl -sf http://localhost:3000/api/health >/dev/null 2>&1; then
log_success "前端健康检查通过"
break
local frontend_ok=false
local backend_ok=false

if curl -sf "http://localhost:${FRONTEND_PORT}/api/health" >/dev/null 2>&1; then
frontend_ok=true
fi

# 检查后端
if curl -sf http://localhost:7900/api/health >/dev/null 2>&1; then
if curl -sf "http://localhost:${BACKEND_PORT}/health" >/dev/null 2>&1; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL · 后端健康检查路径与实际 API 不匹配

后端健康检查端点从 /api/health 改为了 /health。这会导致健康检查永远失败,除非后端 server 确实暴露 /health 端点而非 /api/health。语义上下文显示后端启动命令为 BACKEND_PORT=7901 npm run start:server,需确认后端路由是否支持 /health 路径。

建议:确认 server/dist 中的路由实现。如果后端健康检查端点仍是 /api/health,应将 L134 改为:if curl -sf "http://localhost:${BACKEND_PORT}/api/health" >/dev/null 2>&1; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL · 后端健康检查端点被静默修改,可能导致部署失败

diff 显示后端健康检查端点从 /api/health 变更为 /health。这是一个破坏性变更——如果后端服务没有同步实现 /health 端点,健康检查将持续失败,导致部署超时退出。当前 diff 中没有看到后端代码同步修改的证据。

建议:确认后端服务是否已实现 /health 端点。如果后端仍在使用 /api/health(或其他路径),请将 L134 改回 curl -sf "http://localhost:${BACKEND_PORT}/api/health" 或确保前后端健康检查端点保持一致。也可考虑同时检查两个端点以实现平滑迁移。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 后端健康检查端点与原配置不一致

健康检查端点从 /api/health 变更为 /health。旧代码检查的是 /api/health,新代码检查 /health。这可能导致健康检查失败,如果后端服务实际只暴露了 /api/health 端点。

建议:确认 server/dist 的实际健康检查路由。如果 server 同时暴露两个端点则无问题,否则需将端点改回 /api/health 或同步更新 server 的路由配置。

backend_ok=true
fi

if [ "$frontend_ok" = true ] && [ "$backend_ok" = true ]; then
log_success "前端健康检查通过"
log_success "后端健康检查通过"
break
fi
Expand Down
6 changes: 4 additions & 2 deletions deploy/systemd/codagraph-lite-frontend.service
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ After=network.target
Wants=network.target

[Service]
Type=notify
Type=simple
NotifyAccess=all
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit switches to Type=simple but keeps NotifyAccess=all. NotifyAccess is only relevant for notify-based services, so this is likely a leftover and may produce confusing configuration warnings. Either remove NotifyAccess or keep Type=notify and ensure the process actually sends readiness notifications.

Suggested change
NotifyAccess=all

Copilot uses AI. Check for mistakes.
WorkingDirectory=/opt/codagraph-lite/web

# 环境变量
Environment="NODE_ENV=production"
Environment="NODE_OPTIONS=--max-old-space-size=200"
Environment="PORT=3000"
Environment="HOSTNAME=0.0.0.0"

# 启动命令
ExecStart=/usr/bin/node /opt/codagraph-lite/web/node_modules/.bin/next start
ExecStart=/usr/bin/node /opt/codagraph-lite/web/node_modules/next/dist/bin/next start -p 3000 -H 0.0.0.0
ExecReload=/bin/kill -s HUP $MAINPID

# 2u2g 资源限制
Expand Down
2 changes: 1 addition & 1 deletion deploy/verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ check_api_health() {
check
log_info "检查 API 健康状态..."

local backend_url="http://localhost:${BACKEND_PORT:-7900}/api/health"
local backend_url="http://localhost:${BACKEND_PORT:-7900}/health"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 健康检查端点路径可能与服务端实际端点不匹配

将健康检查端点从 /api/health 改为 /health,但根据 docs/api.md:205 中 API 版本路径为 /api/v1/,且服务端路由 (server/src/auth/routes.ts:8) 使用 /api/auth/verify 前缀模式,如果服务端健康检查实际端点不是 /health,此改动将导致 verify.sh 无法正确检测后端健康状态,使得部署验证失败。

建议:确认服务端实际提供的健康检查端点路径。如果服务端使用统一 /api 前缀,应使用 /api/health;如果使用 /api/v1 版本,应使用 /api/v1/health;如果服务端确实提供无前缀的 /health,则此改动正确。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 健康检查端点路径变更需确认服务器端实现

健康检查路径从 /api/health 改为 /health。根据语义上下文,server/src/auth/routes.ts 定义了 /api/auth/verify 路由,但未找到 /api/health/health 端点的服务器端定义。如果服务器未实现 /health 端点,部署验证将持续失败。

建议:确认服务器端健康检查端点的实际路径。如果服务器使用 Express,通常在入口文件中有 app.get('/health', ...)app.get('/api/health', ...) 的路由定义。建议在修改前验证目标路径在服务器端确实存在。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · API 端点路径变更需与后端配置对齐

健康检查端点从 /api/health 变更为 /health。从 Impact references 可见,系统存在多个 API 端点(如 /api/v1/auth/verify/api/auth/verify),且使用 /api/ 前缀。如果后端健康检查实际仍为 /api/health,此变更将导致健康检查永远失败,进而导致部署验证流程中断。

建议:确认后端健康检查端点的实际路径。如果后端同时支持两路径则无问题;如果只支持 /api/health,需回退此变更或同时更新后端路由配置。建议在变更前验证后端实际暴露的端点。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 健康检查端点路径变更可能导致验证失败

将健康检查端点从 /api/health 改为 /health。根据语义上下文,docs/api.md 定义的 API 路径为 /api/v1/auth/verify,前端 API 客户端调用的是 /api/auth/verify。如果后端服务将所有 /api/* 路径作为业务 API 统一管理,健康检查可能实际由 /api/health 提供而非 /health。此变更可能导致部署验证脚本无法正确检测后端服务状态。

建议:确认后端实际提供的健康检查端点路径。如果使用路径前缀 /api 统一管理服务,应保持 /api/health。可通过检查 server 代码中的路由配置(如 Express/ Gin 的 health 路由定义)确认。

local frontend_url="http://localhost:${FRONTEND_PORT:-3000}"

# 检查后端 API
Expand Down
6 changes: 4 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ WORKSPACE_ROOT=/opt/codagraph-lite/workspace

### LLM 提供商

这些环境变量会初始化后台里的默认 LLM 配置。需要多套 LLM API 时,可以在设置页继续新增、保存并切换。

| 环境变量 | 默认值 | 可选值 |
|-----------|---------|--------|
| `LLM_PROVIDER` | `openai` | `openai`, `anthropic`, `deepseek`, `自定义` |
Expand Down Expand Up @@ -611,9 +613,9 @@ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
**配置示例**:
```bash
# 开发环境
CORS_ORIGINS=http://localhost:3000
CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · CORS 配置示例同时包含 localhost 和 127.0.0.1 可能导致认证问题

AGENTS.md:37 指出 'using 127.0.0.1 can create a false negative during login and settings verification'。虽然 localhost 和 127.0.0.1 指向同一台机器,但浏览器可能将它们视为不同的 origin(域名 origin 的判断依赖字符串比较),这可能导致 cookie/session 无法正确共享,造成认证失败。文档明确建议两者都包含,但未警告潜在风险。

建议:在开发环境配置示例后添加说明,或改为只推荐使用 localhost。例如:'注意:如果登录或设置验证遇到问题,尝试只使用 localhost 而不包含 127.0.0.1'。


# 生产环境(多个域名
# 生产环境(多个可信域名
CORS_ORIGINS=https://app1.com,https://app2.com

# 允许所有(不推荐生产环境)
Expand Down
18 changes: 13 additions & 5 deletions e2e/dashboard-pages.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';

const seededRepositoryName = 'codagraph-lite-e2e-fixture';

test.beforeAll(() => {
execSync('node scripts/seed-e2e-data.js', { stdio: 'inherit' });
});
Expand All @@ -19,9 +21,10 @@ test('repositories page renders seeded repository', async ({ page }) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 页面错误监听器注册时机晚于页面导航

在第20行 page.on('pageerror', ...) 注册错误监听器后,第22行才执行 page.goto('/dashboard/repositories')。如果页面加载过程中立即抛出错误(如 JS 执行初始化脚本失败),此时错误监听器可能尚未完全就绪,导致页面错误无法被捕获,测试无法正确验证页面稳定性。jobs 和 history 测试存在相同问题(第34行注册、L35导航;第47行注册、L49导航)。

建议:将 page.on('pageerror', ...) 调用移至 beforeEach 中,在任何 page.goto 之前统一注册一次。或者在每个测试的开头、第一行就注册监听器,再进行导航。

await page.goto('/dashboard/repositories');
await expect(page.getByRole('heading', { name: '仓库管理' })).toBeVisible();
await expect(page.getByText('mars167')).toBeVisible();
await expect(page.getByText('codagraph-lite')).toBeVisible();
await expect(page.getByText('已连接', { exact: true })).toBeVisible();
await page.getByRole('textbox', { name: 'Search' }).fill(`mars167/${seededRepositoryName}`);
await expect(page.getByText('搜索命中 1', { exact: true })).toBeVisible();
await expect(page.getByRole('link', { name: seededRepositoryName, exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: '开启 Watch' })).toBeVisible();
expect(pageErrors).toEqual([]);
});

Expand All @@ -31,7 +34,11 @@ test('jobs page renders seeded job stats and row', async ({ page }) => {

await page.goto('/dashboard/jobs');
await expect(page.getByRole('heading', { name: '作业状态' })).toBeVisible();
await expect(page.getByText('analyze_pr')).toBeVisible();
await expect(
page.getByRole('link', {
name: new RegExp(`Job #\\d+ · ${seededRepositoryName} · PR #\\d+`),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 正则表达式硬编码 Job 链接格式,脆弱且无回退断言

正则表达式 Job #\d+ · ${seededRepositoryName} · PR #\d+ 假设前端 Job 链接的文本格式完全匹配。如果前端 UI 文本发生微小变化(如分隔符从 · 改为 -、顺序调整、翻译变更),测试会失败,即使后端数据和核心功能正常。这违反了测试金字塔原则——过度依赖 UI 文本细节使测试变得脆弱。

建议:增加一个或多个不依赖精确文本格式的断言,例如检查链接元素存在且 href 指向正确的端点:expect(page.locator('a[href*="/jobs/"]')).toBeVisible()。将正则匹配降级为可选的辅助断言。

})
).toBeVisible();
expect(pageErrors).toEqual([]);
});

Expand All @@ -40,7 +47,8 @@ test('history page renders seeded analysis entry', async ({ page }) => {
page.on('pageerror', (error) => pageErrors.push(error.message));

await page.goto('/dashboard/history');
await expect(page.getByRole('heading', { name: '分析历史' })).toBeVisible();
await expect(page.getByRole('heading', { name: '按 PR 维度回看 Review 历史' })).toBeVisible();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 页面标题变更需确认 UI 同步更新

history 页面的标题从 '分析历史' 改为 '按 PR 维度回看 Review 历史'。这是一个用户可见的文本变更,如果后端代码已更新但前端页面尚未同步此文本,测试将失败。需确认这是预期的前端 UI 变更。

建议:确认 scripts/seed-e2e-data.js 或相关前端组件是否同步更新了标题文本,确保 PR 变更与 UI 代码变更一并部署。

await expect(page.getByRole('link', { name: `mars167/${seededRepositoryName}` })).toBeVisible();
await expect(page.getByText('E2E validation PR')).toBeVisible();
expect(pageErrors).toEqual([]);
});
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
"build:web": "npm --prefix web run build",
"build:server": "npm --prefix server run build",
"build": "npm run build:web && npm run build:server",
"check:web": "npm --prefix web run lint",
"check:server": "npm --prefix server run typecheck",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · check:server 脚本依赖未验证的子项目配置

check:server 调用 server 目录下的 typecheck 命令,但未确认 server/package.json 中是否存在 typecheck 脚本。如果 server 项目未配置 TypeScript 类型检查,此命令将失败。

建议:在添加此脚本前,确保 server/package.json 中已正确定义 'typecheck' 脚本(如 tsc --noEmit)。

"check": "npm run check:web && npm run check:server && npm run build",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · check 脚本的执行顺序可能导致资源浪费

check 脚本按顺序执行 check:web && check:server && npm run build。如果 lint/typecheck 失败,后续的 build 仍会被尝试执行,浪费构建时间。通常 lint/typecheck 应该在 build 之前运行,以便尽早发现问题。

建议:考虑将 check 脚本改为 npm run check:web && npm run check:server(去掉 build),将 build 交给单独的 build 命令。或者,如果确实需要 check + build 的组合,应在 check 失败时立即退出,不执行 build。

"start:web": "npm --prefix web run start",
"start:server": "npm --prefix server run start",
"start": "concurrently \"npm run start:web\" \"npm run start:server\"",
"seed:e2e": "node scripts/seed-e2e-data.js",
"e2e:playwright": "npm run seed:e2e && playwright test e2e/dashboard-pages.spec.ts",
"e2e:puppeteer": "npm run seed:e2e && node scripts/puppeteer-dashboard-smoke.js"
"e2e:puppeteer": "npm run seed:e2e && node scripts/puppeteer-dashboard-smoke.js",
"test:e2e": "NEXT_PUBLIC_API_URL=http://127.0.0.1:7901 npm run build && npm run e2e:playwright"
},
"repository": {
"type": "git",
Expand Down
16 changes: 15 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ export default defineConfig({
testDir: './e2e',
timeout: 30_000,
use: {
baseURL: 'http://localhost:3000',
baseURL: 'http://127.0.0.1:3001',
headless: true,
},
webServer: [
{
command: 'SKIP_REMOTE_REPOSITORY_SYNC=1 BACKEND_PORT=7901 FRONTEND_PORT=3001 CORS_ORIGINS=http://127.0.0.1:3001 npm run start:server',
url: 'http://127.0.0.1:7901/api/health',
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
{
command: 'cd web && mkdir -p .next/standalone/.next && if [ -d .next/static ]; then ln -sfn ../../static .next/standalone/.next/static; fi && PORT=3001 HOSTNAME=127.0.0.1 NEXT_PUBLIC_API_URL=http://127.0.0.1:7901 node .next/standalone/server.js',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 前端服务器启动命令链过长,单点故障难以诊断

webServer 命令使用 && 链式执行 5 个操作:mkdir、if 条件判断、ln 命令、PORT/HOSTNAME/NEXT_PUBLIC_API_URL 设置、node 启动。如果任一步骤失败(如 standalone 构建缺失、文件权限问题、目录不存在),Playwright 仅报告超时,开发者需要手动运行命令才能定位具体故障点。这会显著增加调试成本,特别是在 CI 环境中。

建议:考虑拆分为两步:(1) 在项目根目录添加 scripts/start-frontend-for-e2e.ts 脚本,负责预检查和准备逻辑;(2) webServer command 改为调用该脚本,使失败信息更明确。或将关键步骤的错误输出捕获并显示。

url: 'http://127.0.0.1:3001/api/health',
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
],
});
73 changes: 61 additions & 12 deletions scripts/seed-e2e-data.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
const path = require('path');
const Database = require('../server/node_modules/better-sqlite3');
const crypto = require('crypto');
const Database = require(require.resolve('better-sqlite3', {
paths: [path.join(__dirname, '..', 'server')],
}));

const dbPath = path.join(__dirname, '..', 'server', 'data', 'codagraph-lite.db');
const db = new Database(dbPath);
// Keep fixture data ahead of existing rows so the e2e smoke tests can locate it deterministically.
const seededTimestamp = "datetime('now', 'localtime', '+1 day')";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 未来时间戳可能导致测试断言不稳定

seededTimestamp 使用 datetime('now', 'localtime', '+1 day') 设置为未来1天的时间。如果E2E测试中有任何依赖当前时间的断言(如检查token是否过期、判断记录是否'最近'创建等),这些测试可能因为时间差而失败。特别是 oauth_installations.token_expires_at 字段虽然当前未填充值,但若应用代码未来依赖该字段与当前时间做比较,测试数据将无法覆盖这些场景。

建议:考虑使用 datetime('now', 'localtime') 获取当前时间戳,仅在明确需要测试'过期'或'未来'场景时才使用固定偏移。如果必须使用未来时间,建议在脚本顶部添加注释说明原因,并确保相关测试明确处理时间边界情况。

const seededAdminPasswordHash = crypto
.createHash('sha256')
.update('changeme')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 硬编码测试凭证存在意外泄露风险

admin 密码和 OAuth token 使用硬编码字符串('changeme'、'e2e-token')。虽然这是 E2E 测试脚本,但如果错误地作为生产数据库初始化脚本运行,这些凭证可能被意外使用。

建议:考虑从环境变量读取敏感数据,例如:const adminPassword = process.env.E2E_ADMIN_PASSWORD || 'changeme';。或在脚本头部添加明确的危险警告注释。

.digest('hex');

try {
db.exec(`
CREATE TABLE IF NOT EXISTS admin (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')),
last_login_at TEXT
);

CREATE TABLE IF NOT EXISTS oauth_installations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL CHECK(platform IN ('github', 'gitee', 'gitlab')),
auth_type TEXT DEFAULT 'oauth',
github_app_installation_id TEXT,
account_id TEXT NOT NULL,
account_name TEXT,
access_token TEXT NOT NULL,
refresh_token TEXT,
token_expires_at DATETIME,
permissions TEXT,
is_active INTEGER DEFAULT 1,
created_at DATETIME DEFAULT (datetime('now', 'localtime')),
updated_at DATETIME DEFAULT (datetime('now', 'localtime'))
);

db.exec(`
CREATE TABLE IF NOT EXISTS repository (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
Expand Down Expand Up @@ -60,31 +95,45 @@ db.exec(`
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT OR REPLACE INTO admin (
id, username, password_hash, created_at, updated_at, last_login_at
) VALUES (
1, 'admin', '${seededAdminPasswordHash}', ${seededTimestamp}, ${seededTimestamp}, NULL
);

INSERT OR REPLACE INTO oauth_installations (
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 新增 oauth_installations 表的 INSERT 缺少幂等性保护

repository、analysis、jobs 表都使用了 INSERT OR REPLACE 保证幂等性,但新增的 oauth_installations 表(第11-25行创建)也使用 INSERT OR REPLACE,却缺少表可能已存在数据的保护逻辑。如果该表已存在且包含其他记录,INSERT OR REPLACE 可能导致意外的数据丢失。

建议:确认 oauth_installations 表的预期状态,或考虑使用 DELETE 配合条件后再 INSERT,确保与测试预期一致。

id, platform, auth_type, account_id, account_name, access_token, permissions, is_active, created_at, updated_at
) VALUES (
9001, 'github', 'oauth', 'mars167-e2e', 'mars167 E2E', 'e2e-token', 'repo', 1, ${seededTimestamp}, ${seededTimestamp}
);

INSERT OR REPLACE INTO repository (
id, platform, owner, name, full_name, installation_id, webhook_url, is_active, created_at, updated_at, last_analyzed_at
) VALUES (
9101, 'github', 'mars167', 'codagraph-lite', 'mars167/codagraph-lite',
9001, 'https://example.com/webhook', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
9101, 'github', 'mars167', 'codagraph-lite-e2e-fixture', 'mars167/codagraph-lite-e2e-fixture',
9001, 'https://example.com/webhook', 1, ${seededTimestamp}, ${seededTimestamp}, ${seededTimestamp}
);

INSERT OR REPLACE INTO analysis (
id, platform, owner, repo_name, pr_number, pr_title, pr_author, base_commit, head_commit,
status, analysis_result, comment_count, file_count, issue_count, started_at, completed_at,
error_message, created_at, updated_at
) VALUES (
9201, 'github', 'mars167', 'codagraph-lite', 42, 'E2E validation PR', 'mars167',
'main', 'feature/e2e', 'completed', '{}', 3, 5, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
9201, 'github', 'mars167', 'codagraph-lite-e2e-fixture', 42, 'E2E validation PR', 'mars167',
'main', 'feature/e2e', 'completed', '{}', 3, 5, 1, ${seededTimestamp}, ${seededTimestamp},
NULL, ${seededTimestamp}, ${seededTimestamp}
);

INSERT OR REPLACE INTO jobs (
id, type, payload, status, priority, attempts, max_attempts,
error_message, created_at, updated_at, started_at, completed_at
) VALUES (
9301, 'pr_analysis', '{"platform":"github","repo_name":"codagraph-lite","pr_number":"42"}',
'completed', 3, 1, 3, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
9301, 'pr_analysis', '{"platform":"github","repo_name":"codagraph-lite-e2e-fixture","pr_number":"42"}',
'completed', 3, 1, 3, NULL, ${seededTimestamp}, ${seededTimestamp}, ${seededTimestamp}, ${seededTimestamp}
);
`);
`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH · 缺少错误处理和数据库关闭保证

db.exec()执行可能因磁盘空间不足、权限问题或数据库锁而失败,当前代码没有try-catch包装。如果执行中途失败,数据库连接也不会被正确关闭(db.close()在L114),可能导致资源泄漏。

建议:使用try-finally或try-catch-finally确保db.close()始终执行:

try {
  db.exec(`...`);
  console.log(`Seeded E2E data into ${dbPath}`);
} finally {
  db.close();
}

console.log(`Seeded E2E data into ${dbPath}`);
db.close();
console.log(`Seeded E2E data into ${dbPath}`);
} finally {
db.close();
}
Loading