onlyWeb 系统架构文档
目录
- 1. 架构目标
- 2. 推荐技术栈
- 3. 当前前端 Mock 原型
- 4. 系统组件
- 5. 模块划分
- 6. 建议目录结构
- 7. 数据模型草案
- 8. 路由设计
- 9. Docker 架构
- 10. 认证设计
- 11. 文件上传设计
- 12. 部署设计
- 13. 架构原则
- 14. 当前正式程序结构
- 15. 软删除字段
- 16. 项目文档上传与在线预览
- 17. HR 联系方式与个人资料图片
- 18. 作品模块展示配置
- 19. Wiki 知识库架构
1. 架构目标
onlyWeb 的架构目标是:
- 小型化:适合个人维护,不引入不必要的复杂系统。
- Docker 化:支持本地、服务器和 NAS 等环境部署。
- 数据驱动:页面内容来自数据库,而不是硬编码页面。
- 可扩展:后续可以加入访问统计、文件存储、备份和权限增强。
2. 推荐技术栈
当前采用方案 A:完整 Docker 化。
推荐组合:
Web 框架:Next.js
语言:TypeScript
数据库:SQLite
数据访问:better-sqlite3
样式:全局 CSS
认证:自定义管理员登录
文档转换:LibreOffice Writer,用于 Word 转 PDF 预览
部署:Docker Compose当前正式实现使用 Next.js App Router;数据库模型和产品结构保持轻量,避免引入复杂 CMS。
3. 当前前端 Mock 原型
当前原型代码独立放在:
webMock/用途:
- 验证页面风格。
- 验证前台信息架构。
- 验证后台管理范围。
- 验证定制页的管理员预览和 HR 专属访问流程。
当前原型不连接数据库,数据来自:
webMock/src/mockData.tswebMock/ 的 Mock 登录状态暂存在浏览器 localStorage。正式程序已使用服务端认证和 HTTP-only Cookie 会话。
4. 系统组件
Browser
↓
Web App
↓
SQLiteMVP Docker 服务:
app
SQLite 文件数据库后续增强服务:
caddy / nginx
backup
minio5. 模块划分
5.1 Public Frontend
负责公开页面展示。
包含:
- 首页
- 关于我
- 通用简历
- 作品列表
- 作品详情
- 定制简历页
5.2 Admin Dashboard
负责内容管理。
包含:
- 登录
- 个人资料管理
- 作品管理
- 经历管理
- 技能管理
- 定制简历页管理
- HR 专属分享链接生成与复制
- 管理员预览与 HR 视图切换
5.3 Server Layer
负责业务逻辑和数据访问。
包含:
- 认证与会话
- 数据校验
- 数据库读写
- 文件上传,后续
5.4 Database
使用 SQLite 文件数据库存储结构化数据。
核心数据:
- 管理员账号
- 个人资料
- 作品
- 经历
- 技能
- 定制简历页
6. 建议目录结构
onlyWeb/
webMock/
src/
main.tsx
mockData.ts
styles.css
package.json
vite.config.ts
src/
routes/
index.tsx
about.tsx
resume.tsx
projects/
for/
admin/
components/
public/
admin/
ui/
db/
schema.sql
sqlite.ts
server/
auth.ts
validators.ts
lib/
slug.ts
dates.ts
styles/
public/
uploads/
docs/
prd.md
architecture.md
roadmap.md
docker-compose.yml
Dockerfile
.env.example
README.md实际目录会根据框架约定微调。
7. 数据模型草案
7.1 admin_users
管理员账号。
{
id: string
email: string
passwordHash: string
createdAt: Date
updatedAt: Date
}7.2 profiles
个人资料。MVP 可以只维护一条记录。
{
id: string
name: string
title: string
bio: string
email: string
phone?: string
location?: string
githubUrl?: string
linkedinUrl?: string
websiteUrl?: string
avatarUrl?: string
createdAt: Date
updatedAt: Date
}7.3 projects
作品。
{
id: string
title: string
slug: string
summary: string
description: string
coverImageUrl?: string
techStack: string[]
role: string
highlights: string[]
demoUrl?: string
githubUrl?: string
docsUrl?: string
isFeatured: boolean
isPublished: boolean
createdAt: Date
updatedAt: Date
}7.4 experiences
经历。
{
id: string
company: string
role: string
startDate: string
endDate?: string
summary: string
highlights: string[]
skills: string[]
isPublished: boolean
createdAt: Date
updatedAt: Date
}7.5 skills
技能。
{
id: string
name: string
category: string
level?: number
sortOrder: number
createdAt: Date
updatedAt: Date
}7.6 resume_pages
定制简历页。SQLite MVP 中直接用 JSON 数组字段保存关联 ID,减少个人小系统的表复杂度。
{
id: string
companyName: string
companySlug: string
positionName: string
positionSlug: string
headline: string
intro: string
motivation?: string
shareToken: string
projectIds: string[]
experienceIds: string[]
skillIds: string[]
isPublished: boolean
createdAt: Date
updatedAt: Date
}8. 路由设计
8.1 前台路由
/ 首页
/about 关于我
/resume 通用简历
/projects 作品列表
/portfolio 独立作品集展示页,无导航,可进入作品详情
/portfolio/:slug 独立作品集专用详情页,无全站导航
/portfolio/:slug/docs 独立作品集专用文档预览页,无全站导航
/wiki Wiki 知识库首页
/wiki/:slug Wiki 笔记详情页
/projects/:slug 作品详情
/r/:shareToken HR 专属定制简历页
/projects/:slug/docs Markdown / PDF / Word 项目文档在线预览
/projects/:slug/docs/preview PDF 预览与 Word 转 PDF 预览接口
/projects/:slug/docs/download 原始项目文档下载接口
/uploads/profile/:fileName 个人资料图片访问接口
/contact 联系方式说明:
/r/:shareToken是发给 HR 的访问地址。- 前台不提供定制页列表。
- HR 只能访问 token 对应的单个定制页。
/portfolio使用独立展示模式,不渲染全站 Header/Footer;作品卡片输出到/portfolio/:slug专用详情链接。/portfolio/:slug复用作品详情内容,/portfolio/:slug/docs复用文档预览能力,但同样不渲染全站 Header/Footer,避免访客通过右上角导航进入其他页面。后台/admin/projects展示该页面完整地址并支持复制。
8.2 后台路由
/admin/login 登录
/admin 后台首页、个人资料、技能列表行编辑
/admin/projects 新版作品管理,支持新建、编辑、软删除、普通文档/效果演示文档上传、模块展示配置;桌面端使用独立滚动双栏布局
/admin/experiences 新版经历管理,支持新建、编辑、软删除
/admin/custom-pages 新版定制页管理,支持新建、编辑、软删除
/admin/recycle-bin 回收站,查看和恢复已删除作品、经历、定制页
/for/:companySlug/:positionSlug 管理员预览定制简历页,需要登录9. Docker 架构
MVP 预期:
services:
app:
build:
context: .
args:
APP_PORT: ${APP_PORT:-18473}
ports:
- "${APP_PORT:-18473}:${APP_PORT:-18473}"
environment:
PORT: ${APP_PORT:-18473}
DATABASE_PATH: ${DATABASE_PATH:-/app/data/onlyweb.db}
APP_URL: ${APP_URL:-http://localhost:${APP_PORT:-18473}}
volumes:
- ./data:/app/data
- ./public/uploads:/app/public/uploads实际配置见 docker-compose.yml;端口通过 APP_PORT 配置,对外域名通过 APP_URL 配置。运行镜像内置 LibreOffice Writer 和中文字体,用于 .doc/.docx 转 PDF 在线预览。
10. 认证设计
MVP 使用单管理员账号。
建议:
- 密码使用 bcrypt 或 argon2 哈希。
- 会话使用 HTTP-only Cookie。
- 后台路由统一校验登录态。
- 管理员预览路由
/for/:companySlug/:positionSlug需要登录态。 - HR 分享路由
/r/:shareToken不需要登录,但 token 必须足够随机且不可枚举。 - 初始管理员可通过环境变量创建,或通过 seed 脚本创建。
11. 文件上传设计
当前使用项目目录绑定挂载。
./public/uploads -> /app/public/uploads上传文件分为:
- 普通项目文档:
public/uploads/project-docs/<projectId>/ - 效果演示文档:
public/uploads/project-docs/<projectId>/effect-demo/ - Word 预览缓存:
public/uploads/project-doc-previews/<documentId>/preview.pdf - 个人资料图片:
public/uploads/profile/
后续可迁移到:
- S3
- Cloudflare R2
- MinIO
12. 部署设计
12.1 MVP 部署
docker compose up -d12.2 生产增强
后续加入:
- Caddy 自动 HTTPS
- 数据库定时备份
- 上传文件备份
- 日志轮转
- 健康检查
13. 架构原则
- 不为每个公司岗位创建单独页面文件。
- 不在前台暴露定制页列表。
- HR 只通过专属 token 链接访问单个定制页。
- 定制简历页必须由数据库记录驱动。
- 后台表单尽量简单,先满足个人使用。
- 优先使用关系型数据建模,避免早期引入复杂 CMS。
14. 当前正式程序结构
当前正式程序已开始落地:
src/
app/
page.tsx
about/page.tsx
projects/page.tsx
projects/[slug]/page.tsx
resume/page.tsx
contact/page.tsx
admin/login/page.tsx
admin/page.tsx
for/[companySlug]/[positionSlug]/page.tsx
r/[shareToken]/page.tsx
components/
AppShell.tsx
Layout.tsx
Shared.tsx
admin/
resume/
db/
schema.sql
sqlite.ts
lib/
mockData.ts说明:
src/lib/mockData.ts仅用于首次初始化种子数据。src/db/schema.sql是正式 SQLite schema。src/db/sqlite.ts负责初始化数据库和种子数据。/r/:shareToken使用专属分享视图,不显示普通站点导航。/for/:companySlug/:positionSlug当前使用服务端 session 做管理员预览保护。
15. 软删除字段
projects、experiences、resume_pages 增加 deleted_at 字段。
deleted_at IS NULL:正常内容。deleted_at IS NOT NULL:已进入回收站。- 前台查询、定制页查询和后台管理列表默认只读取
deleted_at IS NULL的内容。 - 回收站读取
deleted_at IS NOT NULL的内容,并通过恢复操作把deleted_at置回NULL。
16. 项目文档上传与在线预览
作品外部文档链接继续复用 projects.docs_url 字段;上传的多个作品文档保存到 project_documents 表,并通过 document_kind 区分普通项目文档和效果演示文档。上传文件由项目目录 public/uploads/ 持久化。
上传文件保存时保留原始文件名;同一作品下 project_id + file_name + document_kind 唯一,普通文档和效果演示文档互不覆盖。Markdown 文档通过 /projects/:slug/docs?doc=<documentId> 在线渲染;PDF 直接通过 /projects/:slug/docs/preview?doc=<documentId> 嵌入预览;Word .doc/.docx 由 LibreOffice 转换为 PDF 后预览。原始文档下载通过 /projects/:slug/docs/download?doc=<documentId> 返回 Content-Disposition: attachment 强制下载。已上传文档删除时设置 deleted_at,不物理删除文件。
17. HR 联系方式与个人资料图片
profiles 表通过 wechat_id 和 wechat_qr_url 保存微信联系方式。后台个人资料页支持填写微信号、填写二维码链接或上传二维码图片。
微信二维码图片保存到 public/uploads/profile/,Docker 中通过 ./public/uploads:/app/public/uploads 绑定挂载持久化。为了兼容中文文件名和运行时上传文件,图片访问走 /uploads/profile/:fileName 动态路由读取文件并返回正确图片类型。
HR 定制页 /r/:shareToken 展示统一风格的联系方式卡片,包括邮箱、手机号、微信号、所在地和微信二维码;底部旧联系方式模块在 HR 定制页中隐藏,避免重复。
18. 作品模块展示配置
作品详情页由数据库字段控制模块是否展示,避免为不同作品硬编码页面。作品详情页使用面向非技术决策者的业务成果展示布局,后台可按作品维护展示标题、首屏说明、使用场景、核心价值、交付形式、业务痛点、解决步骤、使用效果、项目价值、个人贡献、技术实现标签和管理者关注点。管理员登录后访问作品详情页会看到“编辑页面”按钮;点击后进入页面原位编辑模式,可直接修改业务成果展示、个人贡献、技术实现标签,并可在资料入口上传普通项目文档或效果演示文档;普通访客不显示该入口。页面在配置为空时才回退到作品摘要、详情、亮点或效果文档生成的短摘要;完整 Markdown、PDF、Word 文档仍通过文档详情页查看。
projects 表中的模块开关包括:
show_description:项目介绍show_role:我的职责show_effect_demo:效果演示show_highlights:项目亮点show_tech_stack:技术栈show_links:相关链接/文档
后台 /admin/projects 的“模块展示”区域维护这些开关;已有作品默认全部展示。
19. Wiki 知识库架构
Wiki 模块通过 WIKI_HOST_VAULT_PATH 将宿主机 Obsidian vault 挂载到容器内 WIKI_VAULT_PATH。服务端读取 Markdown 文件,生成文件树、双链、标签、反向链接和局部关系图谱数据;左侧文件树目录默认收起,可点击展开或收起;后台 /admin/wiki 可以点击“知识库根目录路径”后用弹窗浏览服务器目录并选择 vault 路径,通过下拉选项维护公开状态、忽略目录,并上传 Markdown 文档到 vault;上传目标目录从已扫描到的知识库目录中选择。
图谱前端使用浏览器端 SVG 力导向模拟实现,不依赖外部图谱服务。节点支持拖拽,节点标签支持点击跳转到对应笔记。默认 WIKI_PUBLIC=false,访问 /wiki 需要管理员登录;设置为 true 后可公开访问。后台保存的 Wiki 配置写入 app_settings 表,优先级高于环境变量;WIKI_EXCLUDE_DIRS 保存为空字符串表示不忽略任何目录,而不是回退到默认值。Docker 部署时通过 WIKI_HOST_BROWSE_ROOT 和 WIKI_CONTAINER_BROWSE_ROOT 将宿主机可浏览目录以读写方式映射到容器内,解决 LinuxOS 中 /root/onlyweb/wiki/vault 这类宿主机路径无法直接从容器访问的问题;.env 存在且可写时会同步更新 Wiki 配置。Wiki 扫描包含系统目录保护:拒绝扫描 /、/proc、/sys、/dev、/run、/boot、/tmp,并跳过符号链接。