后端面板开发

功能需求

  1. 前端创建一个URL路径,但不会在页面显示管理员登录功能,只有输入正确的URL才能进入登录界面;
  2. 登录界面进行登录,发送的请求传到后端对应的API接口,和数据库里存储的管理员账户和密码进行比对(数据库里只有一张存储管理员登录信息的表);
  3. 登陆成功后进入管理员前端界面,界面内容和一般用户访问看到的不同,只能看到文章列表(只显示关键信息: 标题、作者、发布时间等等,即前端定义的数据格式:yukina\src\content.config.ts下的posts.schema,仍然按照一级、二级分类划分),但是多了对文档的编辑、删除、新建功能;
  4. 新建功能会在前端post/目录下新建一篇md文档(在后端编写完成后推送给前端)
  5. 编辑和删除功能: 在页面上点击某一篇文章时,后端请求该篇文章的完整内容,前端响应并返回文章完整内容(包括文章的id,方便后续的操作),编辑或删除后,会将该篇文章发送给前端,前端接收后覆盖或者删除该id的文章

工程化翻译

  1. 隐藏式管理员入口:创建隐藏的URL路径,不在页面显示登录入口
  2. 身份认证:JWT认证,与SQLite数据库中的用户表进行密码验证
  3. 受保护的文章管理界面:登录后显示文章列表,按二级分类组织,支持编辑/删除/新建
  4. 文章CRUD操作:直接操作yukina\src\contents\posts目录下的.md文件
  5. 自动重建:文章操作后自动触发pnpm run build重建前端

后端架构

核心架构原则

  1. Layered Architecture:

    • 路由层 (api) 负责定义 API 接口和处理 HTTP 请求/响应
    • 服务层 (Services) 负责实现核心业务逻辑(如文件操作)
    • 数据访问层 (Data) 负责与数据库交互
    • 模式层 (Schemas) 负责定义数据结构。
  2. 依赖注入:

    ​ 深度利用 FastAPI 的依赖注入系统来管理数据库会话、获取当前登录用户等,这使得代码解耦、易于测试。

  3. 配置驱动:

    ​ 所有敏感信息(数据库 URL、JWT 密钥)和环境相关配置(Astro 项目路径)都将通过配置文件或环境变量进行管理,绝不硬编码。

  4. API 合约优先:

    ​ 使用 Pydantic 模型来精确定义所有 API 的输入和输出。这不仅提供了自动的数据验证和文档生成,还构成了前后端之间清晰的“数据合约”。

项目结构

/webTest/
├── /backend/                  <-- FastAPI 项目
   ├── app/
   ├── __init__.py
   ├── main.py            // FastAPI 应用入口
   ├── core/              // 核心配置与安全
   ├── config.py      // 应用配置 (环境变量)
   └── security.py    // 密码哈希, JWT 创建/验证
   ├── data/              // 数据库相关
   ├── database.py    // 数据库引擎与会话管理
   └── models.py      // SQLAlchemy 数据模型 (User )
   ├── api/           	   // API 路由层
   ├── auth.py        // 认证路由 (/token)
   └── posts.py       // 文章管理路由 (/api/admin/posts)
   ├── schemas/           // Pydantic 数据合约层
   ├── post.py        // Post 相关的 Pydantic 
					  模型
   └── user.py        // User 相关的 Pydantic 
						  模型
   └── services/          // 业务逻辑层
       ├── post_service.py// 核心文件操作逻辑
       └── user_service.py// 用户认证业务逻辑
   ├── .env                   // 环境变量文件
   ├── Dockerfile             #  构建镜像的清单
   ├── docker-compose.yml     #  本地开发的启动器
   └── requirements.txt       # Python 依赖清单

└── /yukina/                 <-- Astro 项目
    ├── src/
   ├── content/
   └── posts/          <-- [后端的主要操作目标]
   └── ...
    └── ...

功能实现思路

1. & 2. 管理员登录与认证

