Skip to content

fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据#720

Merged
ding113 merged 2 commits intoding113:devfrom
WAY29:fix/actions-session-propagation
Feb 5, 2026
Merged

fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据#720
ding113 merged 2 commits intoding113:devfrom
WAY29:fix/actions-session-propagation

Conversation

@WAY29
Copy link
Contributor

@WAY29 WAY29 commented Feb 4, 2026

Summary

Fix session propagation in /api/actions endpoints to ensure getSession() returns valid data when called from within actions.

Problem

After PR #704 fixed the authentication issue (#687) by adding allowReadOnlyAccess: true to endpoints, a related problem remained: when the action adapter (Hono-based) successfully authenticates a user and the action internally calls getSession(), it returns null because:

  1. The adapter layer uses Hono to read Cookie/Authorization headers and calls validateKey()
  2. The action layer traditionally relies on next/headers to read request context
  3. In certain runtimes, the action cannot access the Next.js headers context, causing getSession() to return null
  4. This results in actions returning ok: true but with empty data

Related Issues:

Solution

Introduce AsyncLocalStorage to propagate the authenticated session from the adapter layer to the action layer:

  1. New session storage (auth-session-storage.node.ts): Creates a global AsyncLocalStorage instance for request-scoped session storage
  2. Session injection functions (auth.ts): Adds runWithAuthSession() to wrap action execution with session context, and getScopedAuthSession() to retrieve the injected session
  3. Priority-based session retrieval: Modifies getSession() to first check for an adapter-injected session before falling back to next/headers
  4. Adapter integration (action-adapter-openapi.ts): Wraps authenticated action calls with runWithAuthSession() to inject the validated session

Changes

Core Changes

File Change
src/lib/auth-session-storage.node.ts New file: AsyncLocalStorage-based session storage
src/lib/auth.ts Add runWithAuthSession(), getScopedAuthSession(), and priority check in getSession()
src/lib/api/action-adapter-openapi.ts Wrap action calls with runWithAuthSession() when authenticated
src/app/api/actions/[...route]/route.ts Import session storage module to initialize global storage

Test Changes

File Change
tests/api/action-adapter-auth-session.unit.test.ts New regression test verifying session propagation

Testing

Automated Tests

  • Unit test added: Verifies getSession() returns injected session within action context
  • Test mocks next/headers to throw error, ensuring the new path is used

Manual Testing

Verified with curl command:

curl http://localhost:13500/api/actions/users/getUsers \
  --request POST \
  --header 'Content-Type: application/json' \
  --cookie 'auth-token=YOUR_SECRET_TOKEN' \
  --data '{}'

Regular user tokens now return valid user data instead of empty results.

Breaking Changes

None. This is a backwards-compatible fix that improves session handling without changing the API contract.

Checklist

  • Code follows project conventions
  • Self-review completed
  • Tests pass locally
  • No breaking changes

Description enhanced by Claude AI

Greptile Overview

Greptile Summary

Fixes session propagation in /api/actions endpoints by introducing AsyncLocalStorage-based context passing, ensuring getSession() returns valid data when called from within actions.

Key Changes

  • New session storage (auth-session-storage.node.ts): Global AsyncLocalStorage singleton for request-scoped session storage
  • Session injection (auth.ts): runWithAuthSession() wraps execution context, getScopedAuthContext() retrieves injected session
  • Priority-based retrieval: getSession() checks ALS context first, falls back to next/headers
  • Adapter integration: Action calls wrapped with runWithAuthSession() when authenticated
  • Permission inheritance: Scoped sessions respect their creation-time allowReadOnlyAccess setting, with support for explicit downgrade

Technical Approach

The solution addresses the runtime incompatibility where actions cannot access Next.js headers() context by:

  1. Adapter layer validates credentials via validateKey()
  2. Session stored in AsyncLocalStorage before action execution
  3. Actions retrieve session from ALS instead of Next.js headers
  4. Permission semantics preserved with proper inheritance logic

Test Coverage

Comprehensive unit test verifies:

  • Session propagation through ALS
  • Correct handling of read-only keys (canLoginWebUi: false)
  • Permission downgrade when calling getSession({allowReadOnlyAccess: false})
  • Fallback doesn't trigger next/headers in action context

Confidence Score: 5/5

  • This PR is safe to merge - it's a well-architected fix with comprehensive test coverage
  • Elegant solution using standard Node.js AsyncLocalStorage pattern, proper permission semantics preservation, excellent test coverage, and backwards-compatible implementation with fallback to existing behavior
  • No files require special attention - all changes follow established patterns and include proper safeguards

