From 45e4b36225eea06f3807ebccdcbcc17ba8d2e14a Mon Sep 17 00:00:00 2001
From: Zyy
Date: Fri, 10 Jul 2026 19:00:14 +0800
Subject: [PATCH] refactor: complete postgresql redis migration
Co-authored-by: Codex
---
.github/workflows/ci.yml | 26 +
README.md | 35 +-
backend/Cargo.lock | 99 +
backend/Cargo.toml | 4 +-
backend/{build => }/build.rs | 0
backend/scripts/check_architecture.sh | 59 +
.../accounts/model.rs => accounts/account.rs} | 0
backend/src/accounts/cookies/mod.rs | 5 +
.../cookies.rs => accounts/cookies/store.rs} | 70 +-
.../importing.rs => accounts/import.rs} | 4 +-
.../service => accounts/manage}/cookies.rs | 4 +-
.../manage/export.rs} | 18 +-
.../manage/import.rs} | 116 +-
.../service => accounts/manage}/lifecycle.rs | 173 +-
backend/src/accounts/manage/mod.rs | 15 +
.../service => accounts/manage}/oauth.rs | 46 +-
.../testing.rs => accounts/manage/probe.rs} | 34 +-
.../service => accounts/manage}/quota.rs | 16 +-
.../manage}/quota_view.rs | 68 +-
.../mod.rs => accounts/manage/service.rs} | 106 +-
.../contracts.rs => accounts/manage/types.rs} | 56 +-
backend/src/accounts/mod.rs | 13 +
backend/src/{upstream => }/accounts/pool.rs | 725 +------
backend/src/accounts/pool/runtime.rs | 593 ++++++
backend/src/accounts/pool/types.rs | 100 +
backend/src/accounts/quota/mod.rs | 7 +
.../{upstream => }/accounts/quota/runtime.rs | 26 +-
.../mod.rs => accounts/quota/service.rs} | 7 -
backend/src/accounts/refresh/lease.rs | 118 ++
backend/src/accounts/refresh/mod.rs | 9 +
.../refresh}/runtime.rs | 149 +-
backend/src/accounts/refresh/runtime/types.rs | 109 +
.../mod.rs => accounts/refresh/service.rs} | 151 +-
.../mod.rs => accounts/scheduler.rs} | 2 +-
.../scheduler/candidates.rs | 4 +-
.../scheduler/feedback.rs | 0
.../src/accounts/scheduler/strategy/mod.rs | 13 +
.../scheduler/strategy/quota_reset.rs | 2 +-
.../scheduler/strategy/round_robin.rs | 2 +-
.../scheduler/strategy/smart.rs | 4 +-
.../scheduler/strategy/sticky.rs | 2 +-
.../scheduler/strategy/types.rs} | 19 +-
.../src/{upstream => }/accounts/service.rs | 2 +-
backend/src/accounts/store.rs | 477 +++++
.../{upstream => }/accounts/store/queries.rs | 323 ++-
.../src/{upstream => }/accounts/store/rows.rs | 160 +-
backend/src/accounts/store/write.rs | 660 ++++++
backend/src/{upstream => }/accounts/window.rs | 0
backend/src/admin/accounts/mod.rs | 5 -
backend/src/admin/accounts/routes.rs | 1481 -------------
backend/src/admin/auth/mod.rs | 4 -
backend/src/admin/auth/service.rs | 341 ---
backend/src/admin/keys/mod.rs | 4 -
backend/src/admin/keys/service.rs | 698 ------
backend/src/admin/mod.rs | 19 -
backend/src/admin/monitoring/mod.rs | 14 -
.../src/admin/monitoring/ops_error_service.rs | 68 -
.../src/admin/monitoring/ops_error_store.rs | 261 ---
.../admin/monitoring/usage_record_store.rs | 1114 ----------
backend/src/admin/settings/mod.rs | 3 -
backend/src/admin/system/mod.rs | 5 -
backend/src/admin/system/routes.rs | 5 -
backend/src/api/admin/accounts_routes.rs | 606 ++++++
.../src/api/admin/accounts_routes/actions.rs | 312 +++
.../src/api/admin/accounts_routes/stats.rs | 512 +++++
backend/src/api/admin/dashboard_routes.rs | 388 ++++
.../admin/dashboard_routes/data.rs} | 489 +----
.../routes.rs => api/admin/keys_routes.rs} | 24 +-
backend/src/api/admin/mod.rs | 13 +
backend/src/api/admin/ops_routes.rs | 155 ++
backend/src/{ => api}/admin/response.rs | 0
backend/src/{ => api}/admin/router.rs | 28 +-
.../src/{admin/auth => api/admin}/session.rs | 12 +-
.../admin/settings_routes.rs} | 49 +-
backend/src/api/admin/system_routes.rs | 151 ++
backend/src/{ => api}/admin/update_payload.rs | 2 +-
.../admin/usage_routes.rs} | 167 +-
backend/src/{web => api}/assets.rs | 0
backend/src/{proxy => api/client}/auth.rs | 17 +-
.../{proxy/openai => api/client}/errors.rs | 7 +-
.../src/{proxy/openai => api/client}/mod.rs | 3 +-
.../{proxy/openai => api/client}/models.rs | 4 +-
.../{proxy/openai => api/client}/responses.rs | 26 +-
.../openai/routes.rs => api/client/router.rs} | 2 +-
.../src/{proxy/openai => api/client}/sse.rs | 2 +-
backend/src/{http => api}/middleware/mod.rs | 0
.../{http => api}/middleware/request_id.rs | 0
backend/src/{http => api}/middleware/trace.rs | 2 +-
backend/src/api/mod.rs | 7 +
backend/src/{http => api}/router.rs | 33 +-
backend/src/auth/mod.rs | 5 +
backend/src/auth/service.rs | 192 ++
backend/src/auth/store.rs | 137 ++
backend/src/auth/types.rs | 42 +
.../bootstrap.rs => bootstrap/application.rs} | 77 +-
.../{config/schema.rs => bootstrap/config.rs} | 50 +-
backend/src/bootstrap/import_sqlite.rs | 233 ++
backend/src/bootstrap/import_sqlite/core.rs | 491 +++++
.../src/bootstrap/import_sqlite/telemetry.rs | 330 +++
backend/src/bootstrap/mod.rs | 9 +
backend/src/bootstrap/services.rs | 422 ++++
.../src/{runtime => bootstrap}/shutdown.rs | 0
backend/src/{runtime => bootstrap}/state.rs | 10 +-
.../{runtime => bootstrap}/tasks/cleanup.rs | 0
.../tasks/cookie_cleanup.rs | 14 +-
.../tasks/coordinator.rs | 30 +-
.../tasks/fingerprint_update.rs | 8 +-
.../src/{runtime => bootstrap}/tasks/mod.rs | 3 +-
.../tasks/model_refresh.rs | 4 +-
.../{runtime => bootstrap}/tasks/periodic.rs | 0
.../tasks/quota_refresh.rs | 12 +-
backend/src/bootstrap/tasks/retention_trim.rs | 70 +
.../tasks/token_refresh.rs | 13 +-
backend/src/config/loader.rs | 31 -
backend/src/config/mod.rs | 10 -
backend/src/config/settings.rs | 601 ------
backend/src/dispatch/affinity/identity.rs | 222 ++
backend/src/dispatch/affinity/mod.rs | 12 +
.../affinity/resolve.rs} | 14 +-
backend/src/dispatch/affinity/service.rs | 161 ++
backend/src/dispatch/affinity/store.rs | 194 ++
backend/src/dispatch/affinity/types.rs | 25 +
.../src/{proxy => }/dispatch/cloudflare.rs | 16 +-
backend/src/{proxy => }/dispatch/errors.rs | 6 +-
.../src/{proxy => }/dispatch/exhaustion.rs | 2 +-
.../{proxy => }/dispatch/implicit_resume.rs | 2 +-
backend/src/{proxy => }/dispatch/mod.rs | 3 +-
.../{proxy => }/dispatch/reasoning_replay.rs | 0
.../{proxy => }/dispatch/responses/errors.rs | 4 +-
.../dispatch/responses/event_recording.rs | 69 +-
.../dispatch/responses/live_stream.rs | 6 +-
.../src/{proxy => }/dispatch/responses/mod.rs | 1 -
.../dispatch/responses/prefetch.rs | 4 +-
backend/src/dispatch/responses/service.rs | 452 ++++
.../src/dispatch/responses/service/compact.rs | 290 +++
.../dispatch/responses/service/complete.rs | 563 +++++
.../src/dispatch/responses/service/stream.rs | 603 ++++++
.../dispatch/responses/sse_failure.rs | 6 +-
.../dispatch/responses/stream_lifecycle.rs | 31 +-
.../{proxy => }/dispatch/responses/trace.rs | 0
backend/src/{proxy => }/dispatch/upstream.rs | 14 +-
backend/src/http/mod.rs | 4 -
backend/src/infra/database.rs | 148 +-
backend/src/infra/identity.rs | 13 +
backend/src/infra/migrations/0001_initial.sql | 419 ++--
.../0002_rename_rotation_strategies.sql | 40 -
.../infra/migrations/0003_ops_error_logs.sql | 31 -
backend/src/infra/mod.rs | 5 +-
backend/src/infra/redis.rs | 54 +
backend/src/keys/manage.rs | 101 +
backend/src/keys/mod.rs | 6 +
backend/src/keys/service.rs | 120 ++
backend/src/keys/store.rs | 227 ++
backend/src/keys/types.rs | 68 +
backend/src/lib.rs | 16 +-
backend/src/main.rs | 51 +-
backend/src/{upstream => }/models/catalog.rs | 3 +-
backend/src/{upstream => }/models/config.rs | 0
backend/src/{upstream => }/models/info.rs | 0
backend/src/{upstream => }/models/mod.rs | 3 +-
backend/src/{upstream => }/models/service.rs | 24 +-
backend/src/{upstream => }/models/snapshot.rs | 0
backend/src/models/store.rs | 106 +
.../src/proxy/dispatch/responses/service.rs | 1877 -----------------
.../src/proxy/dispatch/session_affinity.rs | 683 ------
backend/src/proxy/mod.rs | 11 -
backend/src/proxy/router.rs | 12 -
backend/src/runtime/mod.rs | 7 -
backend/src/runtime/services.rs | 322 ---
.../runtime/tasks/session_affinity_cleanup.rs | 65 -
backend/src/runtime/tasks/session_cleanup.rs | 60 -
backend/src/settings/mod.rs | 8 +
backend/src/settings/service.rs | 188 ++
backend/src/settings/store.rs | 210 ++
backend/src/settings/types.rs | 75 +
backend/src/telemetry/account_usage/mod.rs | 4 +
.../account_usage/query.rs} | 94 +-
.../account_usage/store.rs} | 193 +-
.../monitoring => telemetry}/billing.rs | 6 +-
backend/src/telemetry/buckets/mod.rs | 4 +
backend/src/telemetry/buckets/query.rs | 276 +++
backend/src/telemetry/buckets/store.rs | 143 ++
.../monitoring => telemetry}/diagnostics.rs | 2 +-
backend/src/telemetry/mod.rs | 10 +
backend/src/telemetry/ops/mod.rs | 5 +
backend/src/telemetry/ops/query.rs | 169 ++
backend/src/telemetry/ops/store.rs | 322 +++
.../ops/types.rs} | 10 +
backend/src/telemetry/rebuild.rs | 138 ++
.../usage_events.rs => telemetry/recorder.rs} | 121 +-
backend/src/telemetry/usage/mod.rs | 5 +
.../usage/query.rs} | 167 +-
backend/src/telemetry/usage/store.rs | 369 ++++
backend/src/telemetry/usage/store/query.rs | 432 ++++
.../usage/types.rs} | 69 +-
.../system/updater => update}/archive.rs | 20 +-
.../system/updater => update}/download.rs | 30 +-
backend/src/update/mod.rs | 8 +
.../system/updater => update}/release.rs | 12 +-
.../updater/mod.rs => update/service.rs} | 316 +--
backend/src/{admin/system => update}/state.rs | 20 +-
backend/src/update/types.rs | 33 +
backend/src/upstream/accounts/mod.rs | 19 -
backend/src/upstream/accounts/store/mod.rs | 1142 ----------
backend/src/upstream/fingerprint.rs | 576 -----
backend/src/upstream/mod.rs | 10 +-
backend/src/upstream/models/snapshot_store.rs | 95 -
.../src/upstream/openai/fingerprint/mod.rs | 11 +
.../upstream/openai/fingerprint/runtime.rs | 40 +
.../src/upstream/openai/fingerprint/store.rs | 216 ++
.../src/upstream/openai/fingerprint/types.rs | 92 +
.../upstream/openai/fingerprint/updater.rs | 213 ++
backend/src/upstream/openai/mod.rs | 6 +
.../upstream/{ => openai}/protocol/events.rs | 2 +-
.../src/upstream/{ => openai}/protocol/mod.rs | 2 +
.../protocol/model_catalog.rs} | 0
.../{ => openai}/protocol/responses.rs | 7 +-
.../upstream/{ => openai}/protocol/schema.rs | 0
.../src/upstream/{ => openai}/protocol/sse.rs | 0
.../src/upstream/openai/protocol/websocket.rs | 423 ++++
.../protocol/websocket/validation.rs} | 421 +---
.../src/upstream/{ => openai}/token_client.rs | 26 +-
.../src/upstream/openai/transport/client.rs | 606 ++++++
.../transport/client/requests.rs} | 609 +-----
.../{ => openai}/transport/diagnostics.rs | 0
.../{ => openai}/transport/endpoints.rs | 0
.../{ => openai}/transport/headers.rs | 2 +-
.../upstream/{ => openai}/transport/mod.rs | 0
.../{ => openai}/transport/response_meta.rs | 2 +-
.../upstream/{ => openai}/transport/tls.rs | 0
.../upstream/{ => openai}/transport/usage.rs | 2 +-
.../upstream/openai/transport/websocket.rs | 729 +++++++
.../openai/transport/websocket/stream.rs | 564 +++++
.../openai/transport/websocket/types.rs | 189 ++
.../{ => openai}/transport/websocket_pool.rs | 0
.../{ => openai}/transport/websocket_pump.rs | 0
backend/src/upstream/transport/websocket.rs | 1470 -------------
backend/src/web/mod.rs | 3 -
.../accounts/account_pool/mod.rs | 4 +-
.../accounts/account_pool/quota.rs | 0
.../accounts/account_pool/selection.rs | 66 +-
.../accounts/account_pool/usage_window.rs | 0
.../account_store.rs} | 292 ++-
.../{upstream => }/accounts/cloudflare.rs | 2 +-
.../tests/{upstream => }/accounts/cookies.rs | 43 +-
backend/tests/{upstream => }/accounts/mod.rs | 3 +-
.../{upstream => }/accounts/quota_refresh.rs | 99 +-
backend/tests/accounts/refresh_leases.rs | 54 +
.../scheduler/feedback.rs | 2 +-
.../{upstream => accounts}/scheduler/mod.rs | 0
.../{upstream => accounts}/scheduler/score.rs | 4 +-
.../accounts/token_refresh/failures.rs | 60 +-
.../accounts/token_refresh/mod.rs | 20 +-
.../accounts/token_refresh/scheduling.rs | 84 +-
.../accounts/token_refresh/success.rs | 62 +-
backend/tests/admin/auth/session_store.rs | 84 -
backend/tests/admin/keys/mod.rs | 80 -
backend/tests/admin/keys/store.rs | 115 -
.../admin/monitoring/usage_record_routes.rs | 935 --------
.../admin/monitoring/usage_record_store.rs | 157 --
.../{ => api}/admin/accounts/exporting.rs | 21 +-
.../{ => api}/admin/accounts/importing.rs | 124 +-
.../{ => api}/admin/accounts/lifecycle.rs | 278 ++-
.../tests/{ => api}/admin/accounts/list.rs | 183 +-
backend/tests/{ => api}/admin/accounts/mod.rs | 77 +-
.../tests/{ => api}/admin/accounts/oauth.rs | 8 +-
.../tests/{ => api}/admin/accounts/quota.rs | 40 +-
.../tests/{ => api}/admin/accounts/testing.rs | 219 +-
backend/tests/{ => api}/admin/auth/mod.rs | 0
.../tests/{ => api}/admin/auth/password.rs | 0
backend/tests/{ => api}/admin/auth/session.rs | 76 +-
backend/tests/api/admin/auth/session_store.rs | 63 +
.../{ => api}/admin/keys/authorization.rs | 7 +-
.../tests/{ => api}/admin/keys/lifecycle.rs | 4 +-
backend/tests/api/admin/keys/mod.rs | 58 +
backend/tests/api/admin/keys/store.rs | 124 ++
backend/tests/{ => api}/admin/mod.rs | 0
.../{ => api}/admin/monitoring/dashboard.rs | 190 +-
.../tests/{ => api}/admin/monitoring/mod.rs | 1 +
.../api/admin/monitoring/ops_error_store.rs | 61 +
.../admin/monitoring/usage_record_routes.rs | 165 ++
.../admin/monitoring/usage_record_store.rs | 115 +
backend/tests/{ => api}/admin/response/mod.rs | 2 +-
backend/tests/{ => api}/admin/settings/mod.rs | 20 +-
.../tests/{ => api}/admin/settings/routes.rs | 174 +-
backend/tests/{ => api}/admin/system/mod.rs | 96 +-
backend/tests/{proxy => api/client}/auth.rs | 2 +-
.../keys/auth.rs => api/client/key_auth.rs} | 0
backend/tests/api/client/mod.rs | 3 +
.../tests/{proxy => api/client}/openai/mod.rs | 0
.../client}/openai/models_routes.rs | 91 +-
.../client}/openai/responses_routes.rs | 79 +-
backend/tests/{ => api}/http/mod.rs | 0
.../{ => api}/http/trace_middleware/mod.rs | 2 +-
.../tests/{ => api}/http/web_assets/mod.rs | 59 +-
backend/tests/api/mod.rs | 3 +
.../account_pool_restore/mod.rs | 34 +-
backend/tests/{ => bootstrap}/config/mod.rs | 24 +-
backend/tests/bootstrap/import_sqlite.rs | 256 +++
backend/tests/{runtime => bootstrap}/mod.rs | 2 +
backend/tests/bootstrap/services.rs | 81 +
backend/tests/bootstrap/tasks/cleanup.rs | 34 +
.../tasks/coordinator.rs | 6 +-
.../tasks/fingerprint.rs | 74 +-
.../tests/{runtime => bootstrap}/tasks/mod.rs | 108 +-
.../tasks/model_refresh.rs | 22 +-
backend/tests/dispatch/affinity.rs | 125 ++
.../dispatch/chat_upstream/compact_routes.rs | 17 +-
.../{proxy => }/dispatch/chat_upstream/mod.rs | 599 +++---
.../dispatch/chat_upstream/responses_http.rs | 50 +-
.../chat_upstream/responses_recovery.rs | 110 +-
.../chat_upstream/responses_websocket.rs | 142 +-
.../dispatch/chat_upstream/usage_logging.rs | 114 +-
backend/tests/dispatch/mod.rs | 2 +
backend/tests/fixtures/sqlite_v3.sql | 382 ++++
backend/tests/infra/storage_schema/mod.rs | 833 ++------
backend/tests/main.rs | 11 +-
.../tests/{upstream => }/models/catalog.rs | 40 +-
backend/tests/{upstream => }/models/mod.rs | 0
backend/tests/{upstream => }/models/store.rs | 23 +-
backend/tests/proxy/dispatch/mod.rs | 3 -
.../tests/proxy/dispatch/session_affinity.rs | 276 ---
.../dispatch/session_affinity_integration.rs | 53 -
backend/tests/proxy/keys/mod.rs | 1 -
backend/tests/proxy/mod.rs | 4 -
backend/tests/runtime/services.rs | 119 --
backend/tests/runtime/tasks/cleanup.rs | 121 --
backend/tests/support/accounts.rs | 2 +-
backend/tests/support/admin.rs | 25 +-
backend/tests/support/client_keys.rs | 8 +-
backend/tests/support/config.rs | 8 +-
backend/tests/support/fingerprint.rs | 6 +-
backend/tests/support/mod.rs | 2 +-
backend/tests/support/sqlite.rs | 10 -
backend/tests/support/storage.rs | 150 ++
backend/tests/telemetry/mod.rs | 1 +
backend/tests/telemetry/rebuild.rs | 97 +
.../tests/upstream/accounts/refresh_leases.rs | 133 --
backend/tests/upstream/mod.rs | 8 +-
.../upstream/{ => openai}/fingerprint/mod.rs | 31 +-
backend/tests/upstream/openai/mod.rs | 4 +
.../{ => openai}/protocol/codex_websocket.rs | 4 +-
.../upstream/{ => openai}/protocol/mod.rs | 10 +-
.../{ => openai}/protocol/openai_responses.rs | 6 +-
.../protocol/response_first_event.rs | 20 +-
.../protocol/usage_rate_limits.rs | 11 +-
.../upstream/{ => openai}/token_client.rs | 5 +-
.../upstream/{ => openai}/transport/client.rs | 4 +-
.../{ => openai}/transport/diagnostics.rs | 2 +-
.../{ => openai}/transport/headers.rs | 49 +-
.../{ => openai}/transport/http_client.rs | 24 +-
.../upstream/{ => openai}/transport/mod.rs | 10 +-
.../{ => openai}/transport/websocket.rs | 34 +-
.../{ => openai}/transport/websocket_pool.rs | 16 +-
deploy/config.example.yaml | 10 +-
deploy/docker-compose.yml | 44 +
docs/architecture.md | 42 +-
docs/database.md | 52 +-
frontend/src/api/modules/usage.ts | 8 +
.../api-keys/components/ApiKeyActions.vue | 15 +-
.../api-keys/components/ApiKeyCreateModal.vue | 21 +-
.../api-keys/components/ApiKeyPrefixCell.vue | 4 +-
.../api-keys/components/ApiKeyUseModal.vue | 116 -
.../composables/useApiKeyMutations.ts | 8 +-
frontend/src/views/api-keys/constants.ts | 6 +-
frontend/src/views/api-keys/index.vue | 46 +-
.../dashboard/components/UsageRecordCard.vue | 28 +-
.../settings/components/AdminApiKeyCard.vue | 7 +-
frontend/src/views/settings/index.vue | 14 +-
.../usage/components/OpsErrorDetailModal.vue | 110 +
.../views/usage/components/OpsErrorPanel.vue | 120 ++
.../views/usage/components/UsageFilters.vue | 11 -
.../usage/components/UsageInsightsGrid.vue | 13 +-
.../usage/components/UsageLevelBadge.vue | 28 -
.../components/UsageRecordDetailModal.vue | 11 +-
.../usage/components/UsageSummaryCards.vue | 18 +-
.../usage/composables/useOpsErrorsTable.ts | 104 +
.../usage/composables/useUsageFilters.ts | 4 +-
.../usage/composables/useUsageRecordsTable.ts | 4 -
frontend/src/views/usage/constants.ts | 69 +-
frontend/src/views/usage/index.vue | 221 +-
381 files changed, 21571 insertions(+), 20990 deletions(-)
rename backend/{build => }/build.rs (100%)
create mode 100755 backend/scripts/check_architecture.sh
rename backend/src/{upstream/accounts/model.rs => accounts/account.rs} (100%)
create mode 100644 backend/src/accounts/cookies/mod.rs
rename backend/src/{upstream/accounts/cookies.rs => accounts/cookies/store.rs} (85%)
rename backend/src/{upstream/accounts/importing.rs => accounts/import.rs} (99%)
rename backend/src/{admin/accounts/service => accounts/manage}/cookies.rs (81%)
rename backend/src/{admin/accounts/service/exporting.rs => accounts/manage/export.rs} (77%)
rename backend/src/{admin/accounts/service/importing.rs => accounts/manage/import.rs} (71%)
rename backend/src/{admin/accounts/service => accounts/manage}/lifecycle.rs (70%)
create mode 100644 backend/src/accounts/manage/mod.rs
rename backend/src/{admin/accounts/service => accounts/manage}/oauth.rs (81%)
rename backend/src/{admin/accounts/service/testing.rs => accounts/manage/probe.rs} (94%)
rename backend/src/{admin/accounts/service => accounts/manage}/quota.rs (72%)
rename backend/src/{admin/accounts => accounts/manage}/quota_view.rs (87%)
rename backend/src/{admin/accounts/service/mod.rs => accounts/manage/service.rs} (54%)
rename backend/src/{admin/accounts/service/contracts.rs => accounts/manage/types.rs} (83%)
create mode 100644 backend/src/accounts/mod.rs
rename backend/src/{upstream => }/accounts/pool.rs (53%)
create mode 100644 backend/src/accounts/pool/runtime.rs
create mode 100644 backend/src/accounts/pool/types.rs
create mode 100644 backend/src/accounts/quota/mod.rs
rename backend/src/{upstream => }/accounts/quota/runtime.rs (92%)
rename backend/src/{upstream/accounts/quota/mod.rs => accounts/quota/service.rs} (98%)
create mode 100644 backend/src/accounts/refresh/lease.rs
create mode 100644 backend/src/accounts/refresh/mod.rs
rename backend/src/{upstream/accounts/token_refresh => accounts/refresh}/runtime.rs (85%)
create mode 100644 backend/src/accounts/refresh/runtime/types.rs
rename backend/src/{upstream/accounts/token_refresh/mod.rs => accounts/refresh/service.rs} (71%)
rename backend/src/{upstream/scheduler/mod.rs => accounts/scheduler.rs} (98%)
rename backend/src/{upstream => accounts}/scheduler/candidates.rs (97%)
rename backend/src/{upstream => accounts}/scheduler/feedback.rs (100%)
create mode 100644 backend/src/accounts/scheduler/strategy/mod.rs
rename backend/src/{upstream => accounts}/scheduler/strategy/quota_reset.rs (96%)
rename backend/src/{upstream => accounts}/scheduler/strategy/round_robin.rs (92%)
rename backend/src/{upstream => accounts}/scheduler/strategy/smart.rs (98%)
rename backend/src/{upstream => accounts}/scheduler/strategy/sticky.rs (95%)
rename backend/src/{upstream/scheduler/strategy/mod.rs => accounts/scheduler/strategy/types.rs} (90%)
rename backend/src/{upstream => }/accounts/service.rs (94%)
create mode 100644 backend/src/accounts/store.rs
rename backend/src/{upstream => }/accounts/store/queries.rs (63%)
rename backend/src/{upstream => }/accounts/store/rows.rs (67%)
create mode 100644 backend/src/accounts/store/write.rs
rename backend/src/{upstream => }/accounts/window.rs (100%)
delete mode 100644 backend/src/admin/accounts/mod.rs
delete mode 100644 backend/src/admin/accounts/routes.rs
delete mode 100644 backend/src/admin/auth/mod.rs
delete mode 100644 backend/src/admin/auth/service.rs
delete mode 100644 backend/src/admin/keys/mod.rs
delete mode 100644 backend/src/admin/keys/service.rs
delete mode 100644 backend/src/admin/mod.rs
delete mode 100644 backend/src/admin/monitoring/mod.rs
delete mode 100644 backend/src/admin/monitoring/ops_error_service.rs
delete mode 100644 backend/src/admin/monitoring/ops_error_store.rs
delete mode 100644 backend/src/admin/monitoring/usage_record_store.rs
delete mode 100644 backend/src/admin/settings/mod.rs
delete mode 100644 backend/src/admin/system/mod.rs
delete mode 100644 backend/src/admin/system/routes.rs
create mode 100644 backend/src/api/admin/accounts_routes.rs
create mode 100644 backend/src/api/admin/accounts_routes/actions.rs
create mode 100644 backend/src/api/admin/accounts_routes/stats.rs
create mode 100644 backend/src/api/admin/dashboard_routes.rs
rename backend/src/{admin/monitoring/dashboard.rs => api/admin/dashboard_routes/data.rs} (62%)
rename backend/src/{admin/keys/routes.rs => api/admin/keys_routes.rs} (90%)
create mode 100644 backend/src/api/admin/mod.rs
create mode 100644 backend/src/api/admin/ops_routes.rs
rename backend/src/{ => api}/admin/response.rs (100%)
rename backend/src/{ => api}/admin/router.rs (85%)
rename backend/src/{admin/auth => api/admin}/session.rs (95%)
rename backend/src/{admin/settings/routes.rs => api/admin/settings_routes.rs} (70%)
create mode 100644 backend/src/api/admin/system_routes.rs
rename backend/src/{ => api}/admin/update_payload.rs (97%)
rename backend/src/{admin/monitoring/usage_record_routes.rs => api/admin/usage_routes.rs} (82%)
rename backend/src/{web => api}/assets.rs (100%)
rename backend/src/{proxy => api/client}/auth.rs (87%)
rename backend/src/{proxy/openai => api/client}/errors.rs (98%)
rename backend/src/{proxy/openai => api/client}/mod.rs (76%)
rename backend/src/{proxy/openai => api/client}/models.rs (96%)
rename backend/src/{proxy/openai => api/client}/responses.rs (95%)
rename backend/src/{proxy/openai/routes.rs => api/client/router.rs} (95%)
rename backend/src/{proxy/openai => api/client}/sse.rs (95%)
rename backend/src/{http => api}/middleware/mod.rs (100%)
rename backend/src/{http => api}/middleware/request_id.rs (100%)
rename backend/src/{http => api}/middleware/trace.rs (98%)
create mode 100644 backend/src/api/mod.rs
rename backend/src/{http => api}/router.rs (54%)
create mode 100644 backend/src/auth/mod.rs
create mode 100644 backend/src/auth/service.rs
create mode 100644 backend/src/auth/store.rs
create mode 100644 backend/src/auth/types.rs
rename backend/src/{runtime/bootstrap.rs => bootstrap/application.rs} (60%)
rename backend/src/{config/schema.rs => bootstrap/config.rs} (89%)
create mode 100644 backend/src/bootstrap/import_sqlite.rs
create mode 100644 backend/src/bootstrap/import_sqlite/core.rs
create mode 100644 backend/src/bootstrap/import_sqlite/telemetry.rs
create mode 100644 backend/src/bootstrap/mod.rs
create mode 100644 backend/src/bootstrap/services.rs
rename backend/src/{runtime => bootstrap}/shutdown.rs (100%)
rename backend/src/{runtime => bootstrap}/state.rs (66%)
rename backend/src/{runtime => bootstrap}/tasks/cleanup.rs (100%)
rename backend/src/{runtime => bootstrap}/tasks/cookie_cleanup.rs (77%)
rename backend/src/{runtime => bootstrap}/tasks/coordinator.rs (86%)
rename backend/src/{runtime => bootstrap}/tasks/fingerprint_update.rs (93%)
rename backend/src/{runtime => bootstrap}/tasks/mod.rs (77%)
rename backend/src/{runtime => bootstrap}/tasks/model_refresh.rs (98%)
rename backend/src/{runtime => bootstrap}/tasks/periodic.rs (100%)
rename backend/src/{runtime => bootstrap}/tasks/quota_refresh.rs (92%)
create mode 100644 backend/src/bootstrap/tasks/retention_trim.rs
rename backend/src/{runtime => bootstrap}/tasks/token_refresh.rs (89%)
delete mode 100644 backend/src/config/loader.rs
delete mode 100644 backend/src/config/mod.rs
delete mode 100644 backend/src/config/settings.rs
create mode 100644 backend/src/dispatch/affinity/identity.rs
create mode 100644 backend/src/dispatch/affinity/mod.rs
rename backend/src/{proxy/dispatch/responses/affinity.rs => dispatch/affinity/resolve.rs} (88%)
create mode 100644 backend/src/dispatch/affinity/service.rs
create mode 100644 backend/src/dispatch/affinity/store.rs
create mode 100644 backend/src/dispatch/affinity/types.rs
rename backend/src/{proxy => }/dispatch/cloudflare.rs (91%)
rename backend/src/{proxy => }/dispatch/errors.rs (98%)
rename backend/src/{proxy => }/dispatch/exhaustion.rs (99%)
rename backend/src/{proxy => }/dispatch/implicit_resume.rs (98%)
rename backend/src/{proxy => }/dispatch/mod.rs (83%)
rename backend/src/{proxy => }/dispatch/reasoning_replay.rs (100%)
rename backend/src/{proxy => }/dispatch/responses/errors.rs (99%)
rename backend/src/{proxy => }/dispatch/responses/event_recording.rs (91%)
rename backend/src/{proxy => }/dispatch/responses/live_stream.rs (99%)
rename backend/src/{proxy => }/dispatch/responses/mod.rs (93%)
rename backend/src/{proxy => }/dispatch/responses/prefetch.rs (95%)
create mode 100644 backend/src/dispatch/responses/service.rs
create mode 100644 backend/src/dispatch/responses/service/compact.rs
create mode 100644 backend/src/dispatch/responses/service/complete.rs
create mode 100644 backend/src/dispatch/responses/service/stream.rs
rename backend/src/{proxy => }/dispatch/responses/sse_failure.rs (98%)
rename backend/src/{proxy => }/dispatch/responses/stream_lifecycle.rs (92%)
rename backend/src/{proxy => }/dispatch/responses/trace.rs (100%)
rename backend/src/{proxy => }/dispatch/upstream.rs (98%)
delete mode 100644 backend/src/http/mod.rs
delete mode 100644 backend/src/infra/migrations/0002_rename_rotation_strategies.sql
delete mode 100644 backend/src/infra/migrations/0003_ops_error_logs.sql
create mode 100644 backend/src/infra/redis.rs
create mode 100644 backend/src/keys/manage.rs
create mode 100644 backend/src/keys/mod.rs
create mode 100644 backend/src/keys/service.rs
create mode 100644 backend/src/keys/store.rs
create mode 100644 backend/src/keys/types.rs
rename backend/src/{upstream => }/models/catalog.rs (99%)
rename backend/src/{upstream => }/models/config.rs (100%)
rename backend/src/{upstream => }/models/info.rs (100%)
rename backend/src/{upstream => }/models/mod.rs (69%)
rename backend/src/{upstream => }/models/service.rs (93%)
rename backend/src/{upstream => }/models/snapshot.rs (100%)
create mode 100644 backend/src/models/store.rs
delete mode 100644 backend/src/proxy/dispatch/responses/service.rs
delete mode 100644 backend/src/proxy/dispatch/session_affinity.rs
delete mode 100644 backend/src/proxy/mod.rs
delete mode 100644 backend/src/proxy/router.rs
delete mode 100644 backend/src/runtime/mod.rs
delete mode 100644 backend/src/runtime/services.rs
delete mode 100644 backend/src/runtime/tasks/session_affinity_cleanup.rs
delete mode 100644 backend/src/runtime/tasks/session_cleanup.rs
create mode 100644 backend/src/settings/mod.rs
create mode 100644 backend/src/settings/service.rs
create mode 100644 backend/src/settings/store.rs
create mode 100644 backend/src/settings/types.rs
create mode 100644 backend/src/telemetry/account_usage/mod.rs
rename backend/src/{admin/monitoring/account_usage_service.rs => telemetry/account_usage/query.rs} (73%)
rename backend/src/{admin/monitoring/account_usage_store.rs => telemetry/account_usage/store.rs} (55%)
rename backend/src/{admin/monitoring => telemetry}/billing.rs (99%)
create mode 100644 backend/src/telemetry/buckets/mod.rs
create mode 100644 backend/src/telemetry/buckets/query.rs
create mode 100644 backend/src/telemetry/buckets/store.rs
rename backend/src/{admin/monitoring => telemetry}/diagnostics.rs (96%)
create mode 100644 backend/src/telemetry/mod.rs
create mode 100644 backend/src/telemetry/ops/mod.rs
create mode 100644 backend/src/telemetry/ops/query.rs
create mode 100644 backend/src/telemetry/ops/store.rs
rename backend/src/{admin/monitoring/ops_error_model.rs => telemetry/ops/types.rs} (84%)
create mode 100644 backend/src/telemetry/rebuild.rs
rename backend/src/{proxy/dispatch/usage_events.rs => telemetry/recorder.rs} (53%)
create mode 100644 backend/src/telemetry/usage/mod.rs
rename backend/src/{admin/monitoring/usage_record_service.rs => telemetry/usage/query.rs} (66%)
create mode 100644 backend/src/telemetry/usage/store.rs
create mode 100644 backend/src/telemetry/usage/store/query.rs
rename backend/src/{admin/monitoring/usage_record_model.rs => telemetry/usage/types.rs} (65%)
rename backend/src/{admin/system/updater => update}/archive.rs (93%)
rename backend/src/{admin/system/updater => update}/download.rs (89%)
create mode 100644 backend/src/update/mod.rs
rename backend/src/{admin/system/updater => update}/release.rs (96%)
rename backend/src/{admin/system/updater/mod.rs => update/service.rs} (68%)
rename backend/src/{admin/system => update}/state.rs (94%)
create mode 100644 backend/src/update/types.rs
delete mode 100644 backend/src/upstream/accounts/mod.rs
delete mode 100644 backend/src/upstream/accounts/store/mod.rs
delete mode 100644 backend/src/upstream/fingerprint.rs
delete mode 100644 backend/src/upstream/models/snapshot_store.rs
create mode 100644 backend/src/upstream/openai/fingerprint/mod.rs
create mode 100644 backend/src/upstream/openai/fingerprint/runtime.rs
create mode 100644 backend/src/upstream/openai/fingerprint/store.rs
create mode 100644 backend/src/upstream/openai/fingerprint/types.rs
create mode 100644 backend/src/upstream/openai/fingerprint/updater.rs
create mode 100644 backend/src/upstream/openai/mod.rs
rename backend/src/upstream/{ => openai}/protocol/events.rs (99%)
rename backend/src/upstream/{ => openai}/protocol/mod.rs (83%)
rename backend/src/upstream/{models/backend_entry.rs => openai/protocol/model_catalog.rs} (100%)
rename backend/src/upstream/{ => openai}/protocol/responses.rs (98%)
rename backend/src/upstream/{ => openai}/protocol/schema.rs (100%)
rename backend/src/upstream/{ => openai}/protocol/sse.rs (100%)
create mode 100644 backend/src/upstream/openai/protocol/websocket.rs
rename backend/src/upstream/{protocol/websocket.rs => openai/protocol/websocket/validation.rs} (61%)
rename backend/src/upstream/{ => openai}/token_client.rs (82%)
create mode 100644 backend/src/upstream/openai/transport/client.rs
rename backend/src/upstream/{transport/client.rs => openai/transport/client/requests.rs} (54%)
rename backend/src/upstream/{ => openai}/transport/diagnostics.rs (100%)
rename backend/src/upstream/{ => openai}/transport/endpoints.rs (100%)
rename backend/src/upstream/{ => openai}/transport/headers.rs (98%)
rename backend/src/upstream/{ => openai}/transport/mod.rs (100%)
rename backend/src/upstream/{ => openai}/transport/response_meta.rs (95%)
rename backend/src/upstream/{ => openai}/transport/tls.rs (100%)
rename backend/src/upstream/{ => openai}/transport/usage.rs (96%)
create mode 100644 backend/src/upstream/openai/transport/websocket.rs
create mode 100644 backend/src/upstream/openai/transport/websocket/stream.rs
create mode 100644 backend/src/upstream/openai/transport/websocket/types.rs
rename backend/src/upstream/{ => openai}/transport/websocket_pool.rs (100%)
rename backend/src/upstream/{ => openai}/transport/websocket_pump.rs (100%)
delete mode 100644 backend/src/upstream/transport/websocket.rs
delete mode 100644 backend/src/web/mod.rs
rename backend/tests/{upstream => }/accounts/account_pool/mod.rs (90%)
rename backend/tests/{upstream => }/accounts/account_pool/quota.rs (100%)
rename backend/tests/{upstream => }/accounts/account_pool/selection.rs (94%)
rename backend/tests/{upstream => }/accounts/account_pool/usage_window.rs (100%)
rename backend/tests/{upstream/accounts/account_repository.rs => accounts/account_store.rs} (72%)
rename backend/tests/{upstream => }/accounts/cloudflare.rs (94%)
rename backend/tests/{upstream => }/accounts/cookies.rs (62%)
rename backend/tests/{upstream => }/accounts/mod.rs (75%)
rename backend/tests/{upstream => }/accounts/quota_refresh.rs (87%)
create mode 100644 backend/tests/accounts/refresh_leases.rs
rename backend/tests/{upstream => accounts}/scheduler/feedback.rs (95%)
rename backend/tests/{upstream => accounts}/scheduler/mod.rs (100%)
rename backend/tests/{upstream => accounts}/scheduler/score.rs (97%)
rename backend/tests/{upstream => }/accounts/token_refresh/failures.rs (85%)
rename backend/tests/{upstream => }/accounts/token_refresh/mod.rs (90%)
rename backend/tests/{upstream => }/accounts/token_refresh/scheduling.rs (86%)
rename backend/tests/{upstream => }/accounts/token_refresh/success.rs (83%)
delete mode 100644 backend/tests/admin/auth/session_store.rs
delete mode 100644 backend/tests/admin/keys/mod.rs
delete mode 100644 backend/tests/admin/keys/store.rs
delete mode 100644 backend/tests/admin/monitoring/usage_record_routes.rs
delete mode 100644 backend/tests/admin/monitoring/usage_record_store.rs
rename backend/tests/{ => api}/admin/accounts/exporting.rs (93%)
rename backend/tests/{ => api}/admin/accounts/importing.rs (88%)
rename backend/tests/{ => api}/admin/accounts/lifecycle.rs (82%)
rename backend/tests/{ => api}/admin/accounts/list.rs (75%)
rename backend/tests/{ => api}/admin/accounts/mod.rs (65%)
rename backend/tests/{ => api}/admin/accounts/oauth.rs (96%)
rename backend/tests/{ => api}/admin/accounts/quota.rs (82%)
rename backend/tests/{ => api}/admin/accounts/testing.rs (75%)
rename backend/tests/{ => api}/admin/auth/mod.rs (100%)
rename backend/tests/{ => api}/admin/auth/password.rs (100%)
rename backend/tests/{ => api}/admin/auth/session.rs (70%)
create mode 100644 backend/tests/api/admin/auth/session_store.rs
rename backend/tests/{ => api}/admin/keys/authorization.rs (97%)
rename backend/tests/{ => api}/admin/keys/lifecycle.rs (98%)
create mode 100644 backend/tests/api/admin/keys/mod.rs
create mode 100644 backend/tests/api/admin/keys/store.rs
rename backend/tests/{ => api}/admin/mod.rs (100%)
rename backend/tests/{ => api}/admin/monitoring/dashboard.rs (82%)
rename backend/tests/{ => api}/admin/monitoring/mod.rs (75%)
create mode 100644 backend/tests/api/admin/monitoring/ops_error_store.rs
create mode 100644 backend/tests/api/admin/monitoring/usage_record_routes.rs
create mode 100644 backend/tests/api/admin/monitoring/usage_record_store.rs
rename backend/tests/{ => api}/admin/response/mod.rs (98%)
rename backend/tests/{ => api}/admin/settings/mod.rs (81%)
rename backend/tests/{ => api}/admin/settings/routes.rs (74%)
rename backend/tests/{ => api}/admin/system/mod.rs (94%)
rename backend/tests/{proxy => api/client}/auth.rs (94%)
rename backend/tests/{proxy/keys/auth.rs => api/client/key_auth.rs} (100%)
create mode 100644 backend/tests/api/client/mod.rs
rename backend/tests/{proxy => api/client}/openai/mod.rs (100%)
rename backend/tests/{proxy => api/client}/openai/models_routes.rs (65%)
rename backend/tests/{proxy => api/client}/openai/responses_routes.rs (67%)
rename backend/tests/{ => api}/http/mod.rs (100%)
rename backend/tests/{ => api}/http/trace_middleware/mod.rs (99%)
rename backend/tests/{ => api}/http/web_assets/mod.rs (71%)
create mode 100644 backend/tests/api/mod.rs
rename backend/tests/{runtime => bootstrap}/account_pool_restore/mod.rs (73%)
rename backend/tests/{ => bootstrap}/config/mod.rs (91%)
create mode 100644 backend/tests/bootstrap/import_sqlite.rs
rename backend/tests/{runtime => bootstrap}/mod.rs (61%)
create mode 100644 backend/tests/bootstrap/services.rs
create mode 100644 backend/tests/bootstrap/tasks/cleanup.rs
rename backend/tests/{runtime => bootstrap}/tasks/coordinator.rs (76%)
rename backend/tests/{runtime => bootstrap}/tasks/fingerprint.rs (64%)
rename backend/tests/{runtime => bootstrap}/tasks/mod.rs (59%)
rename backend/tests/{runtime => bootstrap}/tasks/model_refresh.rs (91%)
create mode 100644 backend/tests/dispatch/affinity.rs
rename backend/tests/{proxy => }/dispatch/chat_upstream/compact_routes.rs (96%)
rename backend/tests/{proxy => }/dispatch/chat_upstream/mod.rs (80%)
rename backend/tests/{proxy => }/dispatch/chat_upstream/responses_http.rs (97%)
rename backend/tests/{proxy => }/dispatch/chat_upstream/responses_recovery.rs (97%)
rename backend/tests/{proxy => }/dispatch/chat_upstream/responses_websocket.rs (96%)
rename backend/tests/{proxy => }/dispatch/chat_upstream/usage_logging.rs (92%)
create mode 100644 backend/tests/dispatch/mod.rs
create mode 100644 backend/tests/fixtures/sqlite_v3.sql
rename backend/tests/{upstream => }/models/catalog.rs (88%)
rename backend/tests/{upstream => }/models/mod.rs (100%)
rename backend/tests/{upstream => }/models/store.rs (54%)
delete mode 100644 backend/tests/proxy/dispatch/mod.rs
delete mode 100644 backend/tests/proxy/dispatch/session_affinity.rs
delete mode 100644 backend/tests/proxy/dispatch/session_affinity_integration.rs
delete mode 100644 backend/tests/proxy/keys/mod.rs
delete mode 100644 backend/tests/proxy/mod.rs
delete mode 100644 backend/tests/runtime/services.rs
delete mode 100644 backend/tests/runtime/tasks/cleanup.rs
delete mode 100644 backend/tests/support/sqlite.rs
create mode 100644 backend/tests/support/storage.rs
create mode 100644 backend/tests/telemetry/mod.rs
create mode 100644 backend/tests/telemetry/rebuild.rs
delete mode 100644 backend/tests/upstream/accounts/refresh_leases.rs
rename backend/tests/upstream/{ => openai}/fingerprint/mod.rs (80%)
create mode 100644 backend/tests/upstream/openai/mod.rs
rename backend/tests/upstream/{ => openai}/protocol/codex_websocket.rs (99%)
rename backend/tests/upstream/{ => openai}/protocol/mod.rs (89%)
rename backend/tests/upstream/{ => openai}/protocol/openai_responses.rs (98%)
rename backend/tests/upstream/{ => openai}/protocol/response_first_event.rs (58%)
rename backend/tests/upstream/{ => openai}/protocol/usage_rate_limits.rs (96%)
rename backend/tests/upstream/{ => openai}/token_client.rs (96%)
rename backend/tests/upstream/{ => openai}/transport/client.rs (95%)
rename backend/tests/upstream/{ => openai}/transport/diagnostics.rs (92%)
rename backend/tests/upstream/{ => openai}/transport/headers.rs (94%)
rename backend/tests/upstream/{ => openai}/transport/http_client.rs (87%)
rename backend/tests/upstream/{ => openai}/transport/mod.rs (94%)
rename backend/tests/upstream/{ => openai}/transport/websocket.rs (96%)
rename backend/tests/upstream/{ => openai}/transport/websocket_pool.rs (98%)
delete mode 100644 frontend/src/views/api-keys/components/ApiKeyUseModal.vue
create mode 100644 frontend/src/views/usage/components/OpsErrorDetailModal.vue
create mode 100644 frontend/src/views/usage/components/OpsErrorPanel.vue
delete mode 100644 frontend/src/views/usage/components/UsageLevelBadge.vue
create mode 100644 frontend/src/views/usage/composables/useOpsErrorsTable.ts
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c642f6a82..1dea14e58 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,6 +16,29 @@ concurrency:
jobs:
backend:
runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:18-alpine
+ env:
+ POSTGRES_DB: codex_proxy
+ POSTGRES_USER: codex_proxy
+ POSTGRES_PASSWORD: codex_proxy
+ ports:
+ - 5432:5432
+ options: >-
+ --health-cmd "pg_isready -U codex_proxy -d codex_proxy"
+ --health-interval 5s
+ --health-timeout 5s
+ --health-retries 10
+ redis:
+ image: redis:8-alpine
+ ports:
+ - 6379:6379
+ options: >-
+ --health-cmd "redis-cli ping"
+ --health-interval 5s
+ --health-timeout 5s
+ --health-retries 10
steps:
- name: Checkout
uses: actions/checkout@v7
@@ -38,6 +61,9 @@ jobs:
working-directory: backend
run: cargo clippy --all-targets --all-features --locked -- -D warnings
+ - name: Architecture boundaries
+ run: backend/scripts/check_architecture.sh
+
- name: Rust tests
working-directory: backend
run: cargo test --test main --locked
diff --git a/README.md b/README.md
index 63ca35664..3b25776b7 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@
-
+
+
@@ -17,15 +18,16 @@
Codex Proxy RS 是一个单进程网关:
-- Rust/Axum 后端提供 Responses 兼容代理、管理端 API、SQLite 持久化和静态前端托管。
+- Rust/Axum 后端提供 Responses 兼容代理、管理端 API、PostgreSQL 持久化、Redis 运行态存储和静态前端托管。
- Vue 管理端负责账号导入、API Key、用量统计、请求记录、运行参数、模型映射和系统更新。
-- 运行数据默认写入仓库 `.runtime/`;Docker 部署也使用这套宿主机目录挂载。
+- 应用本地文件默认写入仓库 `.runtime/`;PostgreSQL 与 Redis 数据由各自服务管理。
## 环境
- Rust 1.95
- Node 24 或兼容版本,pnpm 11(只在开发前端或构建镜像时需要)
-- Docker / Docker Compose(部署或验证镜像时需要)
+- PostgreSQL 18、Redis 8
+- Docker / Docker Compose(推荐用于启动依赖、部署或验证镜像)
## 本地运行
@@ -35,11 +37,14 @@ Codex Proxy RS 是一个单进程网关:
cp deploy/config.example.yaml .runtime/config.yaml
```
-如果直接用本地二进制而不是 Docker,确保 `.runtime/config.yaml` 里的路径是宿主机路径:
+如果直接用本地二进制而不是 Docker,确保 `.runtime/config.yaml` 使用宿主机可访问的连接地址与日志路径:
```yaml
database:
- url: 'sqlite://.runtime/data/codex-proxy-rs.sqlite'
+ url: 'postgres://codex_proxy:codex_proxy@127.0.0.1:5432/codex_proxy'
+
+redis:
+ url: 'redis://127.0.0.1:6379/'
logging:
directory: .runtime/logs
@@ -59,12 +64,14 @@ CPR_CONFIG_FILE=.runtime/config.yaml cargo run --manifest-path backend/Cargo.tom
## Docker 部署
-Compose 默认把宿主机 `.runtime` 映射进容器:
+Compose 同时启动 PostgreSQL、Redis 和主服务。主服务默认把宿主机 `.runtime` 映射进容器:
- `.runtime/config.yaml` -> `/app/config.yaml`
-- `.runtime/data` -> `/app/data`
+- `.runtime/data` -> `/app/data`(installation id 与在线更新状态,不含数据库)
- `.runtime/logs` -> `/app/logs`
+PostgreSQL 与 Redis 分别使用 `postgres-data`、`redis-data` 命名卷。
+
初始化配置:
```bash
@@ -77,7 +84,10 @@ Docker 配置文件里路径保持容器路径:
```yaml
database:
- url: 'sqlite:///app/data/codex-proxy-rs.sqlite'
+ url: 'postgres://codex_proxy:codex_proxy@postgres:5432/codex_proxy'
+
+redis:
+ url: 'redis://redis:6379/'
logging:
directory: /app/logs
@@ -142,20 +152,19 @@ CPR_CONFIG_FILE=.runtime/config.yaml cargo run --manifest-path backend/Cargo.tom
管理端功能:
- 账号导入、OAuth 授权、连接测试、quota 刷新、token 刷新。
-- 客户端 API Key 管理。
+- 客户端 API Key 管理,完整 Key 可长期复制并导入 CCSwitch。
- Dashboard、请求记录、Token 用量、模型用量和账号额度。
- 运行参数、模型别名、账号选择策略。
- 版本检查、在线更新、更新日志、重启和回滚。
## 配置与运行数据
-配置文件只承载启动必需项。账号、API Key、模型映射、账号选择策略和多数运行参数由管理端写入 SQLite。
+配置文件只承载启动必需项。账号、API Key、模型映射、账号选择策略和多数运行参数由管理端写入 PostgreSQL;管理会话、刷新租约、会话亲和与模型缓存位于 Redis。
`.runtime` 约定:
```text
.runtime/config.yaml # 本地或 Docker 启动配置,路径按运行环境写
-.runtime/data/codex-proxy-rs.sqlite # SQLite 数据库
.runtime/data/installation_id # 上游 installation id
.runtime/data/update-state.json # 在线更新状态
.runtime/data/update.lock # 在线更新锁
@@ -184,7 +193,7 @@ release/publish 1.0.4
## 项目结构
```text
-backend/ Rust/Axum 后端、SQLite migration、集成测试
+backend/ Rust/Axum 后端、PostgreSQL migration、SQLite 导入工具、集成测试
frontend/ Vue 3 管理端
deploy/ Dockerfile、Compose、部署配置模板
docs/ 架构和维护文档
diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index 909757c40..79fe04aef 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -47,6 +47,21 @@ dependencies = [
"libc",
]
+[[package]]
+name = "arc-swap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "arcstr"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d"
+
[[package]]
name = "argon2"
version = "0.5.3"
@@ -87,6 +102,17 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -171,6 +197,15 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "backon"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
+dependencies = [
+ "fastrand",
+]
+
[[package]]
name = "base64"
version = "0.22.1"
@@ -332,6 +367,7 @@ dependencies = [
"ipnet",
"rand 0.10.1",
"rand_core 0.6.4",
+ "redis",
"reqwest",
"rustls",
"rustls-native-certs",
@@ -358,6 +394,20 @@ dependencies = [
"wiremock",
]
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "memchr",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+]
+
[[package]]
name = "compression-codecs"
version = "0.4.38"
@@ -755,6 +805,16 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
[[package]]
name = "fastrand"
version = "2.4.1"
@@ -1882,6 +1942,33 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+[[package]]
+name = "redis"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fa6f8e4b491d7a8ef3a9550a4d71969bd0064f46e32b8dbbcc7fc60dad94fed"
+dependencies = [
+ "arc-swap",
+ "arcstr",
+ "async-lock",
+ "backon",
+ "bytes",
+ "cfg-if",
+ "combine",
+ "futures-channel",
+ "futures-util",
+ "itoa",
+ "percent-encoding",
+ "pin-project-lite",
+ "ryu",
+ "sha1_smol",
+ "socket2",
+ "tokio",
+ "tokio-util",
+ "url",
+ "xxhash-rust",
+]
+
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -2254,6 +2341,12 @@ dependencies = [
"digest 0.11.3",
]
+[[package]]
+name = "sha1_smol"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
+
[[package]]
name = "sha2"
version = "0.10.9"
@@ -3577,6 +3670,12 @@ dependencies = [
"rustix",
]
+[[package]]
+name = "xxhash-rust"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576"
+
[[package]]
name = "yaml-rust2"
version = "0.11.0"
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 4aca96cbd..98405de14 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -4,7 +4,6 @@ edition = "2021"
license = "MIT"
publish = false
rust-version = "1.95"
-build = "build/build.rs"
[dependencies]
argon2 = "0.5.3"
@@ -22,6 +21,7 @@ indexmap = { version = "2.0", features = ["serde"] }
ipnet = "2.12.0"
rand = "0.10.1"
rand_core = { version = "0.6.4", features = ["getrandom"] }
+redis = { version = "1.3.0", default-features = false, features = ["tokio-comp", "connection-manager", "script"] }
reqwest = { version = "=0.12.28", default-features = false, features = ["json", "stream", "cookies", "rustls-tls-native-roots", "gzip", "brotli", "zstd", "deflate", "http2"] }
rustls = { version = "=0.23.36", default-features = false, features = ["ring", "std", "tls12"] }
rustls-native-certs = "0.8.4"
@@ -31,7 +31,7 @@ semver = "1.0.27"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.150", features = ["preserve_order"] }
sha2 = "0.10.9"
-sqlx = { version = "0.9.0", features = ["runtime-tokio", "tls-rustls", "sqlite", "chrono", "uuid"] }
+sqlx = { version = "0.9.0", features = ["runtime-tokio", "tls-rustls", "postgres", "sqlite", "chrono", "uuid", "json"] }
subtle = "2.6.1"
tar = "0.4.44"
thiserror = "2.0.18"
diff --git a/backend/build/build.rs b/backend/build.rs
similarity index 100%
rename from backend/build/build.rs
rename to backend/build.rs
diff --git a/backend/scripts/check_architecture.sh b/backend/scripts/check_architecture.sh
new file mode 100755
index 000000000..5fdf124c5
--- /dev/null
+++ b/backend/scripts/check_architecture.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cd "$(dirname "$0")/.."
+
+fail=0
+
+deny() {
+ local description=$1
+ local pattern=$2
+ shift 2
+ if grep -rn --include='*.rs' -E "$pattern" "$@"; then
+ echo "ARCH VIOLATION: $description" >&2
+ fail=1
+ fi
+}
+
+domain_paths=(
+ src/infra src/upstream src/telemetry src/keys src/auth src/settings src/update
+ src/accounts src/models src/dispatch
+)
+
+deny "domain -> api/bootstrap" 'crate::(api|bootstrap)\b' "${domain_paths[@]}"
+deny "upstream -> domain" 'crate::(accounts|dispatch|telemetry|keys|auth|settings|update|models)\b' src/upstream
+deny "axum outside api/bootstrap" '\baxum(::|\b)' "${domain_paths[@]}"
+deny "sqlx in api" '\bsqlx(::|\b)' src/api
+deny "redis in api" '\bredis::' src/api
+deny "legacy module path" 'crate::(admin|proxy|runtime|web|http|config)::' src
+deny "Admin-prefixed domain type" '\bAdmin[A-Z][A-Za-z0-9]*\b' "${domain_paths[@]}"
+deny "Repository naming" '\b[A-Za-z0-9]*Repository\b' src
+deny "persistence repository naming" '\b[A-Za-z0-9_]*repository[A-Za-z0-9_]*\b' \
+ src/accounts src/auth src/dispatch src/keys src/models src/settings src/telemetry \
+ src/upstream/openai/fingerprint
+deny "Arc alias" '\bArc[[:space:]]+as[[:space:]]+' src
+
+banned=$(find src \( -name 'importing.rs' -o -name 'exporting.rs' -o -name 'testing.rs' \
+ -o -name 'util.rs' -o -name 'helper.rs' -o -name 'common.rs' -o -name 'misc.rs' \))
+if [ -n "$banned" ]; then
+ echo "ARCH VIOLATION: banned file names: $banned" >&2
+ fail=1
+fi
+
+while read -r file; do
+ lines=$(wc -l < "$file")
+ if [ "$lines" -gt 50 ]; then
+ echo "ARCH VIOLATION: $file has $lines lines (mod.rs <= 50)" >&2
+ fail=1
+ fi
+done < <(find src -name mod.rs)
+
+while read -r file; do
+ lines=$(wc -l < "$file")
+ if [ "$lines" -gt 800 ]; then
+ echo "ARCH VIOLATION: $file has $lines lines (> 800)" >&2
+ fail=1
+ fi
+done < <(find src -name '*.rs' ! -name mod.rs)
+
+exit "$fail"
diff --git a/backend/src/upstream/accounts/model.rs b/backend/src/accounts/account.rs
similarity index 100%
rename from backend/src/upstream/accounts/model.rs
rename to backend/src/accounts/account.rs
diff --git a/backend/src/accounts/cookies/mod.rs b/backend/src/accounts/cookies/mod.rs
new file mode 100644
index 000000000..f46bf4eca
--- /dev/null
+++ b/backend/src/accounts/cookies/mod.rs
@@ -0,0 +1,5 @@
+//! 账号 Cookie 存储。
+
+mod store;
+
+pub use store::*;
diff --git a/backend/src/upstream/accounts/cookies.rs b/backend/src/accounts/cookies/store.rs
similarity index 85%
rename from backend/src/upstream/accounts/cookies.rs
rename to backend/src/accounts/cookies/store.rs
index 9752ee167..02f569fdc 100644
--- a/backend/src/upstream/accounts/cookies.rs
+++ b/backend/src/accounts/cookies/store.rs
@@ -3,7 +3,7 @@
use std::{collections::HashMap, sync::Arc};
use chrono::{DateTime, Duration, Utc};
-use sqlx::{Row, SqlitePool};
+use sqlx::{PgPool, Row};
use thiserror::Error;
use tokio::sync::RwLock;
use uuid::Uuid;
@@ -11,35 +11,31 @@ use uuid::Uuid;
const DEFAULT_COOKIE_DOMAIN: &str = "chatgpt.com";
const CAPTURABLE_COOKIES: &[&str] = &["cf_clearance"];
-/// SQLite Cookie 存储错误。
+/// PostgreSQL Cookie 存储错误。
#[derive(Debug, Error)]
-pub enum SqliteCookieStoreError {
+pub enum PgCookieStoreError {
/// 数据库错误。
- #[error("sqlite cookie store database error: {0}")]
+ #[error("PostgreSQL cookie store database error: {0}")]
Database(#[from] sqlx::Error),
}
-/// SQLite Cookie 存储结果。
-pub type SqliteCookieStoreResult = Result;
+/// PostgreSQL Cookie 存储结果。
+pub type PgCookieStoreResult = Result;
-/// SQLite Cookie 存储。
+/// PostgreSQL Cookie 存储。
#[derive(Clone)]
-pub struct SqliteCookieStore {
- pool: SqlitePool,
+pub struct PgCookieStore {
+ pool: PgPool,
}
-impl SqliteCookieStore {
+impl PgCookieStore {
/// 构造 Cookie 存储。
- pub fn new(pool: SqlitePool) -> Self {
+ pub fn new(pool: PgPool) -> Self {
Self { pool }
}
/// 捕获上游 `Set-Cookie` 响应头中允许持久化的 Cookie。
- pub async fn capture_set_cookie(
- &self,
- account_id: &str,
- raw: &str,
- ) -> SqliteCookieStoreResult<()> {
+ pub async fn capture_set_cookie(&self, account_id: &str, raw: &str) -> PgCookieStoreResult<()> {
let Some(parsed) = parse_set_cookie(raw) else {
return Ok(());
};
@@ -54,7 +50,7 @@ impl SqliteCookieStore {
&self,
account_id: &str,
request_domain: &str,
- ) -> SqliteCookieStoreResult