此功能的核心是**认证 **,确认“你是谁”。

  • 数据库 (data/models.py): 定义一个 User SQLAlchemy 模型,至少包含 id, username, hashed_password 字段。

    注意:数据库只存储哈希后的密码。

  • 安全层 (core/security.py):

    • 提供 verify_password() 和 get_password_hash() 函数 (使用 passlib)。
    • 提供 create_access_token() 和一个核心的 get_current_user() FastAPI 依赖项。此依赖项负责解码和验证 Authorization 头中的 JWT。
  • 服务层 (services/user_service.py):

    • 实现 authenticate_user(db, username, password) 函数。它会从数据库中查找用户,如果找到,则调用 verify_password() 来比对密码。
  • 路由层 (api/auth.py):

    • 创建一个 POST /token 接口。
    • 它接收 OAuth2PasswordRequestForm 格式的表单数据(用户名和密码)。
    • 调用 user_service.authenticate_user() 进行验证。
    • 如果成功,调用 security.create_access_token() 生成 JWT 并返回给前端。
    • 如果失败,返回 HTTP 401 Unauthorized 错误。

3.登录后获取受保护的文章列表

此功能的核心是授权,确认“你有什么权限”。

  • 配置 (core/config.py): 定义一个 ASTRO_CONTENT_PATH 变量,指向 yukina\src\contents\posts 的绝对路径。
  • 模式层 (schemas/post.py):
    • 创建一个 PostMetadata Pydantic 模型,其字段应严格匹配前端 content/config.ts 中定义的 posts.schema(如 title, author, pubDate, tags 等)。这是前后端的数据合约。
  • 服务层 (services/post_service.py):
    • 实现 get_all_posts_metadata() 函数。它会遍历 ASTRO_CONTENT_PATH 目录下的所有 .md 文件,使用 python-frontmatter 库**只读取每个文件的元数据 **,然后将这些元数据解析并构造成 PostMetadata 对象的列表。
  • 路由层 (api/posts.py):
    • 创建一个 GET /api/admin/posts 接口。
    • 关键: 为此接口添加依赖项 Depends(security.get_current_user)。这确保了只有携带有效 JWT 的请求才能访问。
    • 接口内部调用 post_service.get_all_posts_metadata() 并返回结果。

4. & 5. 文章的创建、编辑与删除 (CRUD)

这是后端的核心业务逻辑,直接操作文件系统。

​ 后端直接在服务器的文件系统上(即 yukina\src\contents\posts 目录)创建、修改或删除 .md 文件。这些文件是 Astro 项目的源代码。操作完成后,后端会触发一个命令,告诉 Astro 重新构建(pnpm run build) 整个静态网站,从而使更改生效。

  • 模式层 (schemas/post.py):

    • PostCreate: 定义新建文章时,前端需要发送到后端的数据结构(如 title, tags, content)。
    • PostFull: 定义包含完整元数据和内容的文章数据结构,用于编辑功能。
  • 服务层 (services/post_service.py):

    • create_post(post_data: PostCreate):

      1. 根据 title 生成一个 URL 友好的 slug。
      2. 拼接 Markdown 字符串(Frontmatter + --- + content)。
      3. 将字符串写入到 ASTRO_CONTENT_PATH 下的 {slug}.md 文件中。
      4. 调用 trigger_astro_rebuild() 函数。
    • get_post_by_slug(slug):

      ​ 读取 {slug}.md 文件,解析其内容并返回一个 PostFull 对象。

    • update_post(slug, post_data):

      ​ 逻辑与 create_post 类似,但它会覆盖已存在的文件。

    • delete_post(slug):

      ​ 使用 os.remove() 删除 {slug}.md 文件,然后调用 trigger_astro_rebuild()

    • trigger_astro_rebuild(): 内部使用 Python 的 subprocess 模块,在 ../yukina/ 目录下执行 pnpm run build 命令。这是实现自动化的关键。

  • 路由层 (api/posts.py):

    • POST /api/admin/posts: 接收 PostCreate 数据,调用 post_service.create_post()。受 JWT 保护。
    • GET /api/admin/posts/{slug}: 调用 post_service.get_post_by_slug()。受 JWT 保护。
    • PUT /api/admin/posts/{slug}: 接收 PostCreate 数据,调用 post_service.update_post()。受 JWT 保护。
    • DELETE /api/admin/posts/{slug}: 调用 post_service.delete_post()。受 JWT 保护。

