# blog **Repository Path**: yechen653/blog ## Basic Information - **Project Name**: blog - **Description**: fastapi学习阶段做的一个简易的blog - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-14 - **Last Updated**: 2026-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # blog-api — 个人主页 + 博客 一个用 FastAPI 写的个人站点:根路径是**个人简介页**,`/static/` 是**博客页** (文章 / 评论 / 图片上传),所有接口自带 `/docs` 交互式文档。 目标是部署到自己的服务器上,长期用来写日志。 > 这个项目同时是一份**学习记录**:从零搭到一个可用的小后端, > 每一步的设计取舍都写在代码注释里。配套的深度学习笔记在 [`others/notes/`](others/notes/)。 ## 功能 - **文章**:增删改查、草稿 / 发布、按标题搜索、分页、浏览量(Redis 防刷,同一 IP 窗口内只计一次) - **评论**:两层回复、父评论删除时级联删除 - **用户**:注册 / 登录(JWT)、密码 bcrypt 哈希、越权防护 - **上传**:图片;类型**双校验**(`content_type` 粗筛 + 文件头魔数)、大小限制、路径穿越防护 - **安全(阶段17)**:登录 / 注册**限流**、CORS 按需开启 - **前端**:无框架、无构建、无 npm。两张 HTML 骨架 + `site-config.js`(展示内容唯一数据源)+ `site.js`(数据绑定)+ `swiss.css` / `starry.css`(两套视觉 + 字段级语义色板)+ `theme.js`(切换与持久化)+ `starry-canvas.js`(常驻油画层)+ `intro.js`(揭幕演出)。默认 Swiss Editorial(白 / 黑 / 信号红,非对称网格、硬边框),右上角设置键可切到**星月夜**主题,选择记在 `localStorage`。星月夜按《星月夜》原画构图用代码生成:**4 个主旋转点 + 11 颗星**、沿流场切线铺排的短促厚涂笔触、每段独立的墨色 / 粗细档 / 占比 / 同色成簇 / 留白 / 叠笔、断续同心月晕与星晕;本期不画丝柏树。**首屏就有一张静态油画**,演出只是「把这张画画出来的过程」:骨架流线既是演出画的线,也是静态画里的暗底宽笔,各自旋转平移后留在动态背景里,不消失。主动切换时播「空白画布 → 骨架生长 → 笔触浮现 → 多涡旋差速公转 → 收笔 → 圆形换幕 → 元素错峰归位」,可跳过,尊重 `prefers-reduced-motion`。**演出期间 `setAttribute('d')` 为 0**,③ 阶段之后每帧 0 次 DOM 写入,全部交给 CSS 合成器;静止层与旋转层拆成两个独立 SVG,密集底纹只栅格化一次。文章详情 / 编辑器为 `data-view="reading"` 静态态,其余页面保留极慢的涡旋漂移用于展示。**展示内容与视图分离**:姓名、简介、技术栈、站外链接、导航、标题、主题面板文案全在 `site-config.js`,由 `site.js` 填进 HTML 骨架 - **健康检查**:`/health`、`/health/db`、`/health/redis` ## 快速开始(本机) ```bash python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txt -r requirements-dev.txt cp .env.example .env # 按需改 DATABASE_URL / SECRET_KEY / REDIS_URL alembic upgrade head # 建表 uvicorn app.main:app --reload ``` 打开: - 个人主页 - 博客页 - API 文档 需要 **MySQL** 与 **Redis**(起库命令见 `.env.example` 里的注释)。 ## 用 Docker 一把起 ```bash docker compose up --build -d docker compose exec app alembic upgrade head # 首次建表 ``` ## 跑测试 ```bash pytest ``` 测试会**另建** `blog_test` 库(绝不碰开发库),并需要一个能连的 MySQL; Redis 用内存假替身,不需要真服务。 ## 目录结构 ``` app/ main.py 应用入口:装中间件、挂路由、根路径 → 个人主页 core/ 基础设施:config / database / security / cache / ratelimit / deps routers/ 接线层:路径与参数、HTTP 状态码翻译 services/ 业务规则层 crud/ 数据访问层(数据库 / Redis / 磁盘) models/ schemas/ ORM 模型 / Pydantic 契约 static/ profile.html 个人主页:结构骨架(不含展示文案) index.html 博客页:结构骨架 + 交互逻辑 site-config.js 【改内容改这里】全站展示数据唯一来源 site.js 把配置按 data-bind / data-rich / data-repeat 填进骨架 swiss.css 【改风格改这里】共享网格 + 两层语义色板 (基础色板 --bg/--fg/--accent + 字段级 --field-*) starry.css 星月夜覆盖【角色分配】+ 常驻油画层 / 设置键 / 演出层样式 theme.js 设置键、主题切换、localStorage 持久化 starry-canvas.js 【主题的一部分】COMPOSITION 单一构图源(多旋转点) + 流场采样 + 段级墨色/粗细/占比配方 + 短笔触分桶 + 一次性生成静态油画层(静止 / 旋转两个独立 SVG) intro.js 揭幕演出:只编排时间线,不重画任何路径;骨架生长 / 多涡旋差速公转 / 圆形换幕 / 元素归位全靠 CSS 合成器 migrations/ Alembic 迁移 tests/ 测试(需 MySQL) others/notes/ 学习笔记 ``` 调整展示内容(改名字、改简介、增删站外链接、改栏目名、加主题)只动 `site-config.js`; 调整版式结构动 HTML 骨架与 `swiss.css`;换基础配色动 `swiss.css` 的 `:root` 或 `starry.css` 的 `[data-theme="starry"]` 覆盖;**只改某类字段的颜色** (编号 / 日期 / 职能说明 / 标签 / 草稿)改 `--field-*` 这一层,不必动到规则本身。 字段级 token 的默认值逐一等于改绑前的取值,所以改星月夜不会让 Swiss 走样。 加第三套主题:在 `site-config.js` 的 `themes.items` 加一项,再写一份覆盖 基础色板 + 字段级 token 的 CSS 即可。 内容由 `site.js` 在浏览器端渲染,因此页面需要启用 JavaScript; 主题选择由 `` 内联脚本在首帧前应用,避免刷新时先闪一下默认主题。 主题是持久状态,揭幕演出是一次性事件,演出失败或被跳过不影响主题正确性。 `starry-canvas.js` 在 `intro.js` 之前加载。所有 `` 只在首屏生成一次, 演出期间 `setAttribute('d')` 为 0;骨架流线既是演出画的线,也是静态画里的 暗底宽笔,因此**首屏静态与演出结束是同一批 DOM 节点**,不需要搬树或交接重建。 静止层(密集底纹、山丘、村庄、星群、骨架)与旋转层(主涡 / 副涡 / 月亮 / 亮星)拆成两个独立 SVG:前者只栅格化一次,后者各 `` 按 `--orbit` 差速自转。 构图坐标统一从 `COMPOSITION` 派生,并写入 `--moon-x / --vortex-x` 等 CSS 变量, CSS 光晕与 SVG 月亮不会各写各的百分比。`intro.js` 只负责编排时间线。 ## 分层约定 一句话判据: - 出现 `Depends / Query / Path / Body / Request / HTTPException / APIRouter` 的行 → 留在 `routers/` - 业务规则(查重、大小限制、防刷 / 限流判定)→ `services/` 或 `core/` - 只跟**存储**打交道(SQL / Redis / 磁盘)→ `crud/` ## 部署要点 1. **改密钥**:`.env` 里的 `SECRET_KEY` 重新生成 —— `python -c "import secrets;print(secrets.token_hex(32))"`。 2. **数据库**:生产换成独立 / 托管的 MySQL,把 `DATABASE_URL` 指过去。 3. **Redis**:浏览量防刷与登录限流都依赖它;它挂了会降级放行(fail-open), 但生产仍建议配好,别让它长期缺席。 4. **HTTPS + 反向代理**:前面挂 Nginx(示例见下),并让 Nginx **覆盖** `X-Forwarded-For` 后再转发。 5. **CORS**:页面与接口同源时不需要;用独立前端时才在 `.env` 配 `CORS_ORIGINS`。 6. **上传目录**:`UPLOAD_DIR` 指向持久卷,别放在容器临时层。 7. **多进程**:`uvicorn --workers N`,或换 gunicorn + uvicorn worker。 ### Nginx 反向代理示例 ```nginx server { listen 80; server_name your-site.com; client_max_body_size 5m; # 至少要大于上传上限(本项目 2MB) location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 关键:用 Nginx【覆盖】这个头,否则客户端能伪造它绕过限流 / 灌水计数 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` ## License 个人项目,随意参考。