Important Files Changed

Filename Overview
src/lib/auth-session-storage.node.ts New file creates AsyncLocalStorage-based session storage using global singleton pattern
src/lib/auth.ts Adds session injection helpers and priority-based session retrieval with proper permission inheritance
src/lib/api/action-adapter-openapi.ts Wraps authenticated action calls with session context propagation using runWithAuthSession

Sequence Diagram

sequenceDiagram
    participant Client
    participant Adapter as Hono Adapter
    participant Route as Action Route Handler
    participant Storage as AsyncLocalStorage
    participant Action as Server Action
    participant GetSession as getSession()

    Client->>Adapter: HTTP Request with credentials
    Adapter->>Route: Forward to action route
    
    Route->>Route: validateKey() authenticates user
    
    Route->>Storage: runWithAuthSession(session, fn, opts)
    Storage->>Storage: Store session in ALS context
    Storage->>Action: Execute action function
    
    Action->>GetSession: Call getSession()
    GetSession->>Storage: getScopedAuthContext()
    Storage-->>GetSession: Return context from ALS
    
    Note over GetSession: Priority logic:<br/>1. ALS context (injected)<br/>2. next/headers (fallback)
    
    GetSession->>GetSession: Apply permission downgrade if needed
    
    GetSession-->>Action: Return session or null
    Action-->>Storage: Return result
    Storage-->>Route: Propagate result
    Route-->>Client: HTTP Response
Loading

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @WAY29, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在修复 /api/actions 接口在某些情况下无法正确返回数据的问题,其根本原因是认证会话在 API 适配器层和业务 action 层之间未能有效传递。通过引入基于 AsyncLocalStorage 的会话透传机制,确保了认证信息在整个请求处理流程中的一致性,从而使 getUsers 等需要认证信息的 action 能够正常工作并返回预期数据。

Highlights

  • 引入异步本地存储 (AsyncLocalStorage) 进行会话透传: 为了解决在 /api/actions 路由中认证会话无法正确传递到 action 内部的问题,引入了基于 AsyncLocalStorage 的会话存储机制。这允许在请求的整个异步生命周期中,将认证会话从 API 适配器层传递到业务 action 层。
  • 修改认证会话获取逻辑: 更新了 getSession 函数,使其优先从新引入的异步本地存储中获取会话。如果存储中存在会话,则直接使用,避免了在某些运行时环境下因无法访问 next/headers 而导致会话丢失的问题。
  • API 适配器集成会话透传: 在 createActionRoute 函数中,当认证成功后,会将验证过的 AuthSession 通过 runWithAuthSession 方法注入到 action 的执行上下文中,确保 action 内部能够正确访问到当前用户的认证信息。
  • 新增单元测试: 添加了一个新的单元测试文件,专门验证 /api/actions 适配器在鉴权通过后,action 内部调用 getSession() 能够正确获取到注入的会话,从而避免返回空数据的问题。
Changelog
  • src/lib/api/action-adapter-openapi.ts
    • 引入 runWithAuthSession 函数以支持会话透传。
    • createActionRoute 中声明 authSession 变量,用于存储验证后的会话。
    • 在认证成功后,将验证的会话赋值给 authSession
    • 修改 action 的调用方式,使用 runWithAuthSessionauthSession 注入到 action 的执行上下文中。
  • src/lib/auth-session-storage.node.ts
    • 新增文件,用于实现 Node.js 环境下的 AsyncLocalStorage 会话存储。
    • 定义并初始化全局变量 __cchAuthSessionStorage,作为 AuthSessionStorage 的实例。
  • src/lib/auth.ts
    • 定义 AuthSessionStorage 类型,规范会话存储接口。
    • globalThis 中声明 __cchAuthSessionStorage 全局变量。
    • 新增 runWithAuthSession 函数,用于在指定会话上下文中执行回调函数。
    • 新增 getScopedAuthSession 函数,用于获取当前作用域的认证会话。
    • 修改 getSession 函数,使其优先通过 getScopedAuthSession 获取会话,以支持适配器注入的请求级会话。
  • tests/api/action-adapter-auth-session.unit.test.ts
    • 新增单元测试文件,验证 api/actions 适配器会话透传功能。
    • 模拟 next/headers 以确保测试中不会意外调用。
    • 测试在 requiresAuth=true 的情况下,action 内部的 getSession() 能正确获取到注入的会话,并返回正确的用户 ID。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