数据流与职责

  1. 启动: FastAPI 应用启动,加载所有路由和配置。

  2. 登录:

    前端 POST /token -> api.auth -> services.user 验证 -> core.security 生成 JWT -> 返回前端。
  3. 受保护的请求:

    前端携带 JWT 请求 GET /api/admin/posts -> core.security.get_current_user 验证 JWT -> api.posts -> services.post 读取文件元数据 -> 返回前端。
  4. 写操作:

    前端 POST /api/admin/posts 并携带文章数据 -> core.security 验证 JWT -> api.posts 接收数据 -> services.post 创建 .md 文件 -> services.post 触发 npm run build -> 返回成功响应给前端。

后端环境搭建

前情提要

​ 搭建好的后端和现在的前端是要同一打包部署到树莓派4b上的(Linux ARM64系统),因此需要使用Docker完成以下内容:

  1. 在PC的vscode上编写代码(本地开发)
  2. 最终部署到树莓派4b

解决方案

​ 使用 Docker 的多平台构建 (Multi-platform Builds) 功能,在 x86 架构的 PC 上,开发并构建出能在 ARM 架构的树莓派上原生运行的 Docker 镜像。只需要在 PC 上编写代码,剩下的环境一致性问题全部交给 Docker 处理。

核心理念:Build Once, Run Anywhere

​ 目标是创建一个自包含的、可移植的后端应用包(一个 Docker 镜像)。这个包里面包含了 FastAPI 应用、所有 Python 依赖、以及一个标准化的运行环境。

  • PC (开发环境):

    ​ 使用 Docker Compose 来启动环境。

    ​ 关键在于,将 PC 上的源代码目录挂载 (mount)到 Docker 容器内部。这可以在代码编辑器(如 VS Code)中修改代码,而容器内的 FastAPI 服务会实时热重载 (hot-reloading),立即反映修改的代码效果。

  • 树莓派 (生产环境):

    ​ 不会把代码复制到树莓派上。而是直接将在 PC 上**交叉编译 (cross-compile)**好的、针对树莓派 ARM 架构的 Docker 镜像,从镜像仓库(如 Docker Hub)拉取下来,然后直接运行。

项目结构搭建

项目结构即前文提到的”后端架构=>项目结构”部分

所有后端代码,都将在 backend/app/ 目录中编写

编写 Docker 环境定义文件

后续讲述到集成部署时会构建项目Dockerfile架构,会部分后端的Dockerfile和.yml文件

  • Dockerfile: 定义如何从零开始构建应用的运行环境。----后端独立开发使用,集成时作为子文件使用

    # 选择一个官方的、支持多平台架构的 Python 基础镜像
    # python:3.11-slim-bookworm 是一个很好的选择,它同时有 amd64(PC) 和 arm64(Pi) 版本
    FROM python:3.11-slim-bookworm
    
    # 设置容器内的工作目录
    WORKDIR /code
    
    # 设置 PIP 清华源以加速(可选)
    RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
    
    # 复制依赖文件并安装依赖
    # 将这一步分开是为了利用 Docker 的层缓存机制,只要 requirements.txt 不变,就不需要重新安装
    COPY ./requirements.txt /code/requirements.txt
    RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
    
    # 复制所有的应用源代码到工作目录
    COPY ./app /code/app
    
    # 暴露 FastAPI 应用运行的端口 (通常是 8000)
    EXPOSE 8000
    
    # 定义容器启动时要执行的命令
    # 使用 uvicorn 启动应用。--host 0.0.0.0 使其可以从外部访问。
    CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
  • docker-compose.yml: 在 PC 上一键启动和管理开发环境。

    version: '3.8'
    services:
      backend:
        # 告诉 Docker Compose 使用当前目录下的 Dockerfile 来构建镜像
        build: .
        # 命名容器,方便管理
        container_name: blog_backend_dev
        # 端口映射:将 PC的 8000 端口映射到容器的 8000 端口
        ports:
          - "8000:8000"
        # 卷挂载
        # 将PC上的 ./app 目录,实时同步到容器内的 /code/app 目录
        volumes:
          - ./app:/code/app
          - ./data:/code/data
        # 覆盖 Dockerfile 中的 CMD,使用 --reload 参数开启热重载
        command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

