Files
guzhujushiBlog/README.md
T

161 lines
8.1 KiB
Markdown
Raw Normal View History

2026-08-22 22:28:41 +08:00
# 孤竹居士的个人博客
部署在阿里云 Ubuntu 上的个人全栈博客系统:原生前端 SPA + FastAPI + SQLite。
## 技术架构
| 层 | 技术 |
| --- | --- |
| 服务器 | Ubuntu Linux + Nginx |
| 前端 | 原生 HTML + CSS + JavaScriptSPAHistory API 路由) |
| 后端 | Python FastAPI(仅监听 127.0.0.1:8080 |
| 数据库 | SQLite + SQLAlchemy ORM |
| 认证 | JWT Tokenbcrypt 存储密码哈希) |
| 文件 | Nginx 静态访问 uploads 目录 |
- Nginx80HTTP/ 443HTTPS,待配置);`/api/*` 反代到 `127.0.0.1:8080``/uploads/*` 静态映射。
- 端口:22 SSH、80 HTTP、443 HTTPS、25565 Minecraft(独立运行,不占用博客端口)。
## 目录结构
```text
blog/
├── frontend/ # 前端源码(原生 SPAES Modules 拆分)
│ ├── index.html
│ ├── style.css
│ ├── main.js # 入口:初始化 + 全局事件
│ ├── api.js # 网络层:api() / uploadFile()
│ ├── auth.js # 登录 / 注册 / 会话
│ ├── router.js # 路由与页面壳(导航栏、用户区)
│ ├── article.js # 文章列表 / 详情 / 分区 / 简介视图
│ ├── comment.js # 评论与回复
│ ├── friend.js # 好友申请
│ ├── manage.js # 博主管理面板(发文 / 编辑 / 简介设置)
│ ├── markdown.js # Markdown 轻量渲染(含 URL 白名单)
│ ├── state.js # 全局常量与状态
│ └── utils.js # DOM / 日期 / 弹窗 / Toast 工具
├── backend/
│ ├── main.py # FastAPI 实例 + 路由注册 + 统一错误格式
│ ├── database.py # SQLite 连接与初始化
│ ├── models.py # ORM 模型(User/Article/Comment/Like/Friend/EmailCode
│ ├── schemas.py # Pydantic 请求/响应模型
│ ├── auth.py # bcrypt + JWT + 当前用户依赖
│ ├── security.py # 邮箱校验、IP 提取、内存限流器
│ ├── email.py # SMTP 验证码发送
│ ├── seed.py # 博主账号初始化(python -m backend.seed
│ └── routers/ # 业务路由(user/article/comment/like/friend/email/password/upload
├── uploads/
│ ├── avatar/ # 头像
│ ├── article/ # 文章图片(封面/插图)
│ └── project/ # 项目文件(zip)
├── .env # 环境配置(已被 .gitignore 忽略,勿提交)
├── .env.example # 环境变量模板(全中文注释)
├── requirements.txt # Python 依赖
└── README.md
```
## 快速开始(本地开发)
1. 安装依赖:`pip install -r requirements.txt`
2. 配置环境:复制 `.env.example``.env`,填写 `JWT_SECRET``BLOGGER_*``SMTP_*`
3. 初始化数据库与博主:`python -m backend.seed`
4. 启动后端:`uvicorn backend.main:app --host 127.0.0.1 --port 8080`
5. 启动前端:任意静态服务器指向 `frontend/`,并将 `/api/*` 反代到 `127.0.0.1:8080``/uploads/*` 映射到 `uploads/`(本地开发可借助 Nginx 或简单代理脚本实现)。
> 提示:前端为 SPA,任意路径(如 `/article/1`)都应回退到 `index.html`。
## 权限与角色
| 角色 | 权限 |
| --- | --- |
| visitor(游客) | 查看公开文章、申请好友(好友文章仅显示标题与封面) |
| friend(好友) | 查看公开与好友文章、评论(含回复)、点赞 |
| blogger(博主) | 全部权限:发布/编辑/删除文章、审批好友申请、上传图片与项目、修改简介与头像 |
## API 一览(统一前缀 `/api`,统一响应 `{success, data, message}`
**认证与资料**
- `POST /api/register` 注册(邮箱/用户名/密码,bcrypt 存储)
- `POST /api/login` 登录(JWT;失败限流)
- `GET /api/user/level` 当前角色
- `GET /api/user/me` 当前用户资料
- `GET /api/user/blogger` 博主公开资料(简介页)
- `PUT /api/user/profile` 更新头像/简介
**文章**
- `POST /api/article/add` 发布(仅博主)
- `GET /api/article/list?page=&page_size=` 列表(分页;好友文章对游客仅标题+封面)
- `GET /api/article/{id}` 详情(好友文章对游客锁定正文)
- `PUT /api/article/{id}` 编辑(仅博主)
- `DELETE /api/article/{id}` 删除(仅博主,级联评论/点赞)
**评论 / 点赞 / 好友**
- `POST /api/comment/add` 评论或回复(`parent_id` 可选;仅好友/博主)
- `GET /api/comment/list?article_id=` 评论列表
- `POST /api/like/add` 点赞(好友/博主,不可重复)
- `GET /api/like/list?article_id=` 点赞列表
- `POST /api/friend/apply` 申请好友 / `GET /api/friend/status` 好友状态
- `GET /api/friend/applications` 申请列表(仅博主)
- `POST /api/friend/{id}/approve|reject` 审批(仅博主)
**邮箱验证码 / 密码**
- `POST /api/email/send-code` 发送验证码(邮箱+IP 限流)
- `POST /api/email/verify-code` 校验验证码(尝试次数限制)
- `POST /api/password/forgot` 忘记密码 / `POST /api/password/reset` 重置密码
**上传**
- `POST /api/upload/avatar` 头像(jpg/png/webp,≤2MB
- `POST /api/upload/article` 文章图片(仅博主,≤5MB
- `POST /api/upload/project` 项目文件(仅博主,zip,≤50MB)
## 安全说明
- 密码 bcrypt 哈希存储,禁止明文;localStorage 仅保存 token 与用户名。
- JWT_SECRET 缺失时后端拒绝启动(fail-fast),禁止硬编码弱密钥。
- 登录、验证码发送/校验均有内存限流(阈值可在 `.env` 调整)。
- 上传文件:白名单扩展名 + 魔数校验 + 随机文件名,禁止执行用户上传内容。
- 前端 Markdown 渲染带 URL 协议白名单(`javascript:`/`data:` 等被拦截)+ CSP 响应头/标签(纵深防御)。
- 后端仅监听 127.0.0.1:8080,禁止公网直连。
- 注册接口对“邮箱 / 用户名已占用”返回统一提示,且先核验验证码再查唯一性(防枚举);注册失败会作废本次验证码,需重新发送后再试。
## 部署(阿里云)
> 详细的 Nginx 站点配置与 systemd 服务文件将在后续任务中补充(当前仅给出要点)。
1. 上传项目到 `/var/www/blog`,安装依赖:`pip install -r requirements.txt`
2. 配置 `.env`(强随机 `JWT_SECRET`、博主账号、SMTP、安全限流)。
3. `python -m backend.seed` 初始化数据库与博主。
4. Nginx80 端口托管 `frontend/` 静态文件;`/api/*` 反代 `127.0.0.1:8080``/uploads/*` 映射 `uploads/`SPA 回退 `index.html`
5. systemd 守护 `uvicorn`(开机自启、崩溃重启)。
6. 安全组仅开放 22 / 80 / 443 / 25565。
## 数据库升级说明(仅旧库需要)
全新部署无需迁移:`python -m backend.seed` 会自动按最新结构建表。
若从旧版本升级(服务器上已有 `backend/blog.db`),需先补齐新字段再重启服务:
```bash
# 服务器(Ubuntu)执行;正式升级前建议先备份 backend/blog.db 与 uploads/
cd /var/www/blog
python3 - <<'EOF'
import sqlite3, datetime
con = sqlite3.connect("backend/blog.db")
con.execute("ALTER TABLE users ADD COLUMN token_version INTEGER NOT NULL DEFAULT 0")
con.execute("ALTER TABLE friends ADD COLUMN created_time DATETIME")
con.execute("ALTER TABLE articles ADD COLUMN category VARCHAR(30) NOT NULL DEFAULT 'life'")
con.execute("ALTER TABLE users ADD COLUMN avatar_updated_time DATETIME")
now = datetime.datetime.now(datetime.UTC).replace(tzinfo=None) # 与后端 utcnow() 一致:无时区 UTC
con.execute("UPDATE friends SET created_time=? WHERE created_time IS NULL", (now,))
con.commit()
con.close()
print("数据库升级完成")
EOF
```
本地开发(Windows)若仅为调试数据,可省略迁移:删除 `backend/blog.db` 后重新执行 `python -m backend.seed` 即可。
字段说明:
- `users.token_version`:JWT 令牌版本号。重置密码后版本自增,该用户所有旧令牌立即失效(登录状态吊销)。
- `friends.created_time`:好友申请创建时间,用于博主审批列表展示申请时间。