feat: merge upstream v3.1.2 with sing-box egress integration - #1
Conversation
feat: 创作控制台聊天支持单条消息重生成/修改/删除与停止生成
gpt-5.4 → grok-4.5 gpt-5.5 → grok-4.5 并与发现/目录产生的 grok-4.5 → grok-4.5 共存。 仍保持 对外名 public_id 全局唯一;重复创建同一个 gpt-5.5 继续 409。
…user Map definitive upstream dead-account signals to provider.ErrUnauthorized so import identity sync and Web quota refresh persist reauthRequired (UI: 失效) and drop accounts from scheduling. - Session Parse: status=blocked (no identity) → ErrUnauthorized - Web rate-limits: HTTP 403 body with blocked-user / user is blocked → ErrUnauthorized - Keep generic missing-identity and non-blocked 403 as non-invalidating failures - Add sessionidentity Parse tests and Web quota blocked/generic 403 tests
Classify HTTP 403 by response body before egress retry. When body contains blocked-user or user is blocked, mark reauthRequired and rotate accounts; other 403s keep egress retry without account penalty.
Treat session status blocked/unauthenticated before accepting identity fields. On Web chat and quota 403, classify blocked-user body before Statsig invalidation retry so definitive ban signals are not dropped.
…lidation feat(account): 完善 SSO 失效——补齐 403 正文明确封号(导入 / 额度 / Web && Console 对话)
Classify Grok Build 403/429 failures so request-level safety denials and free-usage / Team RPS limits no longer mis-invalidate accounts or block failover. - Treat SAFETY_CHECK_TYPE_* / usage-guidelines 403 as request-scoped - Require explicit access-denied text for permanent account denial - Rotate free-usage-exhausted even when X-Should-Retry is false - Parse Build Team+Model RPS metadata with a 2s default cool-down - Keep previous_response_id pinned to the original account Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… modelRouteID parameter for improved routing candidate selection
放宽「同一 Provider + 同一 upstream 只能有一条路由」的限制,使管理端可创建:
feat: implement batch quota reset functionality for Build accounts
feat: updated egress binding UI
… and suppress late reasoning
feat: introduce webVisibleStreamPhase to manage client-visible output and suppress late reasoning
Allow routing.maxAttempts to be set above 10 so large account pools can retry across more candidates in a single request. Keep the existing minimum of 1 and update frontend validation/UI to match.
Expose effort-suffixed model names (e.g. grok-4.5-low) only for keys with allowModelAliases enabled, using each model's real supported reasoning levels.
Allow single and batch account deletion to also remove linked Web/Build/Console peers resolved only from binding tables (Web↔Build, Web↔Console; Build↔Console two-hop via Web without deleting intermediate Web unless targeted). - Preview counts after checkbox selection (POST deletion-preview) - Delete re-resolves targets; empty targets keep legacy root-only - DeleteManyWithLinked: lock roots, expand links, media check, and delete in one transaction (avoid resolve/delete TOCTOU) - Single delete: missing root 404; non-empty malformed body 400 - UI: linked options, select-all, spinner counts, no fake +0 on preview failure; batch API forwards linkedDeleteTargets - Status cleanup / autoClean / reauth propagation unchanged
Build and Console account tabs gain webLinked/webUnlinked filtering resolved from binding tables; grok_web keeps its six existing values. Per-provider validation rejects cross-pool values with 400. Covered by validation matrix, repository count, and combined-filter tests.
Cleanup can now delete linked Web/Build/Console peers resolved from binding tables, with COUNT-based preview in the dialog. - Repository: RootGroups/PeerProviders in the resolution, shared deleteLinkedGroupsTx helper, cursor-paged DeleteAccountStatusBatchWithLinked, CountCleanupWithLinked for preview - Media semantics: single delete still rejects the whole operation; batch delete and cleanup skip the blocked root group and report skipped - Counts now derive from rows actually deleted instead of the planned set - Cross-pool deletes invalidate every provider's candidate cache - UI: per-status counts, smooth-expanding linked block, warning that linked accounts may be healthy, preview failure blocks confirm, toasts report linked/skipped; provider switch resets cleanup dialog state - Drop the now-unused DeleteAccountStatusBatch
When no page/source title is known, use "1"/"2"/… instead of the URL.
Match non-stream chat: attach xAI tool usage next to citations on the finish_reason chunk before [DONE].
…tion fix(web): Web 上游搜索引用对齐(mgw render_citation → 客户端 citations)
- Integrate third_party/sing-box transport modules - Add egress platform/sharelink/singbox registry - Add account bulk_buffer/preselect/sso_dedup - Add response_cache retry_status and merge repro tests - Fix account/redis interface incompatibility with upstream v3.1.2 - Add Docker publish workflow and CodeQL config - Update Dockerfile and go.mod/go.sum
# Conflicts: # .dockerignore # .github/workflows/codeql.yml # .github/workflows/ghcr-image.yml # .github/workflows/stale.yml # .gitignore # Dockerfile # Makefile # README.md # VERSION # backend/README.md # backend/docs/docs.go # backend/docs/swagger.json # backend/docs/swagger.yaml # backend/go.mod # backend/internal/app/application.go # backend/internal/app/console_routes_test.go # backend/internal/app/startup.go # backend/internal/app/startup_test.go # backend/internal/application/account/console_quota_test.go # backend/internal/application/account/conversion_test.go # backend/internal/application/account/credential_refresh_test.go # backend/internal/application/account/credential_scheduler.go # backend/internal/application/account/export_test.go # backend/internal/application/account/quota_refresh_test.go # backend/internal/application/account/quota_test.go # backend/internal/application/account/service.go # backend/internal/application/account/web_console_sync_test.go # backend/internal/application/accountsync/service.go # backend/internal/application/accountsync/service_test.go # backend/internal/application/audit/service.go # backend/internal/application/audit/service_test.go # backend/internal/application/clientkey/cache.go # backend/internal/application/clientkey/service.go # backend/internal/application/clientkey/service_test.go # backend/internal/application/dashboard/service.go # backend/internal/application/dashboard/service_test.go # backend/internal/application/egress/service.go # backend/internal/application/egress/service_test.go # backend/internal/application/gateway/attempt.go # backend/internal/application/gateway/attempt_test.go # backend/internal/application/gateway/failure.go # backend/internal/application/gateway/failure_test.go # backend/internal/application/gateway/image.go # backend/internal/application/gateway/model_alias_test.go # backend/internal/application/gateway/prompt_cache.go # backend/internal/application/gateway/prompt_cache_test.go # backend/internal/application/gateway/selector.go # backend/internal/application/gateway/selector_plan.go # backend/internal/application/gateway/selector_segmented_active.go # backend/internal/application/gateway/selector_test.go # backend/internal/application/gateway/service.go # backend/internal/application/gateway/service_test.go # backend/internal/application/gateway/timing.go # backend/internal/application/gateway/timing_test.go # backend/internal/application/gateway/video.go # backend/internal/application/gateway/video_test.go # backend/internal/application/media/service.go # backend/internal/application/media/service_test.go # backend/internal/application/model/service.go # backend/internal/application/model/service_test.go # backend/internal/application/quotarecovery/service.go # backend/internal/application/quotarecovery/service_test.go # backend/internal/application/settings/service.go # backend/internal/application/settings/service_test.go # backend/internal/application/updatecheck/service_test.go # backend/internal/domain/account/account.go # backend/internal/domain/account/account_test.go # backend/internal/domain/audit/audit.go # backend/internal/domain/audit/pricing.go # backend/internal/domain/audit/pricing_test.go # backend/internal/domain/clientkey/client_key.go # backend/internal/domain/dashboard/dashboard.go # backend/internal/domain/egress/egress.go # backend/internal/domain/inference/response.go # backend/internal/domain/media/asset.go # backend/internal/domain/media/job.go # backend/internal/domain/model/model.go # backend/internal/domain/settings/settings.go # backend/internal/infra/config/config.go # backend/internal/infra/config/config_test.go # backend/internal/infra/egress/buildclient.go # backend/internal/infra/egress/buildclient_test.go # backend/internal/infra/egress/manager.go # backend/internal/infra/egress/manager_test.go # backend/internal/infra/egress/sticky_retry.go # backend/internal/infra/egress/sticky_retry_test.go # backend/internal/infra/egress/tlsclient.go # backend/internal/infra/egress/trace.go # backend/internal/infra/media/local_store.go # backend/internal/infra/persistence/relational/account_repository.go # backend/internal/infra/persistence/relational/audit_repository.go # backend/internal/infra/persistence/relational/audit_repository_test.go # backend/internal/infra/persistence/relational/client_key_repository.go # backend/internal/infra/persistence/relational/client_key_repository_test.go # backend/internal/infra/persistence/relational/console_provider_test.go # backend/internal/infra/persistence/relational/dashboard_repository.go # backend/internal/infra/persistence/relational/dashboard_repository_test.go # backend/internal/infra/persistence/relational/database.go # backend/internal/infra/persistence/relational/egress_repository.go # backend/internal/infra/persistence/relational/egress_sort_test.go # backend/internal/infra/persistence/relational/list_filter_repository_test.go # backend/internal/infra/persistence/relational/mapping.go # backend/internal/infra/persistence/relational/media_repository.go # backend/internal/infra/persistence/relational/media_repository_test.go # backend/internal/infra/persistence/relational/model_capability_repository_test.go # backend/internal/infra/persistence/relational/model_public_id.go # backend/internal/infra/persistence/relational/model_repository.go # backend/internal/infra/persistence/relational/models.go # backend/internal/infra/persistence/relational/postgres_integration_test.go # backend/internal/infra/persistence/relational/repository_test.go # backend/internal/infra/persistence/relational/response_repository.go # backend/internal/infra/persistence/relational/response_repository_test.go # backend/internal/infra/persistence/relational/schema.go # backend/internal/infra/persistence/relational/schema_console_upgrade_test.go # backend/internal/infra/persistence/relational/settings_repository_test.go # backend/internal/infra/provider/cli/adapter.go # backend/internal/infra/provider/cli/adapter_test.go # backend/internal/infra/provider/cli/billing.go # backend/internal/infra/provider/cli/billing_test.go # backend/internal/infra/provider/cli/egress.go # backend/internal/infra/provider/cli/fallback.go # backend/internal/infra/provider/cli/import.go # backend/internal/infra/provider/cli/normalize.go # backend/internal/infra/provider/cli/normalize_test.go # backend/internal/infra/provider/cli/oauth.go # backend/internal/infra/provider/cli/oauth_test.go # backend/internal/infra/provider/cli/responses_advanced_test.go # backend/internal/infra/provider/cli/responses_codex_tools.go # backend/internal/infra/provider/cli/responses_codex_tools_test.go # backend/internal/infra/provider/cli/responses_compaction.go # backend/internal/infra/provider/cli/responses_compaction_forward.go # backend/internal/infra/provider/cli/responses_compaction_test.go # backend/internal/infra/provider/cli/responses_history.go # backend/internal/infra/provider/cli/responses_input.go # backend/internal/infra/provider/cli/responses_reasoning_recovery.go # backend/internal/infra/provider/cli/responses_reasoning_recovery_test.go # backend/internal/infra/provider/cli/responses_response.go # backend/internal/infra/provider/cli/responses_tool_declarations.go # backend/internal/infra/provider/cli/responses_tool_state.go # backend/internal/infra/provider/cli/responses_tool_types.go # backend/internal/infra/provider/cli/responses_tools_test.go # backend/internal/infra/provider/cli/video.go # backend/internal/infra/provider/console/adapter.go # backend/internal/infra/provider/console/catalog.go # backend/internal/infra/provider/console/console_test.go # backend/internal/infra/provider/console/definition.go # backend/internal/infra/provider/console/import.go # backend/internal/infra/provider/console/normalize.go # backend/internal/infra/provider/conversation/chat_request.go # backend/internal/infra/provider/conversation/conversation_test.go # backend/internal/infra/provider/conversation/messages_request.go # backend/internal/infra/provider/conversation/messages_response.go # backend/internal/infra/provider/conversation/messages_stream.go # backend/internal/infra/provider/conversation/request.go # backend/internal/infra/provider/conversation/response.go # backend/internal/infra/provider/conversation/stream.go # backend/internal/infra/provider/definition.go # backend/internal/infra/provider/definition_contract_test.go # backend/internal/infra/provider/import_json.go # backend/internal/infra/provider/import_json_test.go # backend/internal/infra/provider/provider.go # backend/internal/infra/provider/web/adapter.go # backend/internal/infra/provider/web/attachments.go # backend/internal/infra/provider/web/catalog.go # backend/internal/infra/provider/web/chat.go # backend/internal/infra/provider/web/image.go # backend/internal/infra/provider/web/import.go # backend/internal/infra/provider/web/import_test.go # backend/internal/infra/provider/web/protocol_test.go # backend/internal/infra/provider/web/quota.go # backend/internal/infra/provider/web/quota_test.go # backend/internal/infra/provider/web/sso_build.go # backend/internal/infra/provider/web/statsig.go # backend/internal/infra/provider/web/statsig_test.go # backend/internal/infra/provider/web/tools.go # backend/internal/infra/provider/web/tools_test.go # backend/internal/infra/provider/web/upstream_test.go # backend/internal/infra/provider/web/video.go # backend/internal/infra/runtime/memory/quota_queue.go # backend/internal/infra/runtime/memory/quota_queue_test.go # backend/internal/infra/runtime/memory/store.go # backend/internal/infra/runtime/memory/store_test.go # backend/internal/infra/runtime/redis/response_cache.go # backend/internal/infra/runtime/redis/store.go # backend/internal/infra/runtime/redis/store_integration_test.go # backend/internal/infra/security/cipher.go # backend/internal/infra/security/cipher_test.go # backend/internal/infra/security/token.go # backend/internal/pkg/batch/executor.go # backend/internal/pkg/batch/executor_test.go # backend/internal/pkg/neterror/classify.go # backend/internal/pkg/neterror/classify_test.go # backend/internal/pkg/resultcache/cache.go # backend/internal/pkg/resultcache/cache_test.go # backend/internal/repository/account.go # backend/internal/repository/dashboard.go # backend/internal/repository/egress.go # backend/internal/repository/errors.go # backend/internal/repository/list.go # backend/internal/repository/media.go # backend/internal/repository/model.go # backend/internal/repository/response.go # backend/internal/repository/runtime.go # backend/internal/transport/http/account/handler.go # backend/internal/transport/http/account/handler_test.go # backend/internal/transport/http/adminauth/handler.go # backend/internal/transport/http/audit/handler.go # backend/internal/transport/http/audit/handler_test.go # backend/internal/transport/http/clientkey/handler.go # backend/internal/transport/http/dashboard/handler.go # backend/internal/transport/http/dashboard/handler_test.go # backend/internal/transport/http/egress/handler.go # backend/internal/transport/http/inference/handler.go # backend/internal/transport/http/inference/handler_test.go # backend/internal/transport/http/inference/model_list_test.go # backend/internal/transport/http/inference/prompt_cache.go # backend/internal/transport/http/inference/prompt_cache_test.go # backend/internal/transport/http/media/dto.go # backend/internal/transport/http/media/handler.go # backend/internal/transport/http/media/handler_test.go # backend/internal/transport/http/middleware/auth.go # backend/internal/transport/http/middleware/auth_test.go # backend/internal/transport/http/middleware/concurrency.go # backend/internal/transport/http/middleware/request.go # backend/internal/transport/http/middleware/request_test.go # backend/internal/transport/http/model/handler.go # backend/internal/transport/http/model/handler_test.go # backend/internal/transport/http/server.go # backend/internal/transport/http/settings/handler.go # backend/internal/transport/http/settings/security_test.go # backend/internal/transport/http/swagger_annotations.go # config.example.yaml # docker-compose.yml # docker/entrypoint.sh # frontend/package.json # frontend/pnpm-lock.yaml # frontend/src/app/app-shell.tsx # frontend/src/app/deferred-pages.tsx # frontend/src/app/router.tsx # frontend/src/components/ui/dropdown-menu.tsx # frontend/src/components/ui/input.tsx # frontend/src/components/ui/select.tsx # frontend/src/components/ui/table.tsx # frontend/src/components/ui/tabs.tsx # frontend/src/components/ui/textarea.tsx # frontend/src/entities/model/model-api.ts # frontend/src/entities/model/types.ts # frontend/src/features/accounts/account-quota.tsx # frontend/src/features/accounts/accounts-api.ts # frontend/src/features/accounts/accounts-page.tsx # frontend/src/features/audits/request-audit-detail-dialog.tsx # frontend/src/features/audits/request-audits-api.ts # frontend/src/features/audits/request-audits-page.tsx # frontend/src/features/client-keys/client-keys-api.ts # frontend/src/features/client-keys/client-keys-page.tsx # frontend/src/features/creative-console/creative-console-api.ts # frontend/src/features/creative-console/creative-console-page.tsx # frontend/src/features/dashboard/dashboard-api.ts # frontend/src/features/dashboard/dashboard-page.tsx # frontend/src/features/docs/api-docs-page.tsx # frontend/src/features/media/gallery-page.tsx # frontend/src/features/media/media-api.ts # frontend/src/features/media/types.ts # frontend/src/features/media/video-gallery-page.tsx # frontend/src/features/models/models-page.tsx # frontend/src/features/settings/settings-api.ts # frontend/src/features/settings/settings-model.ts # frontend/src/features/settings/settings-page.tsx # frontend/src/features/settings/use-settings.ts # frontend/src/features/system/version-update.tsx # frontend/src/index.css # frontend/src/shared/api/client.ts # frontend/src/shared/api/decoder.ts # frontend/src/shared/clipboard.ts # frontend/src/shared/components/data-table-filters.tsx # frontend/src/shared/components/date-time-picker.tsx # frontend/src/shared/components/page-header.tsx # frontend/src/shared/components/pagination.tsx # frontend/src/shared/components/period-selector.tsx # frontend/src/shared/components/site-footer.tsx # frontend/src/shared/i18n/index.ts # frontend/src/shared/lib/format.ts # frontend/src/shared/lib/period.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fd88685d08
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return "", ErrSecretUnavailable | ||
| } | ||
| raw, err := s.cipher.DecryptAAD(value.EncryptedSecret, security.ClientKeyAAD(value.Prefix)) | ||
| raw, err := s.cipher.Decrypt(value.EncryptedSecret) |
There was a problem hiding this comment.
Restore AAD-bound client key decryption
When an operator reveals a key created by the previous release, its encrypted copy was sealed with security.ClientKeyAAD(prefix); this now calls the no-AAD decrypt path, so GCM authentication fails and the admin can no longer copy existing keys even though request authentication still works via the hash. Keep the AAD decrypt fallback for stored client-key ciphertexts before switching formats.
Useful? React with 👍 / 👎.
| if c.Media.Driver != "local" { | ||
| return errors.New("media.driver 当前仅支持 local") |
There was a problem hiding this comment.
When media.driver: r2 is configured, validation now rejects it unconditionally, even though the example config still documents R2 and the R2 store code remains in the tree. Existing R2-backed deployments will fail during startup before storage can be initialized, so either keep r2 in MediaConfig/validation or provide a migration path.
Useful? React with 👍 / 👎.
| raw, ok := bearerToken(c.GetHeader("Authorization")) | ||
| if !ok { | ||
| raw = strings.TrimSpace(c.GetHeader("X-API-Key")) |
There was a problem hiding this comment.
Honor configured API key headers
When an installation uses auth.apiKeyHeaders for clients or proxies that cannot set Authorization/X-API-Key, this fallback now ignores every configured custom header, so those requests authenticate with an empty key and fail with invalid_api_key. Preserve the configured header extraction in this path rather than limiting authentication to the two built-in headers.
Useful? React with 👍 / 👎.
admin: Token 列表分页上限扩展到 2000
Summary
Merge upstream v3.1.2 (1188 commits) into fork while preserving all fork-specific features.
Upstream updates (v3.0.3 → v3.1.2)
Fork features preserved
Build
go build ./cmd/grok2apipasses ✅