本地开发工作流 (在 PC 上)

  1. 初始化:
    • 在 backend/ 目录下,创建 requirements.txt 文件并加入 fastapi 和 uvicorn[standard]。
    • 在 backend/app/ 目录下,创建一个最小化的 main.py 文件用于测试。
  2. 启动开发环境:
    • 在 PC 上打开终端,cd 进入 backend 目录。
    • 运行命令:docker-compose up —build
    • Docker 将会: a. 根据 Dockerfile 构建一个本地开发用的镜像。 b. 根据 docker-compose.yml 启动一个容器。 c. 将 app 目录挂载进去,并以热重载模式启动 FastAPI。
  3. 开始编码:
    • 现在,你可以在 PC 上使用任何编辑器打开 /webTest/backend/app/ 目录并开始编写FastAPI 代码。
    • 每当你保存一个文件,终端中运行的 uvicorn 会自动检测到变化并重新加载服务。
    • 你可以通过浏览器访问 http://localhost:8000 来测试API。

后端构建行动清单 (TODO List)

阶段 0:环境搭建与验证 ---- 已完成✅

1. 安装 Docker Desktop

2. 创建项目目录结构:

  • 创建一个总项目文件夹,例如 webTest。
  • 在其中创建 backend 和 frontend 两个文件夹。

3. 初始化 backend 目录:

  • 进入 backend 目录。

  • 创建 Dockerfile 文件,复用前文代码。

  • 创建 docker-compose.yml 文件,复用前文代码。

  • 创建 requirements.txt 文件,并填入初始内容:

    fastapi
    uvicorn[standard]
    python-jose[cryptography]
    passlib[bcrypt]
    pydantic-settings
    sqlalchemy
    python-frontmatter
    # 如果要连接PostgreSQL,还需 psycopg2-binary

4. 创建最小化应用并首次启动:

  • 在 backend 目录下创建 app 文件夹。

  • 在 app 文件夹内创建 main.py 文件,并填入一个最简单的 “Hello World” 应用。

    from fastapi import FastAPI
    app = FastAPI()
    @app.get("/")
    def root():
        return {"message": "Backend is running!"}
  • 在 VS Code 中打开终端,cd 到 backend 目录,运行 docker-compose up --build

  • 验证: 打开浏览器访问 http://localhost:8000。如果看到 {“message”:“Backend is running!”},则开发环境已成功搭建,按 Ctrl+C 停止服务。

阶段 1:构建应用骨架✅

1. 搭建目录结构: 在 backend/app/ 目录下,创建以下文件夹和空的 __init__.py 文件,形成清晰的模块化结构:

app/
├── core/
│   └── __init__.py
├── data/
│   └── __init__.py
├── api/
│   └── __init__.py
├── schemas/
│   └── __init__.py
└── services/
    └── __init__.py

2. 核心配置 (app/core/config.py):

​ 创建此文件,使用 Pydantic Settings 定义应用配置,包括 JWT 密钥、算法、过期时间,以及最重要的Astro 内容目录的绝对路径

3. 数据库设置 (app/data/):

注意:表已经手动创建,位于”webTest\backend\data\dataBase.db”,表结构为:

CREATE TABLE users (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- 主键
    username TEXT NOT NULL UNIQUE,                 -- 唯一的用户名
    hashed_password TEXT NOT NULL
)
  • 在 database.py 中,设置 SQLAlchemy 的数据库引擎和会话管理 (get_db 依赖项)。
  • 在 models.py 中,定义 User 模型,包含 id, username, hashed_password 字段。
  • Pro-Tip: 此时可以写一个小脚本,或在首次运行时手动创建一个管理员用户并将其密码哈希后存入数据库。

阶段 2:实现核心服务 ✅

1. 安全与认证服务 (app/core/security.py):

  • 实现密码哈希与验证函数 (get_password_hash, verify_password)。
  • 实现 JWT 创建函数 (create_access_token)。
  • 实现核心的 get_current_user FastAPI 依赖项,它将负责解码 JWT 并从数据库中获取用户信息

2. 用户服务 (app/services/user_service.py):

  • 创建 authenticate_user(db, username, password) 函数。此函数将是登录接口的核心逻辑,它调用数据库和安全服务来验证用户凭据

3. 文章服务 (app/services/post_service.py):

  • 实现 get_all_posts_metadata(): 遍历文件目录,只读取并返回所有文章的 Frontmatter。
  • 实现 get_post_by_slug(slug): 读取并返回单篇文章的完整内容。
  • 实现 create_post(post_data): 拼接字符串并创建新的 .md 文件。
  • 实现 update_post(slug, post_data): 覆盖写入已有的 .md 文件。
  • 实现 delete_post(slug): 删除指定的 .md 文件。
  • 实现 trigger_astro_rebuild(): 使用 subprocess 模块执行 npm run build 命令。确保在 create, update, delete 操作成功后调用此函数。

