diff --git a/docs/implementation-status.md b/docs/implementation-status.md index ce4d44874..b97783516 100644 --- a/docs/implementation-status.md +++ b/docs/implementation-status.md @@ -7,7 +7,7 @@ Update this file before each feature commit. | Scaffold | Completed | initial scaffold commit | `cargo fmt --check`; `cargo test`; `cargo clippy --all-targets --all-features --locked -- -D warnings` | Rust crate, lint policy, pinned TLS-sensitive dependencies, `.gitignore`, and minimal entry points are in place. | | API contract docs | Completed | initial scaffold commit | `cargo fmt --check`; `cargo test`; `cargo clippy --all-targets --all-features --locked -- -D warnings` | `/v1/*` OpenAI-compatible body, `/admin/*` frontend envelope, body codes, camelCase, and request ID policy are documented. | | Configuration | Completed | pending config commit | `cargo test default_config_keeps_only_codex_backend`; full `fmt/test/clippy` before commit | Defines Codex-only config, default YAML, and `Arc` state shell. | -| SQLite storage | Planned | | | | +| SQLite storage | Completed | pending storage commit | `cargo test migrations_create_accounts_and_event_tables`; full `fmt/test/clippy` before commit | Creates SQLite WAL connector, migrations for accounts/API keys/sessions/cookies/fingerprints/events, and event/account indexes. | | Admin auth and client API keys | Planned | | | | | Secret encryption | Planned | | | | | Logging and pagination | Planned | | | | diff --git a/docs/superpowers/plans/2026-06-11-codex-proxy-rs.md b/docs/superpowers/plans/2026-06-11-codex-proxy-rs.md index 84e366fcf..d57253b06 100644 --- a/docs/superpowers/plans/2026-06-11-codex-proxy-rs.md +++ b/docs/superpowers/plans/2026-06-11-codex-proxy-rs.md @@ -929,7 +929,7 @@ git commit -m "feat: add codex-only configuration" - Modify: `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs` - Test: `/home/zyy/桌面/Codes/codex-proxy-rs/tests/storage_migration_test.rs` -- [ ] **Step 1: Write failing migration test** +- [x] **Step 1: Write failing migration test** Create `/home/zyy/桌面/Codes/codex-proxy-rs/tests/storage_migration_test.rs`: @@ -951,7 +951,7 @@ async fn migrations_create_accounts_and_event_tables() { } ``` -- [ ] **Step 2: Run test and verify RED** +- [x] **Step 2: Run test and verify RED** Run: @@ -961,7 +961,7 @@ cargo test migrations_create_accounts_and_event_tables Expected: compile failure because `storage::db` does not exist. -- [ ] **Step 3: Create migrations** +- [x] **Step 3: Create migrations** Create `/home/zyy/桌面/Codes/codex-proxy-rs/migrations/0001_initial.sql`: @@ -1068,7 +1068,7 @@ create index if not exists idx_client_api_keys_prefix on client_api_keys(prefix) create index if not exists idx_account_cookies_account on account_cookies(account_id); ``` -- [ ] **Step 4: Implement database connector** +- [x] **Step 4: Implement database connector** Modify `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs`: @@ -1103,7 +1103,7 @@ pub async fn connect_sqlite(database_url: &str) -> Result Result { + let options = SqliteConnectOptions::from_str(database_url)? + .create_if_missing(true) + .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) + .foreign_keys(true) + .busy_timeout(Duration::from_secs(5)); + + // 中文注释:WAL 和外键必须在连接层统一启用,避免后续仓储绕过一致性约束。 + let pool = SqlitePool::connect_with(options).await?; + sqlx::migrate!("./migrations").run(&pool).await?; + Ok(pool) +} diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 000000000..dec10232b --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1 @@ +pub mod db; diff --git a/tests/storage_migration_test.rs b/tests/storage_migration_test.rs new file mode 100644 index 000000000..af2241f0d --- /dev/null +++ b/tests/storage_migration_test.rs @@ -0,0 +1,17 @@ +use codex_proxy_rs::storage::db::connect_sqlite; + +#[tokio::test] +async fn migrations_create_accounts_and_event_tables() { + let dir = tempfile::tempdir().unwrap(); + let db = dir.path().join("test.sqlite"); + let url = format!("sqlite://{}", db.display()); + let pool = connect_sqlite(&url).await.unwrap(); + + let row: (i64,) = sqlx::query_as( + "select count(*) from sqlite_master where type = 'table' and name in ('accounts', 'client_api_keys', 'event_logs')", + ) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(row.0, 3); +}