在服务器端增加基于 AsyncLocalStorage 的全局作用域认证会话存储,并在 action adapter 中支持在存在认证会话时通过 runWithAuthSession 包装 action 调用;新增单元测试验证会话传播。

Changes

Cohort / File(s) Summary
认证会话存储基础设施
src/lib/auth-session-storage.node.ts, src/lib/auth.ts
新增服务端 AsyncLocalStorage 实现和全局 AuthSessionStorage 声明;新增类型 ScopedAuthContextAuthSessionStorage 与函数 runWithAuthSessiongetScopedAuthSessiongetScopedAuthContextgetSession 优先使用作用域会话。
Action Adapter 集成
src/lib/api/action-adapter-openapi.ts, src/app/api/actions/[...route]/route.ts
在 adapter 中导入并使用 runWithAuthSession:当存在验证后的会话且需要认证时,用 runWithAuthSession 包装 action 调用;另在 route 文件添加对 auth-session-storage 的副作用导入以确保初始化。
测试覆盖
tests/api/action-adapter-auth-session.unit.test.ts
新增单元测试,模拟验证返回的会话并断言在 requiresAuth 情形下会话能被传递到 action(验证 validateKey 调用、action 执行次数和 200 响应)。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 分钟

Possibly related PRs

Suggested reviewers

  • ding113
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确地概括了主要变更:修复 /api/actions 端点中的会话传播,使 getSession() 能够返回有效数据。
Description check ✅ Passed 描述详细说明了问题、解决方案和所有受影响的文件,与代码变更完全相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the bug Something isn't working label Feb 4, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

此拉取请求通过引入基于 AsyncLocalStorage 的认证会话透传机制,有效解决了 /api/actions 认证会话无法正确传递到 getUsers 等内部 action 函数的问题。新增的 runWithAuthSessiongetScopedAuthSession 函数提供了在不同上下文(如 Hono 适配器和 Next.js getSession)中访问和管理认证会话的统一方式,这是一个很好的改进,提升了 API 的健壮性和可维护性。然而,我们发现 getSession 实现中存在一个高严重性安全漏洞,注入的会话绕过了 allowReadOnlyAccess 检查,可能导致受限的 API 密钥执行未经授权的操作。请务必应用建议的修复,以确保即使从作用域存储中检索会话时,其权限也能得到正确验证。

@github-actions github-actions bot added the size/S Small PR (< 200 lines) label Feb 4, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR implements a clean solution to fix session propagation in the /api/actions adapter by using AsyncLocalStorage. The implementation is well-designed with proper defensive coding and adequate test coverage.

PR Size: S

  • Lines changed: 169 (167 additions, 2 deletions)
  • Files changed: 5

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Key Implementation Highlights

Correct Use of AsyncLocalStorage: The PR uses Node.js AsyncLocalStorage to propagate authentication sessions from the Hono adapter layer to Server Actions, solving the issue where next/headers is unavailable in the Hono context.

Defensive Fallbacks: The code includes proper fallback logic - if the storage isn't initialized, it safely returns without session propagation. The test verifies this behavior by mocking next/headers to throw, ensuring the scoped session is used.

Type Safety: The type cast in auth-session-storage.node.ts is intentional to create a minimal interface contract, and the actual runtime behavior matches the type definitions.

Test Coverage: The regression test adequately covers the critical path (requiresAuth=true with successful authentication), and the test design correctly validates that the session propagation works by mocking next/headers.cookies() to throw an error.

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Automated review by Claude AI

@coderabbitai coderabbitai bot requested a review from ding113 February 4, 2026 06:15
@ding113 ding113 merged commit 93585fa into ding113:dev Feb 5, 2026
6 of 8 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 5, 2026
@github-actions github-actions bot mentioned this pull request Feb 7, 2026
10 tasks
ding113 added a commit that referenced this pull request Feb 7, 2026
* fix(proxy): add 'cannot be modified' error detection to thinking signature rectifier

Extend the thinking signature rectifier to detect and handle the
Anthropic API error when thinking/redacted_thinking blocks have been
modified from their original response. This error occurs when clients
inadvertently modify these blocks in multi-turn conversations.

The rectifier will now remove these blocks and retry the request,
similar to how it handles other thinking-related signature errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore(deps): bump jspdf in the npm_and_yarn group across 1 directory