阶段 3:暴露 API 接口✅

1. 定义数据合约 (app/schemas/):

  • 创建 post.py 和 user.py。
  • 定义 PostMetadata, PostCreate, PostFull, Token, User 等所有与 API 交互的 Pydantic 模型。

2. 认证路由 (app/api/auth.py):

  • 创建 POST /token 接口。
  • 它不应被 JWT 保护。
  • 调用 user_service.authenticate_user 进行验证,成功后返回 create_access_token 生成的令牌。

3. 文章管理路由 (app/api/posts.py):

  • 创建 GET /api/admin/posts
  • 创建 POST /api/admin/posts
  • 创建 GET /api/admin/posts/{slug}
  • 创建 PUT /api/admin/posts/{slug}
  • 创建 DELETE /api/admin/posts/{slug}
  • 关键: 为所有这些接口都加上 Depends(get_current_user) 依赖项,确保它们被安全保护。

4. 组装应用 (app/main.py):

  • 清空 main.py 的 “Hello World” 代码。
  • 创建 FastAPI 主应用实例。
  • 使用 app.include_router() 将 auth.py 和 posts.py 中定义的路由包含进来。
  • 配置 CORS 中间件,允许前端域名访问。

阶段 4:联调与验证 ✅

1. 重启开发环境: 在 VS Code 终端中,运行 docker-compose up。此时后端应用应该已在运行。

2. 单元测试 (使用 Swagger UI):

  • 打开浏览器访问 http://localhost:8000/docs。
  • 测试认证: 使用 /token 接口,输入创建的管理员用户名和密码,获取一个 JWT。
  • 测试保护: 尝试在未授权的情况下访问 GET /api/admin/posts,应该会收到 40x 错误。
  • 测试完整流程: 点击右上角的 “Authorize” 按钮,粘贴获取的 JWT。然后逐一测试文章管理的每一个接口,确保它们都按预期工作

3. 前后端联调:

  • 现在可以开始编写前端的 services 层代码,让前端管理页面真实地调用已经验证过的后端 API。

bugs 日志

Dev 下的渲染故障-----重点⚠️

问题描述

​ 使用svelte构建组件并成功构造管理员面板,而且能够实时修改/添加/删除md文档内容后:在dev模式下,不论是修改/添加文章,还是删除文章,都会导致页面渲染故障

原因—Cross-Process FS Event Storm

跨进程文件系统事件风暴,这是一个在高级开发环境中非常微妙的“跨进程竞争条件”。

​ 可以把 Astro Dev Server (Node.js/Vite)FastAPI 后端 (Python) 想象成两个说不同语言、工作节奏也完全不同的高效团队,它们都在盯着同一块白板(文件系统)。

  1. Astro Dev Server 团队:

    这个团队反应极快,内部沟通效率极高(基于 Node.js 事件循环)。

    使用一个超高灵敏度的监控摄像头(文件监视器 chokidar)盯着白板。

    只要白板上有一丁点改动,哪怕只是擦掉一个点,摄像头都会立即捕捉到,并通知整个团队(HMR 系统)进行响应。团队内部有复杂的缓存和状态来保证极速响应。

  2. FastAPI 后端团队:

    ​ 这个团队在另一个房间工作。当需要修改白板时,他们会派一个人(Python 进程)跑过去,直接在白板上进行涂改(创建、修改、删除文件)。

冲突点就在于: Python 进程对文件的操作,在操作系统层面产生的“通知”(FS Event),对于 Astro 的高灵敏监控摄像头来说,可能是一种“野蛮”的、非标准的信号。

  • 事件风暴:

    ​ 一个简单的 os.remove,在底层可能会触发多个文件系统事件,或者事件的顺序和时机与 Astro 预期的(比如由 VS Code 保存文件产生的事件)有所不同。

  • 缓存竞争:

    ​ 当 Astro 的监控摄像头捕捉到这个“野蛮信号”时,它会立即命令团队去读取白板的最新状态。但此时,Python 进程可能还没完全“离开”现场,或者操作系统还没完全同步好文件的元数据。这导致 Astro 团队读取到了一个中间状态损坏状态的信息。

  • 内部状态崩溃:

    ​ Astro 团队基于这个错误的信息去更新自己的内部缓存和依赖图,导致其内部逻辑彻底混乱。此时,无论浏览器是接收 HMR 推送还是主动刷新,得到的都是一份来自这个混乱大脑的“精神错乱”的页面数据,最终导致渲染故障。

