CareerTrack 支持两种数据库后端,默认使用 SQLite(零配置),可选 PostgreSQL。
| 驱动 | 环境变量 | 说明 |
|---|---|---|
| SQLite | STORAGE_DRIVER=sqlite(默认) |
零配置本地存储,数据库文件 .careertrack/careertrack.db |
| PostgreSQL | STORAGE_DRIVER=postgres |
需要 DATABASE_URL 环境变量 |
- 设置
STORAGE_DRIVER=sqlite→ SQLite(显式) - 设置
STORAGE_DRIVER=postgres→ PostgreSQL(显式) - 未设置
STORAGE_DRIVER,有DATABASE_URL→ PostgreSQL(向后兼容) - 未设置
STORAGE_DRIVER,无DATABASE_URL→ SQLite(默认)
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
STORAGE_DRIVER |
否 | 自动检测 | sqlite 或 postgres |
DATABASE_URL |
postgres 模式必填 | - | PostgreSQL 连接串 |
SQLITE_DB_PATH |
否 | .careertrack/careertrack.db |
SQLite 数据库文件路径 |
SQLite 使用以下类型映射:UUID → TEXT,JSONB → TEXT,BOOLEAN → INTEGER,TIMESTAMP → TEXT。
首次启动时自动建表,无需手动执行迁移。
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(4))) || '-' || ...), -- UUID v4
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255), -- GitHub-only 用户可为 NULL
otp_secret VARCHAR(100),
otp_enabled INTEGER DEFAULT 0,
role VARCHAR(20) NOT NULL DEFAULT 'user',
auth_provider INTEGER NOT NULL DEFAULT 1, -- 登录方式位掩码:PASSWORD=1, GITHUB=2
disabled_at TEXT, -- 禁用时间,NULL 表示正常
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- 个人信息表
CREATE TABLE IF NOT EXISTS profiles (
id TEXT PRIMARY KEY DEFAULT ...,
user_id TEXT UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
basic_info TEXT DEFAULT '{}', -- JSON 对象
education TEXT DEFAULT '[]', -- JSON 数组
skills TEXT DEFAULT '[]',
work_experience TEXT DEFAULT '[]',
projects TEXT DEFAULT '[]',
portfolio TEXT DEFAULT '[]',
awards TEXT DEFAULT '[]',
other_experience TEXT DEFAULT '[]',
research TEXT DEFAULT '[]',
summary TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- 简历表
CREATE TABLE IF NOT EXISTS resumes (
id TEXT PRIMARY KEY DEFAULT ...,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL DEFAULT '未命名简历',
modules_config TEXT DEFAULT '{...}', -- JSON 对象
content TEXT DEFAULT '{}', -- JSON 对象
is_public INTEGER DEFAULT 0,
public_slug VARCHAR(50) UNIQUE,
modules_order TEXT DEFAULT '[...]', -- JSON 数组
module_titles TEXT DEFAULT '{}', -- JSON 对象,自定义模块标题
basic_info_display TEXT DEFAULT '{}', -- JSON 对象,字段图标/头像位置
preview_config TEXT DEFAULT '{}', -- JSON 对象,字号/行距
template VARCHAR(20) DEFAULT 'classic',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- MCP Key 表
CREATE TABLE IF NOT EXISTS mcp_keys (
id TEXT PRIMARY KEY DEFAULT ...,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
prefix VARCHAR(20) NOT NULL,
hash VARCHAR(64) NOT NULL,
scope VARCHAR(20) NOT NULL DEFAULT 'read_write',
created_at TEXT DEFAULT (datetime('now')),
last_used_at TEXT,
revoked_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_mcp_keys_user_id ON mcp_keys(user_id);
CREATE INDEX IF NOT EXISTS idx_mcp_keys_hash ON mcp_keys(hash);
-- OAuth 账号绑定表
CREATE TABLE IF NOT EXISTS user_oauth_accounts (
id TEXT PRIMARY KEY DEFAULT ...,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
provider VARCHAR(50) NOT NULL, -- 提供商:github
provider_account_id VARCHAR(255) NOT NULL, -- 第三方平台用户 ID
provider_username VARCHAR(255), -- 第三方平台用户名
email VARCHAR(255), -- 第三方平台邮箱
avatar_url TEXT, -- 第三方平台头像
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
UNIQUE(provider, provider_account_id)
);
CREATE INDEX IF NOT EXISTS idx_user_oauth_user_id ON user_oauth_accounts(user_id);
-- 注册码表
CREATE TABLE IF NOT EXISTS registration_codes (
id TEXT PRIMARY KEY DEFAULT ...,
code_hash VARCHAR(64) NOT NULL UNIQUE, -- 注册码 SHA-256 哈希
label VARCHAR(100), -- 备注标签
created_by TEXT REFERENCES users(id), -- 创建者
used_by_user_id TEXT REFERENCES users(id), -- 使用者
expires_at TEXT, -- 过期时间,NULL 表示永不过期
disabled_at TEXT, -- 禁用时间,NULL 表示可用
used_at TEXT, -- 使用时间
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_registration_codes_hash ON registration_codes(code_hash);完整的建表 SQL 见
src/lib/storage/schema.ts。
存储用户账号信息和认证数据。
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255), -- GitHub-only 用户可为 NULL
otp_secret VARCHAR(100), -- TOTP 密钥,NULL 表示未启用 OTP
otp_enabled BOOLEAN DEFAULT FALSE,
role VARCHAR(20) NOT NULL DEFAULT 'user', -- 用户角色:user / admin
auth_provider INTEGER NOT NULL DEFAULT 1, -- 登录方式位掩码:PASSWORD=1, GITHUB=2, OTHER=4
disabled_at TIMESTAMP WITH TIME ZONE, -- 禁用时间,NULL 表示正常
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 索引
CREATE INDEX idx_users_username ON users(username);字段说明:
id: 用户唯一标识,使用 UUID 避免 ID 猜测username: 登录用户名,唯一password_hash: bcrypt 加密后的密码,GitHub-only 用户可为 NULLotp_secret: TOTP 密钥,启用 OTP 时生成otp_enabled: 是否启用 OTP 二次验证role: 用户角色,user(普通用户)或admin(管理员),新注册默认userauth_provider: 登录方式位掩码,1=密码,2=GitHub,4=其他disabled_at: 禁用时间戳,NULL 表示账号正常
存储用户的个人信息,每种经历类型使用 JSONB 数组存储。
CREATE TABLE profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
-- 基本信息
basic_info JSONB DEFAULT '{}',
-- 各类经历(数组形式)
education JSONB DEFAULT '[]', -- 教育经历
skills JSONB DEFAULT '[]', -- 专业技能
work_experience JSONB DEFAULT '[]', -- 工作经历
projects JSONB DEFAULT '[]', -- 项目经历
portfolio JSONB DEFAULT '[]', -- 个人作品
awards JSONB DEFAULT '[]', -- 荣誉奖项
other_experience JSONB DEFAULT '[]', -- 其他经历
research JSONB DEFAULT '[]', -- 研究经历
-- 个人简介
summary TEXT DEFAULT '',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 索引
CREATE INDEX idx_profiles_user_id ON profiles(user_id);JSONB 字段结构:
// basic_info
{
"name": "张三",
"phone": "13800138000",
"email": "zhangsan@example.com",
"avatar": "/uploads/avatar.jpg",
"job_intention": {
"current_status": "在职",
"position": "前端工程师",
"expected_city": "北京",
"expected_salary": "20-30K"
},
"other": {
"education_level": "本科",
"website": "https://example.com",
"wechat": "zhangsan",
"city": "北京",
"github": "https://github.com/zhangsan",
"age": 28,
"work_years": 5,
"gender": "男"
}
}
// education 数组中的单个对象
{
"id": "uuid",
"school": "北京大学",
"major": "计算机科学",
"degree": "本科",
"start_date": "2016-09",
"end_date": "2020-06",
"degree_type": "全日制",
"college": "计算机学院",
"city": "北京",
"description": "在校经历描述"
}
// skills 数组中的单个对象
{
"id": "uuid",
"name": "JavaScript",
"description": "熟练掌握 ES6+,有丰富的 React 开发经验"
}
// work_experience 数组中的单个对象
{
"id": "uuid",
"company": "字节跳动",
"start_date": "2020-07",
"end_date": null, // null 表示"至今"
"department": "前端团队",
"position": "高级前端工程师",
"city": "北京",
"description": "工作内容描述"
}
// projects 数组中的单个对象
{
"id": "uuid",
"name": "电商平台重构",
"start_date": "2021-01",
"end_date": "2021-06",
"role": "前端负责人",
"city": "北京",
"link": "https://project-url.com",
"description": "项目描述"
}
// portfolio 数组中的单个对象
{
"id": "uuid",
"name": "开源项目",
"link": "https://github.com/...",
"image": "/uploads/portfolio.jpg",
"description": "项目描述"
}
// awards 数组中的单个对象
{
"id": "uuid",
"name": "最佳员工",
"date": "2023-12",
"description": "获奖描述"
}
// other_experience 和 research 结构类似
{
"id": "uuid",
"name": "经历名称",
"start_date": "2020-01",
"end_date": "2020-06",
"role": "角色",
"department": "部门",
"city": "城市",
"description": "详细描述"
}存储简历配置和内容。
CREATE TABLE resumes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL DEFAULT '未命名简历',
template VARCHAR(20) DEFAULT 'classic',
-- 模块开关配置
modules_config JSONB DEFAULT '{
"basic_info": true,
"education": true,
"skills": true,
"work_experience": true,
"projects": true,
"portfolio": false,
"awards": false,
"other_experience": false,
"research": false,
"summary": false
}',
-- 简历内容(可以覆盖个人信息)
content JSONB DEFAULT '{}',
-- 公开相关
is_public BOOLEAN DEFAULT FALSE,
public_slug VARCHAR(50) UNIQUE,
-- 模块排序
modules_order JSONB DEFAULT '["basic_info", "education", "skills", "work_experience", "projects", "portfolio", "awards", "other_experience", "research", "summary"]',
-- 自定义模块标题
module_titles JSONB DEFAULT '{}',
-- 基本信息显示配置
basic_info_display JSONB DEFAULT '{}',
-- 预览配置(字号、行距等)
preview_config JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 索引
CREATE INDEX idx_resumes_user_id ON resumes(user_id);
CREATE INDEX idx_resumes_public_slug ON resumes(public_slug) WHERE public_slug IS NOT NULL;
CREATE INDEX idx_resumes_template ON resumes(template);存储 MCP 服务的 API Key,用于 AI Agent 访问。
CREATE TABLE mcp_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
prefix VARCHAR(20) NOT NULL, -- Key 前缀,用于列表展示
hash VARCHAR(64) NOT NULL, -- Key 的 SHA-256 哈希
scope VARCHAR(20) NOT NULL DEFAULT 'read_write', -- 权限范围:read_write / read_only
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
last_used_at TIMESTAMP WITH TIME ZONE, -- 最后使用时间
revoked_at TIMESTAMP WITH TIME ZONE -- 撤销时间,NULL 表示有效
);
-- 索引
CREATE INDEX idx_mcp_keys_user_id ON mcp_keys(user_id);
CREATE INDEX idx_mcp_keys_hash ON mcp_keys(hash);存储用户的第三方 OAuth 账号绑定关系。
CREATE TABLE user_oauth_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
provider VARCHAR(50) NOT NULL, -- 提供商:github
provider_account_id VARCHAR(255) NOT NULL, -- 第三方平台用户 ID
provider_username VARCHAR(255), -- 第三方平台用户名
email VARCHAR(255), -- 第三方平台邮箱
avatar_url TEXT, -- 第三方平台头像
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(provider, provider_account_id)
);
-- 索引
CREATE INDEX idx_user_oauth_user_id ON user_oauth_accounts(user_id);字段说明:
provider: OAuth 提供商标识,如githubprovider_account_id: 第三方平台的用户唯一标识provider_username: 第三方平台的用户名email: 第三方平台的邮箱地址avatar_url: 第三方平台的头像 URL
存储邀请注册码,用于控制用户注册。
CREATE TABLE registration_codes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code_hash VARCHAR(64) NOT NULL UNIQUE, -- 注册码 SHA-256 哈希
label VARCHAR(100), -- 备注标签
created_by UUID REFERENCES users(id), -- 创建者(管理员)
used_by_user_id UUID REFERENCES users(id), -- 使用者
expires_at TIMESTAMP WITH TIME ZONE, -- 过期时间,NULL 表示永不过期
disabled_at TIMESTAMP WITH TIME ZONE, -- 禁用时间,NULL 表示可用
used_at TIMESTAMP WITH TIME ZONE, -- 使用时间
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 索引
CREATE INDEX idx_registration_codes_hash ON registration_codes(code_hash);字段说明:
code_hash: 注册码明文的 SHA-256 哈希,明文只在创建时返回一次label: 管理员备注,方便识别注册码用途created_by: 创建该注册码的管理员used_by_user_id: 使用该注册码注册的用户expires_at: 过期时间,NULL 表示永不过期disabled_at: 禁用时间,非 NULL 表示已禁用
content 字段说明:
content 字段用于存储简历的自定义内容。结构与 profiles 类似,但每个字段都是可选的:
- 如果某个字段存在,使用 content 中的值
- 如果某个字段不存在或为 null,自动使用 profile 中的值
{
"basic_info": { ... }, // 可选,覆盖个人信息
"education": [ ... ], // 可选,覆盖个人信息
"skills": [ ... ], // 可选,覆盖个人信息
// ... 其他字段同理
}users (1) ──── (1) profiles
│
├──── (N) resumes
│
├──── (N) mcp_keys
│
├──── (N) user_oauth_accounts
│
└──── created (N) registration_codes
- 一个用户对应一份个人信息(1:1)
- 一个用户可以有多份简历(1:N)
- 一个用户可以有多个 MCP Key(1:N)
- 一个用户可以绑定多个 OAuth 账号(1:N)
- 一个管理员可以创建多个注册码(1:N)
- 简历内容可以引用或覆盖个人信息
modules_config 使用 JSONB,可以轻松添加新模块:
{
"custom_module": true,
"certificates": true
}未来可在 content 中添加 AI 生成的内容标记:
{
"skills": [
{
"id": "uuid",
"name": "JavaScript",
"description": "AI 优化后的描述",
"ai_optimized": true
}
]
}未来可添加 templates 表:
CREATE TABLE templates (
id UUID PRIMARY KEY,
name VARCHAR(100),
content JSONB,
-- ...
);SQLite 使用自动建表策略。首次启动时,src/lib/storage/schema.ts 中的 initSchema() 函数会自动执行 CREATE TABLE IF NOT EXISTS 创建所有表。
无需手动执行迁移。
PostgreSQL 使用相同的自动建表策略。首次启动时自动创建所有表和索引。