Files
guzhujushiBlog/README.md
T

161 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 孤竹居士的个人博客
部署在阿里云 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`:好友申请创建时间,用于博主审批列表展示申请时间。