这就是为什么连最简单的 delete 操作也会让它崩溃的原因。 问题不在于操作本身,而在于操作是由一个“局外人”(Python 进程)发起的。

解决方案

  1. 原子化部署(Atomic Deployment)

    ​ 专业的解决方案是确保部署(即用新文件替换旧文件)这个动作是原子性的——也就是说,它必须在一个瞬间完成,不能有中间状态。

    对后端 backend\app\services\post_service.py的trigger_astro_rebuild() 函数进行升级,采用一个简单而极其有效的“先构建到临时目录,再瞬间替换”的策略。

  2. 让外部修改“更友好”

    ​ 既然无法改变 Astro Dev Server 的核心机制,就需要让后端的写入操作尽可能地“原子化”和“干净”,以减少对文件监视器的干扰。

    ​ 对后端的文件写入逻辑进行一个小而关键的升级:先写入临时文件,再重命名。os.rename 操作对于大多数操作系统来说,是一个原子操作

    ​ 这意味着文件系统监视器只会看到一个单一、瞬时的“文件出现”事件,而不是一个持续的“文件正在被写入”的过程。

    ​ 对后端 backend\app\services\post_service.py的update_postcreate_post修改: 使用os.rename操作。

    ​ 通过这个修改,Astro 的文件监视器将不会再观察到“一个文件正在被慢慢写入”的过程。它只会在 os.rename 或 os.replace 完成的那一瞬间,看到一个单一的、完整的事件:

    • 对于 create_post:一个全新的、内容完整的 .md 文件突然“出现”了。

    • 对于 update_post:一个已有的 .md 文件的内容突然“变”了。

      这个干净利落的事件可以被 Astro Dev Server 更可靠地处理,大大降低了其内部状态发生混乱的概率,从而解决了渲染故障的问题。

生产模式下的工作流:清晰、线性、无冲突

​ 在生产模式下(即在树莓派/服务器上部署时),整个流程是单向、确定且无竞争的。没有“热模块重载”,没有“文件监视器”,只有一个清晰的目标:生成一套全新的静态网站并替换掉旧的

在管理员面板点击“发布”、“更新”或“删除”时,会发生以下一系列事件:

  1. [前端] 发送指令:
    • Svelte 管理界面向 FastAPI 后端发送一个 API 请求(例如 POST /api/admin/posts)。
  2. [后端] 执行文件操作:
    • FastAPI 接收到请求,并调用 post_service.py 中的函数。
    • create_post, update_post 或 delete_post 函数直接、安全地在 yukina/src/contents/posts/ 目录下对 .md 文件进行创建、修改或删除。
    • 关键点: 在这个阶段,公开的网站完全不受影响。因为 Nginx 仍然在为所有访客提供旧的、位于 dist 目录下的静态文件。后台 src 目录的变化,访客是无感知的。
  3. [后端] 触发构建脚本:
    • 文件操作成功后,代码会执行到 trigger_astro_rebuild() 这一步。
    • trigger_astro_rebuild 函数被调用。它启动了一个独立的 pnpm run build 进程。
  4. [构建进程] 在“幕后”工作:
    • Astro 的构建脚本开始运行。它会: a. 读取 src 目录下的所有内容(包括刚刚做的修改)。 b. 在内存中构建所有页面。 c. 将最终的、完整的、全新的 HTML/CSS/JS/图片文件输出到一个指定的输出目录(例如 dist 或之前优化的 dist_new)。
    • 关键点: 在 build 命令运行的这几秒到几十秒内,旧的 dist 目录仍然在线,网站服务从未中断
  5. [部署] 原子化替换:
    • 当 pnpm run build 成功结束后,trigger_astro_rebuild 脚本的后续部分会执行。
    • 它会在一瞬间,用 os.rename 将新构建好的 dist_new 目录重命名为 dist,同时将旧的 dist 目录备份为 dist_old。
    • 关键点: 这个切换是原子性的。对于正在处理访问请求的 Nginx 来说,dist 目录的内容仿佛是在一个纳秒内瞬间更新了。
  6. [完成] 新版本上线:
    • 从切换完成的那一刻起,所有新的网站访问请求,都会由 Nginx 从这个全新的 dist 目录中提供服务。
    • 访客将看到包含最新修改的网站内容。

为什么生产模式没有渲染故障❓️

  1. 没有 HMR,没有实时监听:

    ​ 生产模式下不存在那个高灵敏度的文件监视器和热模块重载系统。Astro 只在 pnpm run build 被调用时,一次性地读取所有文件。因此,不存在 Python 进程与 Node.js 进程之间关于文件读写的竞争。

  2. 读写分离:

    ​ 整个流程是先写后读。FastAPI 先完成对 src 目录的所有写入操作,然后才启动一个独立的构建进程去读取这些已经稳定下来的文件。

  3. 服务与构建分离:

    Nginx Web 服务和 Astro 的构建进程是完全分离的两个东西。Nginx 只关心 dist 目录,而构建进程只关心 src 目录和输出到 dist_new。它们在工作时互不干扰,直到最后一刻才进行原子切换。

需要确保:

  • 使用原子化的 trigger_astro_rebuild 函数

    ​ 使用“先构建到 dist_new,再 os.rename”的 Python 脚本。这可确保即使在构建过程中有用户访问,网站也不会出现一秒钟的“花屏”或404。

  • 确保 settings.ENVIRONMENT 变量在树莓派上被正确设置为 “production”

    ​ 这可以通过环境变量来实现,是最灵活的方式。

后续前后端集成时证明这点是对的


后端API登录返回500状态码

问题已成功解决

问题:

由于 bcrypt 版本兼容性问题,后端 API 在登录时返回 500 内部服务器错误 passlib[bcrypt]==1.7.4 和 bcrypt==5.0.0 之间的兼容性问题。

解决方案:

  1. 更新 requirements.txt 文件以使用兼容版本,修复 bcrypt 版本兼容性问题:

    • passlib==1.7.4(不包含 [bcrypt] extras)
    • bcrypt==4.1.3(特定兼容版本)
  2. 重建 Docker 容器,并添加正确的依赖项

  3. 通过测试验证身份验证是否有效:

    • ✅ 登录端点:使用 admin/admin 凭证的 POST /token

    • ✅ 受保护端点:使用 JWT 令牌的 GET /api/admin/posts

    • ✅ API 文档:可访问 GET /docs

📋总结

🏗️技术架构

分层架构设计:

  • 路由层 (api/):处理HTTP请求/响应
  • 服务层 (services/):核心业务逻辑(文件操作)
  • 数据访问层 (data/):数据库交互
  • 模式层 (schemas/):Pydantic数据合约

📝 数据结构约定

前端Content Schema(必须严格匹配):

  {
    title: string,           // 必填
    published: Date,         // 必填
    description?: string,
    tags?: string[],
    first_level_category: string,    // 必填,一级分类
    second_level_category: string,   // 必填,二级分类
    author?: string,
    draft?: boolean,
    cover?: string,
    sourceLink?: string,
    licenseName?: string,
    licenseUrl?: string,
    readingMetadata?: { time: number, wordCount: number }
  }

📝数据库结构

SQLite数据库:webTest\backend\data\dataBase.db

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT NOT NULL UNIQUE,
  hashed_password TEXT NOT NULL
);

📝环境配置

关键路径:

  • Astro内容目录:D:\Coding\Wrote_Codes\webTest\yukina\src\contents\posts
  • 数据库路径:D:\Coding\Wrote_Codes\webTest\backend\data\dataBase.db
  • 重建命令:在../yukina/目录下执行pnpm run build

🚀 部署目标

Docker多平台部署:

  • 开发环境:PC (x86) + Docker Compose + 热重载
  • 生产环境:树莓派4B (ARM64) + Docker镜像

核心技术栈:

  • FastAPI + SQLAlchemy + JWT认证
  • python-frontmatter(处理Markdown前置信息)
  • Docker + Docker Compose
Author

JuyaoHuang

Publish Date

10 - 01 - 2025