Bumps the npm_and_yarn group with 1 update in the / directory: [jspdf](https://github.com/parallax/jsPDF).


Updates `jspdf` from 3.0.4 to 4.1.0
- [Release notes](https://github.com/parallax/jsPDF/releases)
- [Changelog](https://github.com/parallax/jsPDF/blob/master/RELEASE.md)
- [Commits](parallax/jsPDF@v3.0.4...v4.1.0)

---
updated-dependencies:
- dependency-name: jspdf
  dependency-version: 4.1.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: Hot-reload cache invalidation for Request Filters and Sensitive Words (#710)

* fix: hot-reload request filters via globalThis singleton pattern

EventEmitter and RequestFilterEngine now use globalThis caching to ensure
the same instance is shared across different Next.js worker contexts.
This fixes the issue where filter changes required Docker restart.

Added diagnostic logging for event subscription and propagation.

* fix(redis): wait for subscriber connection ready before subscribe

- ensureSubscriber now returns Promise<Redis>, waits for 'ready' event
- subscribeCacheInvalidation returns null on failure instead of noop
- RequestFilterEngine checks cleanup !== null before logging success
- Fixes false "Subscribed" log when Redis connection fails

* feat(sensitive-words): add hot-reload via Redis pub/sub

Enable real-time cache invalidation for sensitive words detector,
matching the pattern used by request-filter-engine and error-rule-detector.

* fix(redis): harden cache invalidation subscriptions

Ensure sensitive-words CRUD emits update events so hot-reload propagates across workers. Roll back failed pub/sub subscriptions, add retry/timeout coverage, and avoid sticky provider-cache subscription state.

* fix(codex): bump default User-Agent fallback

Update the hardcoded Codex UA used when requests lack an effective user-agent (e.g. filtered out). Keep unit tests in sync with the new default.

* fix(redis): resubscribe cache invalidation after reconnect

Clear cached subscription state on disconnect and resubscribe on ready so cross-worker cache invalidation survives transient Redis reconnects. Add unit coverage, avoid misleading publish logs, track detector cleanup handlers, and translate leftover Russian comments to English.

* fix(sensitive-words): use globalThis singleton detector

Align SensitiveWordDetector with existing __CCH_* singleton pattern to avoid duplicate instances across module reloads. Extend singleton unit tests to cover the detector.

* chore: format code (req-fix-dda97fd)

* fix: address PR review comments

- pubsub.ts: use `once` instead of `on` for ready event to prevent
  duplicate resubscription handlers on reconnect
- forwarder.ts: extract DEFAULT_CODEX_USER_AGENT constant
- provider-cache.ts: wrap subscribeCacheInvalidation in try/catch
- tests: use exported constant instead of hardcoded UA string

* fix(redis): resubscribe across repeated reconnects

Ensure pub/sub resubscribe runs on every reconnect, extend unit coverage, and keep emitRequestFiltersUpdated resilient when logger import fails.

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* feat(logs): make cost column toggleable with improved type safety (#715)

close #713

* fix(proxy): add OpenAI chat completion format support in usage extraction (#705) (#716)

The `extractUsageMetrics` function was missing support for OpenAI chat
completion format fields (`prompt_tokens`/`completion_tokens`), causing
token statistics to not be recorded for OpenAI-compatible providers.

Changes:
- Add `prompt_tokens` -> `input_tokens` mapping
- Add `completion_tokens` -> `output_tokens` mapping
- Preserve priority: Claude > Gemini > OpenAI format
- Add 5 unit tests for OpenAI format handling

Closes #705

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(currency): respect system currencyDisplay setting in UI (#717)

Fixes #678 - Currency display unit configuration was not applied.

Root cause:
- `users-page-client.tsx` hardcoded `currencyCode="USD"`
- `UserLimitBadge` and `LimitStatusIndicator` had hardcoded `unit="$"` default
- `big-screen/page.tsx` used hardcoded "$" in multiple places

Changes:
- Add `getCurrencySymbol()` helper function to currency.ts
- Fetch system settings in `users-page-client.tsx` and pass to table
- Pass `currencySymbol` from `user-key-table-row.tsx` to limit badges
- Remove hardcoded "$" defaults from badge components
- Update big-screen page to fetch settings and use dynamic symbol
- Add unit tests for `getCurrencySymbol`

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(gemini): add Google Search web access preference for Gemini providers (#721)

* feat(gemini): add Google Search web access preference for Gemini providers

Add provider-level preference for Gemini API type providers to control
Google Search (web access) tool injection:

- inherit: Follow client request (default)
- enabled: Force inject googleSearch tool into request
- disabled: Force remove googleSearch tool from request

Changes:
- Add geminiGoogleSearchPreference field to provider schema
- Add GeminiGoogleSearchPreference type and validation
- Implement applyGeminiGoogleSearchOverride with audit trail
- Add UI controls in provider form (Gemini Overrides section)
- Add i18n translations for 5 languages (en, zh-CN, zh-TW, ja, ru)
- Integrate override logic in proxy forwarder for Gemini requests
- Add 22 unit tests for the override logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(gemini): address code review feedback

- Use explicit else-if for disabled preference check (gemini-code-assist)
- Use i18n for SelectValue placeholder instead of hardcoded string (coderabbitai)
- Sync overridden body back to session.request.message for log consistency (coderabbitai)
- Persist Gemini special settings immediately, matching Anthropic pattern (coderabbitai)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(gemini): use strict types for provider config and audit

- Narrow preference type to "enabled" | "disabled" (exclude unreachable "inherit")
- Use ProviderType and GeminiGoogleSearchPreference types instead of string

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据 (#720)

* fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据

* fix(auth): 让 scoped session 继承 allowReadOnlyAccess 语义并支持内部降权校验

* chore: format code (dev-93585fa)

* fix: bound SessionTracker active_sessions zsets by env TTL (#718)

* fix(session): bound active_sessions zsets by env ttl

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(rate-limit): pass session ttl to lua cleanup

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(session): validate SESSION_TTL env and prevent ZSET leak on invalid values

- Add input validation for SESSION_TTL (reject NaN, 0, negative; default 300)
- Guard against invalid TTL in Lua script to prevent clearing all sessions
- Use dynamic EXPIRE based on SESSION_TTL instead of hardcoded 3600s
- Add unit tests for TTL validation and dynamic expiry behavior

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(provider): stop standard-path fallback to legacy provider url

* feat(providers): expose vendor endpoint pools in settings UI (#719)

* feat(providers): add endpoint status mapping

* feat(providers): add endpoint pool hover

* feat(providers): show vendor endpoints in list rows

* feat(providers): extract vendor endpoint CRUD table

* chore(i18n): add provider endpoint UI strings

* fix(providers): integrate endpoint pool into provider form

* fix(provider): wrap endpoint sync in transactions to prevent race conditions (#730)

* fix(provider): wrap provider create/update endpoint sync in transactions

Provider create and update operations now run vendor resolution and
endpoint sync inside database transactions to prevent race conditions
that could leave orphaned or inconsistent endpoint rows.

Key changes:
- createProvider: wrap vendor + insert + endpoint seed in a single tx
- updateProvider: wrap vendor + update + endpoint sync in a single tx
- Add syncProviderEndpointOnProviderEdit for atomic URL/type/vendor
  migration with in-place update, soft-delete, and conflict handling
- Vendor cleanup failures degrade to warnings instead of propagating
- Add comprehensive unit and integration tests for sync edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(provider): defer endpoint circuit reset until transaction commit

Avoid running endpoint circuit reset side effects inside DB transactions to prevent rollback inconsistency. Run resets only after commit and add regression tests for deferred reset behavior in helper and provider update flows.

* fix(provider): distinguish noop from created-next in endpoint sync action label

When ensureNextEndpointActive() returns "noop" (concurrent transaction
already created an active next endpoint), the action was incorrectly
labelled "kept-previous-and-created-next". Add a new
"kept-previous-and-kept-next" action to ProviderEndpointSyncAction and
use a three-way branch so callers and logs reflect the true outcome.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review comments from PR #731

- fix(auth): prevent scoped session access widening via ?? -> && guard
- fix(i18n): standardize zh-CN provider terminology to "服务商"
- fix(i18n): use consistent Russian translations for circuit status
- fix(i18n): replace raw formatDistanceToNow with locale-aware RelativeTime
- fix(gemini): log warning for unknown google search preference values
- fix(error-rules): check subscribeCacheInvalidation return value
- fix(test): correct endpoint hover sort test to assert URLs not labels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: export auth session storage and fix test mock types

- Export authSessionStorage from auth-session-storage.node.ts to prevent
  undefined on named imports; remove duplicate declare global block
- Fix mockEndpoints in provider-endpoint-hover test: remove nonexistent
  lastOk/lastLatencyMs fields, add missing lastProbe* fields, use Date
  objects for createdAt/updatedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com>
Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: 泠音 <im@ling.plus>
Co-authored-by: Longlone <41830147+WAY29@users.noreply.github.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size/S Small PR (< 200 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants