From 9a4e6c3e6e6f97210799ab49bdf5521a02296d96 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 2 Jul 2026 11:40:09 +0800 Subject: [PATCH] =?UTF-8?q?docs(agents):=20=E6=A0=A1=E6=AD=A3=E6=9E=84?= =?UTF-8?q?=E5=BB=BA/=E6=B5=8B=E8=AF=95/=E8=BF=81=E7=A7=BB=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=EF=BC=8C=E5=AF=B9=E9=BD=90=E5=BD=93=E5=89=8D=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除不存在的 ./migrate.sh:迁移实际由 src/db/migrate.rs 在启动期自动运行, 目标库由 db/pool.rs::ensure_database_exists 自举创建;补充新增迁移需在 MIGRATIONS 数组登记(编译期校验磁盘与数组一致) - npm → pnpm:4 个 libs 均使用 pnpm-lock.yaml,Makefile 全程 pnpm - make test / make build 覆盖 4 个 libs(补充 codemirror-editor) - 补充 restore-webp 兜底步骤的成因(dx 0.7.9 重编码 .webp) - 补全 make clippy(-D warnings)、make fix、make clean 真实行为 - 模块树补齐 context/router/ssr_cache/sysinfo_sampler 及 *_bridge - 4 个前端子项目合并为对比表,保留 wasm-bindgen 接线等关键细节 - 保留 Workflow 提交规范 --- AGENTS.md | 133 ++++++++++++++++++++++----------------------------- src/theme.rs | 6 ++- 2 files changed, 62 insertions(+), 77 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1cf5320..2613164 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,26 +10,33 @@ ## Development Commands ```bash -make dev # tailwindcss watch + dx serve (needs PostgreSQL) -make build # build-editor → highlight-css → tailwindcss → doc → dx build --release -make build-linux # same as build but targets x86_64-unknown-linux-musl -make build-freebsd # cross-compile FreeBSD x86_64 server binary (clang + lld + sysroot) +make dev # 增量构建 4 个 libs + tailwindcss watch + dx serve (needs PostgreSQL, SSR_CACHE_SECS=0) +make build # build-editor → build-lightbox → build-core → build-codemirror → highlight-css → tailwindcss → doc → dx build --release → restore-webp +make build-linux # 客户端 + 服务端分离构建,target x86_64-unknown-linux-musl +make build-freebsd # cross-compile FreeBSD x86_64 server binary (clang + lld + sysroot, via cargo, not dx) make freebsd-sysroot # download/extract FreeBSD base.txz → .freebsd-sysroot/ (idempotent) -make css # one-shot CSS -make css-watch # watch mode -make test # cargo test + vitest (tiptap-editor + lightbox + yggdrasil-core libs) +make css # one-shot Tailwind build +make css-watch # Tailwind watch mode +make test # cargo test + pnpm test in all 4 libs (tiptap-editor / lightbox / yggdrasil-core / codemirror-editor) make doc # cargo doc (ayu 主题) → 拷贝到 public/doc/,随 build 发布 make doc-open # 同 doc,生成后自动用浏览器打开(本地预览,不拷贝) -make clean # cargo clean + rm public/style.css +make clippy # cargo clippy --all-targets --all-features -- -D warnings (严格模式,warning 即失败) +make fix # cargo fix --allow-dirty +make clean # cargo clean + rm public/{style.css,highlight.css,doc} + rm -rf uploads/.cache ``` -**Build order matters**: `make build` runs `build-editor` → `highlight-css` (`cargo run --bin generate_highlight_css`) → `tailwindcss --minify` → `doc` (`cargo doc` + 拷贝到 `public/doc/`) → `dx build --release`. Do not run `dx build --release` alone. +**Build order matters**: `make build` runs all 4 lib builds → `highlight-css` (`cargo run --bin generate_highlight_css`) → `tailwindcss --minify` → `doc` → `dx build --release` → `restore-webp`. Do not run `dx build --release` alone. + +**`restore-webp` workaround**: dx build 0.7.9 re-encodes `public/*.webp` into VP8L lossless stills (drops animation frames, 7-8× larger), contradicting the "verbatim copy" promise. `restore-webp` overwrites `.webp` in `target/dx/**/web/public/` from source `public/`. SVG/ICO are unaffected. Remove once upstream fixes it. + +**Libs use pnpm, not npm**: every lib has `pnpm-lock.yaml`; Makefile recipes use `pnpm ci`/`pnpm install`/`pnpm run`. The `*-incremental` targets skip install (assume `node_modules` present) for faster `make dev`. ## Prerequisites - Rust 1.95+ with `wasm32-unknown-unknown` target - `dx` CLI (`cargo install dioxus-cli`) - `tailwindcss` CLI v4 — install via `npm install -g @tailwindcss/cli` (v4 splits the CLI into its own package; the `tailwindcss` core package has no `bin`), or use the standalone binary +- `pnpm` (all 4 `libs/` subprojects use it) - PostgreSQL running locally ## Environment @@ -41,6 +48,10 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/yggdrasil RUST_LOG=info ``` +**Migrations run automatically at startup** — there is no `migrate.sh`. On boot `src/main.rs` calls `db::migrate::run_on_conn`, which applies `migrations/*.sql` in order (tracked in the `MIGRATIONS` array in `src/db/migrate.rs`). Before the pool is touched, `db::pool.rs::ensure_database_exists` connects to the `postgres` maintenance DB and `CREATE`s the target DB if missing (zero manual setup). The migration step is serialized across instances via an advisory lock and waits up to `MIGRATE_STARTUP_TIMEOUT_SECS` for PostgreSQL. + +**Adding a migration**: create `migrations/NNN_name.sql`, then add a `(version, include_str!("./../migrations/NNN_name.sql"))` row to the `MIGRATIONS` array in `src/db/migrate.rs`. A compile-time test asserts every `.sql` file on disk has a matching row (and vice versa), so forgetting the row fails the build. + Optional tuning via env vars (all have sane defaults): ``` @@ -61,7 +72,8 @@ RATE_LIMIT_UNKNOWN_BURST=100 DB_POOL_SIZE=20 # database connection pool size MIGRATE_STARTUP_TIMEOUT_SECS=30 # how long startup waits for PostgreSQL before giving up STATEMENT_TIMEOUT_SECS=30 # per-query timeout; slow queries are canceled to protect the pool -SSR_CACHE_SECS=3600 # incremental SSR cache TTL +SSR_CACHE_SECS=3600 # incremental SSR cache TTL (set 0 in dev) +SYSINFO_SAMPLE_SECS=0.5 # sysinfo sampling interval in seconds, supports decimals ``` Session / security tuning: @@ -72,19 +84,13 @@ TRUSTED_PROXY_COUNT=0 # number of reverse proxies in front of the app; use APP_BASE_URL= # e.g. https://your-domain.example — trusted origin for CSRF checks on write requests; unset falls back to Host header + X-Forwarded-Proto ``` -Run migrations before first dev server start: - -```bash -./migrate.sh # auto-creates DB, runs migrations/ in order -``` - ## Architecture: Conditional Compilation Dioxus 0.7 fullstack project with **two independent gates** — the most common source of compilation errors. | Gate | Applies to | Used for | |------|-----------|----------| -| `#[cfg(feature = "server")]` | Server binary only | DB, env loading, background tasks, server function bodies, highlight, WebP, caching | +| `#[cfg(feature = "server")]` | Server binary only | DB, env loading, background tasks, server function bodies, highlight, WebP, caching, sysinfo | | `#[cfg(target_arch = "wasm32")]` | WASM frontend only | localStorage, DOM APIs, web_sys calls, theme detection | **Critical**: Both default features (`web` + `server`) are enabled in `Cargo.toml`. The `dx` CLI handles feature selection during builds. @@ -93,13 +99,15 @@ Dioxus 0.7 fullstack project with **two independent gates** — the most common **Server-only helpers**: `src/auth/password.rs`, `src/auth/session.rs`, `src/api/auth.rs`, `src/api/comments/helpers.rs`, and several model helper methods are gated with `#[cfg(feature = "server")]` because they are only called from server function bodies, which are stripped in WASM builds. -**Server-only dependencies**: Crates that are only used behind `#[cfg(feature = "server")]` (e.g., `argon2`, `uuid`, `regex`, `pulldown-cmark`, `rand`, `http`, `sha2`, `hex`, plus the pre-existing optional server stack) are declared as `optional = true` in `Cargo.toml` and enabled only through the `server` feature. They are not compiled into the WASM frontend. +**Server-only dependencies**: Crates that are only used behind `#[cfg(feature = "server")]` (e.g., `argon2`, `uuid`, `regex`, `pulldown-cmark`, `rand`, `http`, `sha2`, `hex`, `sysinfo`, `sqlparser`, `dashmap`, plus the rest of the server stack) are declared as `optional = true` in `Cargo.toml` and enabled only through the `server` feature. They are not compiled into the WASM frontend. The `generate_highlight_css` binary is `required-features = ["server"]` (uses `syntect`). + +**Shared types that compile on both targets**: bridges like `src/tiptap_bridge.rs` and `src/codemirror_bridge.rs` keep shared structs (e.g. `UploadsInFlight`, `SqlSchema`/`SqlTable`) outside cfg gates, while wasm-bindgen externs + `EditorHandle` live in inner `#[cfg(target_arch = "wasm32")] mod wasm`. Similarly `src/sysinfo_sampler.rs` exposes `SystemSnapshot` on both targets but gates the actual sampler + `RwLock` behind `server`. ## Dual API Architecture The server exposes two distinct API patterns: -1. **Dioxus server functions** (`#[server(Name, "/api")]` in `src/api/`) — auto-routed, callable from both client and server Rust. Spread across `src/api/auth.rs`, `src/api/posts/`, `src/api/comments/`, and `src/api/settings.rs`. +1. **Dioxus server functions** (`#[server(Name, "/api")]` in `src/api/`) — auto-routed, callable from both client and server Rust. Spread across `src/api/auth.rs`, `src/api/posts/`, `src/api/comments/`, `src/api/settings.rs`, and `src/api/database/`. 2. **Axum routes** (registered in `src/main.rs`) — manual `axum::Router` for endpoints that don't fit the server-function model: - `POST /api/upload` — image upload (multipart, auth-required, rate-limited) @@ -111,6 +119,7 @@ The server exposes two distinct API patterns: src/api/ — server functions + Axum handlers auth.rs — login, register, session validation comments/ — comment CRUD + approval/spam/trash server functions + database/ — /admin/system backend (status/system_status/sql_console/schema/export/backup/tasks) markdown.rs — Markdown→HTML rendering (pulldown-cmark + ammonia sanitization) image.rs — image serving with processing pipeline + disk+memory cache upload.rs — image upload, auto-converts to WebP @@ -120,63 +129,35 @@ src/api/ — server functions + Axum handlers posts/ — CRUD server functions for blog posts src/auth/ — password hashing (Argon2) + session token management src/bin/ — generate_highlight_css (build-time CSS generation) -src/cache.rs — moka future-based caches for posts, tags, stats +src/cache.rs — moka future-based caches for posts, tags, stats + cache_stats() src/components/ — Dioxus UI components -src/db/ — PostgreSQL pool (deadpool-postgres, LazyLock global) +src/context.rs — shared Dioxus context/state +src/db/ — PostgreSQL pool (deadpool-postgres, LazyLock global) + migrate.rs + pool.rs (ensure_database_exists) src/hooks/ — shared Dioxus hooks src/models/ — Post, User, Tag data models src/pages/ — route page components (frontend + admin) +src/router.rs — Dioxus Router route definitions +src/ssr_cache.rs — SSR generation invalidation state (server feature only) +src/sysinfo_sampler.rs — host metrics snapshot; SystemSnapshot on both targets, sampler+RwLock server-only src/tasks/ — background tokio tasks (session cleanup) src/theme.rs — light/dark theme with SSR cookie + WASM localStorage src/webp.rs — zenwebp encode/decode (image crate has no WebP) +src/tiptap_bridge.rs — wasm-bindgen bindings for Tiptap editor +src/codemirror_bridge.rs — wasm-bindgen bindings for CodeMirror editor (mirrors tiptap_bridge) ``` -## Tiptap Editor Subproject +## Frontend Lib Subprojects -Rich-text editor in `libs/tiptap-editor/`, built as an IIFE library exposing `window.TiptapEditor`. +Four Vite-built IIFE libraries under `libs/`, each with `pnpm-lock.yaml`. Built artifacts go to `public//` — **do not edit `public//` files; they are build artifacts**. Each `build` script is `tsc --noEmit && vite build` (type-check before bundle). Output is IIFE because Dioxus `[web.resource] script` injects bare `