Skip to content

添加openai缓存计费显示#20

Merged
CSCITech merged 21 commits into
mainfrom
cscitech
Jul 18, 2026
Merged

添加openai缓存计费显示#20
CSCITech merged 21 commits into
mainfrom
cscitech

Conversation

@CSCITech

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b9f0f307-c700-4342-98e1-75ca08eddb6d

📥 Commits

Reviewing files that changed from the base of the PR and between 21e262c and ebbcc24.

📒 Files selected for processing (3)
  • model/main.go
  • model/usedata.go
  • model/usedata_test.go
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • model/main.go
  • model/usedata_test.go
  • model/usedata.go
🔇 Additional comments (3)
model/usedata.go (1)

76-81: LGTM!

Also applies to: 974-977, 993-996, 1051-1084, 1085-1128, 1129-1134, 1164-1170

model/main.go (1)

324-324: LGTM!

Also applies to: 396-396

model/usedata_test.go (1)

297-297: LGTM!

Also applies to: 338-351, 363-394


📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Channel connectivity tests now show clearer targets, response times, and refreshed results.
    • Notifications adapt between desktop and mobile layouts.
    • Pricing expressions support safer, allowlisted request metadata.
    • Turnstile verification can be submitted through request headers.
  • Bug Fixes
    • Improved quota, billing, subscription, and cache reliability during failures and retries.
    • OAuth errors now provide safer, more consistent messages without exposing sensitive details.
    • Search now treats special characters literally.
    • Stream termination and redirect handling are more reliable.
  • Documentation
    • Added guidance recommending production databases instead of SQLite.

Walkthrough

The PR updates Redis cache fencing, quota persistence and accounting, OAuth error handling, billing settlement, request validation, expression evaluation, configuration loading, and frontend interaction flows.

Changes

Backend and frontend behavior updates

Layer / File(s) Summary
Quota, cache, and persistence consistency
common/*, model/*, service/*
Versioned Redis writes, cache retries, int64 quotas, quota-data upserts, migrations, and atomic quota mutations are updated.
OAuth identity error propagation
oauth/*, controller/*, model/user*
OAuth identity checks now return and handle lookup errors, including soft-deleted users and sanitized responses.
Billing settlement and quota consumption
service/billing_*, service/pre_consume_quota.go
Funding compensation, settlement retries, subscription funding state, and combined quota pre-consumption are updated.
Request validation and expression security
middleware/*, setting/config/*, pkg/billingexpr/*, web/default/src/lib/*
Turnstile headers, configuration errors, expression allowlists, safe errors, redirects, and OAuth binding state are updated.
Frontend flows and interaction updates
web/default/src/features/*, web/default/src/components/*, web/default/src/routes/*
Channel test cache refreshes, responsive notifications, stream lifecycle state, draft model editing, translations, and secure external navigation are updated.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit hops through caches bright,
Fences stale data day and night.
Quotas count and streams behave,
OAuth errors now are brave.
Tiny headers guard the gate!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is mostly a template and does not summarize the code changes in a meaningful way. Replace it with a brief human-written summary of the actual changes, rationale, and validation performed.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding cache billing display for OpenAI-related usage logs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cscitech

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx`:
- Line 232: Add an explicit return type annotation to the getModelRowSignature
function, using the actual type of the value it returns and preserving its
current behavior.
- Around line 557-576: Replace the nested ternary used for billingMode in the
setEditData block with an explicitly typed variable using the exact allowed
billing-mode literals, and assign it through straightforward if-else logic:
tiered_expr for that billing mode, per-request when a price is present,
otherwise per-token. Use the variable in the existing setEditData call and leave
the surrounding editableModel mapping unchanged.

In `@web/default/src/i18n/locales/ru.json`:
- Line 67: Update the "{{target}} test failed" translation in the Russian locale
to explicitly indicate test failure, using wording such as "Тест {{target}} не
пройден" or "Тест {{target}} завершился с ошибкой" instead of language meaning
not performed or completed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cb798633-29c4-418b-aeaf-6d7842260e8e

📥 Commits

Reviewing files that changed from the base of the PR and between d07cf81 and 6b6e051.

📒 Files selected for processing (12)
  • web/default/src/features/channels/components/data-table-row-actions.tsx
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/channels/types.ts
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (4)
web/default/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/default/AGENTS.md)

web/default/src/**/*.{ts,tsx}: 前端页面文本与组件内文案必须支持 i18n:React 组件中应使用 useTranslation() 取得 t,并通过 t() 渲染用户可见文本;子组件也应自行使用 useTranslation() 保持独立性。
禁止使用两层及以上嵌套三元表达式;复杂逻辑应改用 if-else、提前返回或抽取函数。
控制函数圈复杂度,复杂逻辑应拆成更小的函数;变量与函数命名应有意义并遵循驼峰等常规命名约定。
TypeScript 代码应避免使用 any,优先使用具体类型或 unknown;参数与返回值应显式标注类型;仅类型用途的导入应使用 import type
修改 TypeScript 或 TSX 代码后必须执行类型检查,并修复所有类型错误,不得遗留。
对象非必要不要解构,尤其是组件 props;优先直接使用 props.xxx 以保持代码清晰。
组件应使用函数式组件与 Hooks,遵循单一职责;组件 props 必须有明确类型(接口或类型别名)。
单文件超过约 200 行时应考虑拆分子组件或抽取自定义 Hooks;类型定义可与组件同文件或放在同模块的 types 中。
在 React 中应合理使用 useMemouseCallbackReact.memo,避免在渲染路径中创建新对象或数组;必要时进行代码分割与动态 import
React Query 的数据获取应使用 useQuery、变更应使用 useMutation;每个查询需配置唯一 queryKey,并在成功后对相关查询执行 invalidateQueries;服务端错误应统一交给 handleServerError
Axios 请求应使用项目统一的 api 实例;GET 请求默认去重,特殊请求可显式关闭;认证与通用错误应在拦截器中统一处理。
服务端错误应统一使用 handleServerError,展示层应使用 toast.error 等统一方式;文案需走 i18n;路由级错误应由 errorComponent 承接;表单错误应通过 form.setError 等方式映射到字段。
样式应以 Tailwind 工具类为主,动态类名使用 cn() 合并;非动态场景避免内联样式;响应式采用移动优先与 Tailwind 断点,主题与暗色模式通过 CSS 变量与 dark: 处理。
应使用语义化 HTML、正确关联 label 与输入、保证键盘可操作与合理焦点顺序;必要时添加 ARIA 属性,装饰性图标应使用 aria-hidden="true"
认证与权限应在路由与接口层校验;前后端都应做数据校验(如 Zod);敏感信息不得落前端存储;避免使用 dangerouslySetInnerHTML;跨域与 Cookie 需配合 withCredentials 并按后端要求处理 CSRF。
组件测试应使用 React Testing Library,关注交互与行为,避免测试实现细节;关键流程可补充集成与 E2E 测试。
环境变量应通过 .env 读取,并使用 VITE_ 前缀;代码中不得硬编码密钥。

Files:

  • web/default/src/features/channels/types.ts
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/channels/components/data-table-row-actions.tsx
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
web/default/src/features/**

📄 CodeRabbit inference engine (web/default/AGENTS.md)

功能模块应放在 src/features/<feature>/,并按需包含 components/lib/hooks/api.tstypes.tsconstants.ts 等;通用组件应放在 src/components/,通用工具与类型应放在 src/lib/

Files:

  • web/default/src/features/channels/types.ts
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/channels/components/data-table-row-actions.tsx
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
web/default/src/features/**/lib/**/*.ts

📄 CodeRabbit inference engine (web/default/AGENTS.md)

表单应使用 React Hook Form + Zod:在功能模块的 lib/ 下定义 schema,并用 z.infer 导出表单类型;useForm 应配合 @hookform/resolvers/zod 进行校验。

Files:

  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/channels/lib/channel-actions.ts
web/default/src/**/*.test.ts

📄 CodeRabbit inference engine (web/default/AGENTS.md)

工具函数与纯逻辑应优先编写单元测试;测试文件应命名为 *.test.ts

Files:

  • web/default/src/features/channels/lib/channel-actions.test.ts
🔇 Additional comments (12)
web/default/src/features/channels/lib/channel-actions.ts (2)

60-183: LGTM!


342-411: LGTM!

web/default/src/features/channels/types.ts (1)

191-191: LGTM!

web/default/src/features/channels/components/data-table-row-actions.tsx (1)

56-62: LGTM!

Also applies to: 94-112

web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx (1)

348-418: LGTM!

Also applies to: 438-506

web/default/src/i18n/locales/en.json (1)

67-68: LGTM!

Also applies to: 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/i18n/locales/fr.json (1)

67-68: LGTM!

Also applies to: 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/features/channels/lib/channel-actions.test.ts (1)

19-25: 📐 Maintainability & Code Quality

Wire node:test into the workspace test command
I couldn’t find a test script or workflow that runs these suites, so these node:test files may not execute automatically. Add a test command/CI job for them.

web/default/src/i18n/locales/ja.json (1)

14-15: LGTM!

Also applies to: 67-68, 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/i18n/locales/ru.json (1)

68-68: LGTM!

Also applies to: 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/i18n/locales/vi.json (1)

14-15: LGTM!

Also applies to: 67-68, 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/i18n/locales/zh.json (1)

14-15: LGTM!

Also applies to: 67-68, 84-86, 801-802, 2701-2701, 3895-3895

Comment thread web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx Outdated
Comment thread web/default/src/i18n/locales/ru.json Outdated
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx (1)

361-408: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Ignore results from tests belonging to a closed or replaced dialog.

Closing the dialog only resets state; active requests continue and can update testResults, progress, and selection after another channel is opened. Add an operation-generation guard/abort signal, or prevent closing until testing finishes.

Also applies to: 421-514

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx`
around lines 361 - 408, Update the dialog test lifecycle around testSingleModel
and the related test handlers to invalidate or abort in-flight operations when
the dialog closes or the tested channel is replaced. Guard all asynchronous
callbacks and completion updates, including testResults, progress, selection,
and refreshChannelLists, so results from stale generations are ignored while
current-dialog tests continue to work.
setting/config/config.go (1)

203-269: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Configuration updates are not atomic. A later validation error leaves earlier fields modified despite LoadFromDB returning failure.

  • setting/config/config.go#L203-L269: apply changes to a copied struct and publish it only after all fields validate.
  • setting/config/config_test.go#L101-L113: add a valid earlier field plus an invalid later field and assert the whole config remains unchanged.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@setting/config/config.go` around lines 203 - 269, Make LoadFromDB apply all
decoded field updates to a copy of the configuration, using the existing
reflection switch for validation, and publish the copied struct only after every
field succeeds; leave the original unchanged on any error. In
setting/config/config_test.go:101-113, add a valid earlier update followed by an
invalid later value and assert that LoadFromDB fails and the entire
configuration, including the earlier field, remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@common/redis.go`:
- Around line 256-265: Add lifecycle cleanup for persistent version keys used by
RedisInvalidateVersionedHash, deleting the corresponding versionKey whenever the
associated user or token entity is removed. Do not apply a normal TTL to
versionKey, since generation resets could permit stale cache refills, and ensure
the cleanup path targets both the cached hash key and its version key.

In `@model/checkin.go`:
- Around line 116-117: Update the post-transaction cache invalidation call in
the check-in flow to handle failures from invalidateUserQuotaCache(userId)
instead of discarding them. After the database commit, log the invalidation
error and enqueue a retry, without returning the error to the caller or
triggering another check-in attempt.

In `@model/token_cache.go`:
- Around line 31-32: Update RedisInvalidateVersionedHash, used by
invalidateTokenCache, to assign an expiration to each incremented cache-version
key; choose a TTL that exceeds the asynchronous refill/cache-fence window and
apply it consistently in the shared helper so invalidated token version keys can
expire.

In `@model/usedata.go`:
- Around line 117-128: The quota snapshot retry flow around saveQuotaData must
persist a stable idempotency identifier and transactionally deduplicate
snapshots before applying counters, preventing ambiguous commits from
incrementing totals twice; add the corresponding cross-database constraint and
migration compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+. In
model/usedata.go lines 117-128, update the failed-snapshot requeue and save path
to retain and reuse this identifier. In model/usedata_test.go lines 12-48, add
coverage for an ambiguous commit followed by a repeated snapshot and assert
counters are applied exactly once.

In `@model/user_cache.go`:
- Around line 71-82: Update the direct HSET mutation paths for user group,
status, role, name, and setting caches to participate in the same version fence
as populateUserCacheIfVersion. Reuse the existing versioned cache helper and
appropriate user cache version key, or explicitly advance that generation on
every mutation, ensuring stale DB refills cannot overwrite newer auth-sensitive
updates.

In `@model/user.go`:
- Line 1274: Update the OIDC identity existence check in the surrounding user
lookup to return true when RowsAffected is greater than zero, so any matching
record is treated as already taken rather than requiring exactly one row.

In `@service/billing_session.go`:
- Around line 92-107: Update the compensation handling in the billing settlement
flow so a partial result from funding.Settle records the residual committed
funding amount instead of retaining the original appliedFundingDelta. Before a
later token-only retry, reconcile or reapply that missing funding portion before
settling the token, while preserving compensationFailed state and existing
full-compensation behavior. Add a test covering a nonzero partial compensation
result and verifying the retry uses the corrected funding amount.

In `@service/pre_consume_quota.go`:
- Around line 76-79: Update the rollback handling around IncreaseTokenQuota in
the pre-consume failure path so a failed compensation is not only logged: make
the token deduction and quota restoration transactional, or persist a durable
retryable compensation record containing the token and amount details. Ensure
the failed request cannot leave the user’s token balance permanently reduced
without a recovery path.

---

Outside diff comments:
In `@setting/config/config.go`:
- Around line 203-269: Make LoadFromDB apply all decoded field updates to a copy
of the configuration, using the existing reflection switch for validation, and
publish the copied struct only after every field succeeds; leave the original
unchanged on any error. In setting/config/config_test.go:101-113, add a valid
earlier update followed by an invalid later value and assert that LoadFromDB
fails and the entire configuration, including the earlier field, remains
unchanged.

In
`@web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx`:
- Around line 361-408: Update the dialog test lifecycle around testSingleModel
and the related test handlers to invalidate or abort in-flight operations when
the dialog closes or the tested channel is replaced. Guard all asynchronous
callbacks and completion updates, including testResults, progress, selection,
and refreshChannelLists, so results from stale generations are ignored while
current-dialog tests continue to work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9bbc065d-de7f-420b-b5ea-3e15b313d12d

📥 Commits

Reviewing files that changed from the base of the PR and between 6b6e051 and 8ae50e9.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • web/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (77)
  • common/redis.go
  • controller/discord.go
  • controller/github.go
  • controller/linuxdo.go
  • controller/oauth.go
  • controller/oauth_test.go
  • controller/oidc.go
  • controller/telegram.go
  • controller/token.go
  • controller/wechat.go
  • go.mod
  • middleware/turnstile-check.go
  • middleware/turnstile_check_test.go
  • model/checkin.go
  • model/errors.go
  • model/main.go
  • model/main_migration_test.go
  • model/redemption.go
  • model/redemption_search_test.go
  • model/subscription.go
  • model/token.go
  • model/token_cache.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_cache.go
  • model/user_oauth_binding.go
  • model/user_update_test.go
  • model/utils.go
  • pkg/billingexpr/compile.go
  • pkg/billingexpr/expr.md
  • pkg/billingexpr/run.go
  • pkg/billingexpr/security_cache_test.go
  • service/billing_session.go
  • service/billing_session_test.go
  • service/funding_source.go
  • service/pre_consume_quota.go
  • service/quota.go
  • service/task_billing_test.go
  • setting/config/config.go
  • setting/config/config_test.go
  • tools/jsonwrapcheck/allowlist.txt
  • web/default/package.json
  • web/default/src/components/layout/components/chat-presets-item.tsx
  • web/default/src/components/layout/components/footer.tsx
  • web/default/src/features/auth/api.ts
  • web/default/src/features/auth/lib/turnstile-request.test.ts
  • web/default/src/features/auth/lib/turnstile-request.ts
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/keys/components/data-table-row-actions.tsx
  • web/default/src/features/keys/components/dialogs/cc-switch-dialog.tsx
  • web/default/src/features/models/components/dialogs/view-details-dialog.tsx
  • web/default/src/features/playground/components/message-error.tsx
  • web/default/src/features/playground/hooks/use-stream-request.test.ts
  • web/default/src/features/playground/hooks/use-stream-request.ts
  • web/default/src/features/pricing/lib/tier-expr.test.ts
  • web/default/src/features/pricing/lib/tier-expr.ts
  • web/default/src/features/profile/api.ts
  • web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx
  • web/default/src/features/system-settings/integrations/ionet-deployment-settings-section.tsx
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/system-settings/models/tiered-pricing-support.tsx
  • web/default/src/features/usage-logs/components/dialogs/audio-preview-dialog.tsx
  • web/default/src/features/wallet/hooks/use-creem-payment.ts
  • web/default/src/features/wallet/hooks/use-payment.ts
  • web/default/src/features/wallet/hooks/use-waffo-payment.ts
  • web/default/src/i18n/locales/ru.json
  • web/default/src/lib/handle-server-error.test.ts
  • web/default/src/lib/handle-server-error.ts
  • web/default/src/lib/oauth.ts
  • web/default/src/lib/safe-redirect.test.ts
  • web/default/src/lib/safe-redirect.ts
  • web/default/src/routes/__root.tsx
  • web/default/src/routes/oauth/$provider.tsx
💤 Files with no reviewable changes (1)
  • tools/jsonwrapcheck/allowlist.txt
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (8)
web/default/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/default/AGENTS.md)

web/default/src/**/*.{ts,tsx}: 前端页面文本与组件内文案必须支持 i18n:React 组件中应使用 useTranslation() 取得 t,并通过 t() 渲染用户可见文本;子组件也应自行使用 useTranslation() 保持独立性。
禁止使用两层及以上嵌套三元表达式;复杂逻辑应改用 if-else、提前返回或抽取函数。
控制函数圈复杂度,复杂逻辑应拆成更小的函数;变量与函数命名应有意义并遵循驼峰等常规命名约定。
TypeScript 代码应避免使用 any,优先使用具体类型或 unknown;参数与返回值应显式标注类型;仅类型用途的导入应使用 import type
修改 TypeScript 或 TSX 代码后必须执行类型检查,并修复所有类型错误,不得遗留。
对象非必要不要解构,尤其是组件 props;优先直接使用 props.xxx 以保持代码清晰。
组件应使用函数式组件与 Hooks,遵循单一职责;组件 props 必须有明确类型(接口或类型别名)。
单文件超过约 200 行时应考虑拆分子组件或抽取自定义 Hooks;类型定义可与组件同文件或放在同模块的 types 中。
在 React 中应合理使用 useMemouseCallbackReact.memo,避免在渲染路径中创建新对象或数组;必要时进行代码分割与动态 import
React Query 的数据获取应使用 useQuery、变更应使用 useMutation;每个查询需配置唯一 queryKey,并在成功后对相关查询执行 invalidateQueries;服务端错误应统一交给 handleServerError
Axios 请求应使用项目统一的 api 实例;GET 请求默认去重,特殊请求可显式关闭;认证与通用错误应在拦截器中统一处理。
服务端错误应统一使用 handleServerError,展示层应使用 toast.error 等统一方式;文案需走 i18n;路由级错误应由 errorComponent 承接;表单错误应通过 form.setError 等方式映射到字段。
样式应以 Tailwind 工具类为主,动态类名使用 cn() 合并;非动态场景避免内联样式;响应式采用移动优先与 Tailwind 断点,主题与暗色模式通过 CSS 变量与 dark: 处理。
应使用语义化 HTML、正确关联 label 与输入、保证键盘可操作与合理焦点顺序;必要时添加 ARIA 属性,装饰性图标应使用 aria-hidden="true"
认证与权限应在路由与接口层校验;前后端都应做数据校验(如 Zod);敏感信息不得落前端存储;避免使用 dangerouslySetInnerHTML;跨域与 Cookie 需配合 withCredentials 并按后端要求处理 CSRF。
组件测试应使用 React Testing Library,关注交互与行为,避免测试实现细节;关键流程可补充集成与 E2E 测试。
环境变量应通过 .env 读取,并使用 VITE_ 前缀;代码中不得硬编码密钥。

Files:

  • web/default/src/features/wallet/hooks/use-waffo-payment.ts
  • web/default/src/components/layout/components/chat-presets-item.tsx
  • web/default/src/features/keys/components/dialogs/cc-switch-dialog.tsx
  • web/default/src/features/playground/hooks/use-stream-request.test.ts
  • web/default/src/features/wallet/hooks/use-creem-payment.ts
  • web/default/src/lib/safe-redirect.test.ts
  • web/default/src/features/playground/components/message-error.tsx
  • web/default/src/features/auth/lib/turnstile-request.test.ts
  • web/default/src/features/keys/components/data-table-row-actions.tsx
  • web/default/src/lib/handle-server-error.test.ts
  • web/default/src/features/system-settings/integrations/ionet-deployment-settings-section.tsx
  • web/default/src/features/pricing/lib/tier-expr.test.ts
  • web/default/src/features/auth/lib/turnstile-request.ts
  • web/default/src/lib/safe-redirect.ts
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/models/components/dialogs/view-details-dialog.tsx
  • web/default/src/features/wallet/hooks/use-payment.ts
  • web/default/src/features/usage-logs/components/dialogs/audio-preview-dialog.tsx
  • web/default/src/features/system-settings/models/tiered-pricing-support.tsx
  • web/default/src/components/layout/components/footer.tsx
  • web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/profile/api.ts
  • web/default/src/lib/handle-server-error.ts
  • web/default/src/features/auth/api.ts
  • web/default/src/lib/oauth.ts
  • web/default/src/routes/oauth/$provider.tsx
  • web/default/src/routes/__root.tsx
  • web/default/src/features/playground/hooks/use-stream-request.ts
  • web/default/src/features/pricing/lib/tier-expr.ts
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
web/default/src/features/**

📄 CodeRabbit inference engine (web/default/AGENTS.md)

功能模块应放在 src/features/<feature>/,并按需包含 components/lib/hooks/api.tstypes.tsconstants.ts 等;通用组件应放在 src/components/,通用工具与类型应放在 src/lib/

Files:

  • web/default/src/features/wallet/hooks/use-waffo-payment.ts
  • web/default/src/features/keys/components/dialogs/cc-switch-dialog.tsx
  • web/default/src/features/playground/hooks/use-stream-request.test.ts
  • web/default/src/features/wallet/hooks/use-creem-payment.ts
  • web/default/src/features/playground/components/message-error.tsx
  • web/default/src/features/auth/lib/turnstile-request.test.ts
  • web/default/src/features/keys/components/data-table-row-actions.tsx
  • web/default/src/features/system-settings/integrations/ionet-deployment-settings-section.tsx
  • web/default/src/features/pricing/lib/tier-expr.test.ts
  • web/default/src/features/auth/lib/turnstile-request.ts
  • web/default/src/features/channels/components/channels-columns.tsx
  • web/default/src/features/models/components/dialogs/view-details-dialog.tsx
  • web/default/src/features/wallet/hooks/use-payment.ts
  • web/default/src/features/usage-logs/components/dialogs/audio-preview-dialog.tsx
  • web/default/src/features/system-settings/models/tiered-pricing-support.tsx
  • web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/profile/api.ts
  • web/default/src/features/auth/api.ts
  • web/default/src/features/playground/hooks/use-stream-request.ts
  • web/default/src/features/pricing/lib/tier-expr.ts
  • web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx
  • web/default/src/features/channels/lib/channel-actions.ts
  • web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx
web/default/src/**/*.test.ts

📄 CodeRabbit inference engine (web/default/AGENTS.md)

工具函数与纯逻辑应优先编写单元测试;测试文件应命名为 *.test.ts

Files:

  • web/default/src/features/playground/hooks/use-stream-request.test.ts
  • web/default/src/lib/safe-redirect.test.ts
  • web/default/src/features/auth/lib/turnstile-request.test.ts
  • web/default/src/lib/handle-server-error.test.ts
  • web/default/src/features/pricing/lib/tier-expr.test.ts
  • web/default/src/features/channels/lib/channel-actions.test.ts
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • middleware/turnstile_check_test.go
  • model/redemption_search_test.go
  • model/user_oauth_binding.go
  • model/usedata_test.go
  • model/main_migration_test.go
  • model/checkin.go
  • controller/token.go
  • controller/telegram.go
  • setting/config/config_test.go
  • model/utils.go
  • controller/linuxdo.go
  • model/redemption.go
  • service/quota.go
  • pkg/billingexpr/security_cache_test.go
  • controller/oidc.go
  • controller/wechat.go
  • middleware/turnstile-check.go
  • controller/oauth_test.go
  • service/pre_consume_quota.go
  • controller/github.go
  • pkg/billingexpr/run.go
  • service/task_billing_test.go
  • pkg/billingexpr/compile.go
  • model/subscription.go
  • controller/oauth.go
  • controller/discord.go
  • model/main.go
  • model/token_cache.go
  • model/errors.go
  • service/billing_session_test.go
  • model/user_cache.go
  • model/usedata.go
  • common/redis.go
  • setting/config/config.go
  • model/token.go
  • service/funding_source.go
  • model/user_update_test.go
  • model/user.go
  • service/billing_session.go
web/default/src/features/**/lib/**/*.ts

📄 CodeRabbit inference engine (web/default/AGENTS.md)

表单应使用 React Hook Form + Zod:在功能模块的 lib/ 下定义 schema,并用 z.infer 导出表单类型;useForm 应配合 @hookform/resolvers/zod 进行校验。

Files:

  • web/default/src/features/auth/lib/turnstile-request.test.ts
  • web/default/src/features/pricing/lib/tier-expr.test.ts
  • web/default/src/features/auth/lib/turnstile-request.ts
  • web/default/src/features/channels/lib/channel-actions.test.ts
  • web/default/src/features/pricing/lib/tier-expr.ts
  • web/default/src/features/channels/lib/channel-actions.ts
web/default/**/package.json

📄 CodeRabbit inference engine (web/default/AGENTS.md)

脚本与包管理以 package.json 为准;发布前应执行 typecheck、lint、format、生产构建,并检查产物体积与环境变量配置。

Files:

  • web/default/package.json
pkg/billingexpr/**/*.{go,md}

📄 CodeRabbit inference engine (AGENTS.md)

When working on tiered/dynamic billing expression pricing, first read pkg/billingexpr/expr.md and ensure all code changes in the billing expression system follow the documented design, architecture, token normalization, quota conversion, and versioning patterns.

Files:

  • pkg/billingexpr/security_cache_test.go
  • pkg/billingexpr/expr.md
  • pkg/billingexpr/run.go
  • pkg/billingexpr/compile.go
web/default/src/routes/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/default/AGENTS.md)

路由应使用 TanStack Router,并通过 createFileRoute 定义;搜索参数应使用 Zod schema + validateSearch 校验;认证与重定向应放在 beforeLoad 中;导航应优先使用 useNavigateLink,避免直接操作 window.location

Files:

  • web/default/src/routes/oauth/$provider.tsx
  • web/default/src/routes/__root.tsx
🔇 Additional comments (71)
web/default/src/features/playground/hooks/use-stream-request.test.ts (1)

1-32: LGTM!

web/default/src/features/pricing/lib/tier-expr.test.ts (1)

1-58: LGTM!

web/default/src/features/system-settings/models/tiered-pricing-support.tsx (1)

458-464: LGTM!

go.mod (1)

64-69: LGTM!

web/default/src/features/channels/components/dialogs/channel-test-dialog.tsx (1)

98-117: LGTM!

Also applies to: 245-359, 995-999

web/default/src/features/channels/lib/channel-actions.test.ts (1)

1-93: LGTM!

web/default/src/features/channels/lib/channel-actions.ts (1)

40-218: LGTM!

Also applies to: 379-441

web/default/src/i18n/locales/ru.json (1)

67-68: LGTM!

Also applies to: 84-86, 801-802, 2701-2701, 3895-3895

web/default/src/features/profile/api.ts (1)

20-20: LGTM!

Also applies to: 107-116, 187-194

pkg/billingexpr/expr.md (1)

79-105: LGTM!

pkg/billingexpr/run.go (1)

72-88: LGTM!

Also applies to: 118-158

pkg/billingexpr/security_cache_test.go (1)

1-59: LGTM!

web/default/src/features/pricing/lib/tier-expr.ts (1)

19-19: LGTM!

Also applies to: 272-321, 357-361

web/default/src/features/system-settings/models/model-ratio-visual-editor.tsx (1)

131-132: LGTM!

Also applies to: 234-248, 488-503, 560-566, 577-577

web/default/src/lib/handle-server-error.test.ts (1)

1-41: LGTM!

web/default/package.json (1)

44-44: LGTM!

middleware/turnstile-check.go (1)

17-25: LGTM!

Also applies to: 35-35, 60-60

middleware/turnstile_check_test.go (1)

1-37: LGTM!

web/default/src/features/auth/api.ts (1)

20-20: LGTM!

Also applies to: 39-47, 72-75, 84-87, 109-114, 123-126

web/default/src/features/auth/lib/turnstile-request.test.ts (1)

1-37: LGTM!

web/default/src/features/auth/lib/turnstile-request.ts (1)

1-26: LGTM!

web/default/src/lib/oauth.ts (1)

21-67: LGTM!

Also applies to: 154-154, 165-165, 179-179, 190-190

web/default/src/routes/oauth/$provider.tsx (1)

31-32: LGTM!

Also applies to: 52-61, 63-77, 89-99

web/default/src/components/layout/components/footer.tsx (1)

26-26: LGTM!

Also applies to: 236-238

web/default/src/components/layout/components/chat-presets-item.tsx (1)

213-213: LGTM!

web/default/src/features/channels/components/channels-columns.tsx (1)

660-667: LGTM!

web/default/src/features/keys/components/data-table-row-actions.tsx (1)

150-150: LGTM!

web/default/src/features/keys/components/dialogs/cc-switch-dialog.tsx (1)

149-149: LGTM!

web/default/src/features/models/components/dialogs/view-details-dialog.tsx (1)

245-251: LGTM!

web/default/src/features/playground/components/message-error.tsx (1)

60-64: LGTM!

web/default/src/lib/safe-redirect.test.ts (1)

19-42: LGTM!

web/default/src/routes/__root.tsx (1)

63-65: LGTM!

Also applies to: 76-77, 90-93

web/default/src/features/usage-logs/components/dialogs/audio-preview-dialog.tsx (1)

117-119: LGTM!

web/default/src/features/wallet/hooks/use-creem-payment.ts (1)

39-39: LGTM!

web/default/src/features/wallet/hooks/use-payment.ts (1)

104-108: LGTM!

web/default/src/features/wallet/hooks/use-waffo-payment.ts (1)

64-64: LGTM!

web/default/src/features/subscriptions/components/dialogs/subscription-purchase-dialog.tsx (1)

125-125: LGTM!

Also applies to: 147-147, 359-362

web/default/src/features/system-settings/integrations/ionet-deployment-settings-section.tsx (1)

224-228: LGTM!

web/default/src/lib/safe-redirect.ts (1)

20-38: 🔒 Security & Privacy

Backslash-prefixed targets already fall back to /dashboard. new URL('/\\evil.example/path', redirectBase) resolves to https://evil.example, so the parsed.origin !== redirectBase check rejects it.

			> Likely an incorrect or invalid review comment.
common/redis.go (1)

84-254: LGTM!

model/user_cache.go (1)

53-62: LGTM!

Also applies to: 119-148

model/utils.go (1)

85-85: LGTM!

controller/telegram.go (1)

95-97: LGTM!

model/errors.go (1)

7-12: LGTM!

Also applies to: 17-21, 26-27, 31-31, 34-34

setting/config/config.go (1)

4-4: LGTM!

Also applies to: 42-62, 93-161, 165-202

model/usedata.go (1)

4-4: LGTM!

Also applies to: 54-54, 103-116, 129-157

model/redemption_search_test.go (1)

1-20: LGTM!

pkg/billingexpr/compile.go (1)

36-38: LGTM!

Also applies to: 105-115, 182-182

model/token_cache.go (1)

10-29: LGTM!

Also applies to: 46-50

model/token.go (1)

23-28: LGTM!

Also applies to: 285-299, 310-339, 385-461, 497-497, 535-535

model/subscription.go (1)

838-839: LGTM!

model/main.go (1)

285-287: LGTM!

Also applies to: 344-346, 680-756

model/main_migration_test.go (1)

5-20: LGTM!

controller/token.go (1)

188-188: LGTM!

Also applies to: 272-272

service/quota.go (1)

146-146: LGTM!

Also applies to: 417-417

service/task_billing_test.go (1)

100-100: LGTM!

Also applies to: 168-175

service/billing_session.go (1)

8-8: LGTM!

Also applies to: 27-80, 112-117, 335-336, 348-348, 428-430

service/billing_session_test.go (1)

1-166: LGTM!

service/funding_source.go (1)

70-102: LGTM!

service/pre_consume_quota.go (1)

55-56: LGTM!

model/user.go (1)

582-593: LGTM!

Also applies to: 1160-1174, 1189-1234, 1358-1382, 1467-1535, 1665-1665

model/user_update_test.go (1)

12-19: LGTM!

Also applies to: 28-146, 148-203, 205-326

controller/oauth.go (1)

38-49: LGTM!

Also applies to: 245-247, 262-264

controller/oauth_test.go (1)

4-29: LGTM!

Also applies to: 54-64

controller/discord.go (1)

135-135: LGTM!

controller/github.go (1)

115-120: LGTM!

controller/linuxdo.go (1)

212-212: LGTM!

controller/oidc.go (1)

137-137: LGTM!

controller/wechat.go (1)

78-78: LGTM!

model/user_oauth_binding.go (1)

51-60: LGTM!

model/redemption.go (1)

80-84: LGTM!

Comment thread common/redis.go
Comment thread model/checkin.go Outdated
Comment thread model/token_cache.go
Comment on lines +31 to +32
func invalidateTokenCache(key string) error {
return common.RedisInvalidateVersionedHash(getTokenCacheKey(key), getTokenCacheVersionKey(key))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Expire version keys after the cache-fence window.

RedisInvalidateVersionedHash increments cache-version:token:* without assigning an expiration. Every invalidated or deleted token therefore leaves a permanent Redis key, causing unbounded growth. Set a TTL long enough to outlive pending asynchronous refills, preferably in the shared helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/token_cache.go` around lines 31 - 32, Update
RedisInvalidateVersionedHash, used by invalidateTokenCache, to assign an
expiration to each incremented cache-version key; choose a TTL that exceeds the
asynchronous refill/cache-fence window and apply it consistently in the shared
helper so invalidated token version keys can expire.

Comment thread model/usedata.go Outdated
Comment thread model/user_cache.go
Comment thread model/user.go Outdated
Comment thread service/billing_session.go
Comment thread service/pre_consume_quota.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
model/user.go (1)

1089-1094: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not expose a Redis failure as a failed database deletion.

The database deletion is already committed when deleteUserCache fails. Returning that error gives callers an ambiguous result while stale authorization data remains cached. Retry cache deletion asynchronously and report the committed database outcome consistently.

Also applies to: 1104-1107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/user.go` around lines 1089 - 1094, Update the user deletion flow around
DB.Delete and deleteUserCache so a cache-deletion failure does not cause the
committed database deletion to be returned as failed. Trigger deletion-cache
retry asynchronously when deleteUserCache fails, while returning the successful
database outcome consistently; apply the same behavior to the additional
affected deletion path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/subscription.go`:
- Around line 838-842: Update model/subscription.go at lines 838-842 to use
group-aware invalidateUserCache when upgradeGroup is non-empty, while retaining
quota-only invalidation otherwise, and enqueue failed invalidations for retry
instead of only logging them. Update model/token.go at lines 472-475 to enqueue
invalidateUserQuotaCache failures after the committed deduction, preserving the
existing successful invalidation path.

In `@model/token.go`:
- Around line 332-338: Ensure failed deleteTokenCache operations for revoked
tokens are persisted or enqueued for retry instead of only logged, so cache
invalidation occurs after Redis recovers. Apply this to both Token.Delete and
the batch deletion flow at model/token.go lines 332-338 and 540-545; preserve
the existing database deletion behavior and handle each cache-deletion failure
consistently.

In `@model/usedata.go`:
- Around line 188-201: Replace the First-then-Create flow in the quota-data
transaction with a portable unique aggregate key covering the bucket fields and
a single atomic upsert that increments the existing aggregate. Update the
relevant quota-data model/schema and the increaseQuotaDataTx path to use
conflict-safe SQL compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+,
preserving creation when no matching aggregate exists.

In `@model/user.go`:
- Around line 1498-1500: Update both quota mutation paths around
invalidateUserQuotaCache so that, when cache invalidation fails after a
successful quota decrement, they call enqueueUserCacheInvalidationRetry with the
affected user identifier. Keep the existing error logging and successful
mutation response unchanged, and ensure both failure branches schedule a retry.
- Around line 1261-1278: The OAuth ID lookup helpers IsWeChatIdAlreadyTaken,
IsGitHubIdAlreadyTaken, IsDiscordIdAlreadyTaken, IsOidcIdAlreadyTaken, and
IsTelegramIdAlreadyTaken must return both the taken status and the database
error from Find(...).Error. Update every call site to handle and propagate the
error so binding or registration does not continue when the uniqueness lookup
fails.

In `@service/billing_session.go`:
- Around line 80-96: The compensationFailed flag remains set after successful
funding reconciliation, causing later token-step failures to skip compensation.
In the compensationFailed branch around funding.Settle, clear
s.compensationFailed once s.appliedFundingDelta reaches delta, before retrying
the token adjustment, while preserving the existing incomplete-reconciliation
errors.

In `@service/pre_consume_quota.go`:
- Around line 80-85: Update the error handling after DecreaseTokenAndUserQuota
in the pre-consumption flow to treat model.ErrUserQuotaInsufficient the same as
model.ErrTokenQuotaInsufficient. Return the existing pre-consume quota failure
response with Forbidden status and its current retry/logging options, while
preserving the generic update error for other failures.

---

Outside diff comments:
In `@model/user.go`:
- Around line 1089-1094: Update the user deletion flow around DB.Delete and
deleteUserCache so a cache-deletion failure does not cause the committed
database deletion to be returned as failed. Trigger deletion-cache retry
asynchronously when deleteUserCache fails, while returning the successful
database outcome consistently; apply the same behavior to the additional
affected deletion path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e0a1867c-c4fc-4a18-94e9-09200791e9fb

📥 Commits

Reviewing files that changed from the base of the PR and between 8ae50e9 and 4beaba5.

📒 Files selected for processing (17)
  • common/redis.go
  • common/redis_version_test.go
  • model/checkin.go
  • model/log_test.go
  • model/main.go
  • model/option.go
  • model/subscription.go
  • model/token.go
  • model/token_cache.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_cache.go
  • model/user_update_test.go
  • service/billing_session.go
  • service/billing_session_test.go
  • service/pre_consume_quota.go
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Backend checks
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • common/redis_version_test.go
  • model/checkin.go
  • model/subscription.go
  • model/option.go
  • model/log_test.go
  • service/pre_consume_quota.go
  • model/usedata_test.go
  • model/token_cache.go
  • model/main.go
  • service/billing_session_test.go
  • model/usedata.go
  • model/token.go
  • model/user_cache.go
  • service/billing_session.go
  • common/redis.go
  • model/user_update_test.go
  • model/user.go
🔇 Additional comments (17)
common/redis.go (1)

84-119: LGTM!

Also applies to: 231-237, 262-265, 297-314

common/redis_version_test.go (1)

1-49: LGTM!

model/option.go (1)

704-709: LGTM!

model/token_cache.go (1)

35-40: LGTM!

model/usedata.go (1)

10-117: LGTM!

Also applies to: 141-186, 205-213

model/usedata_test.go (1)

37-79: LGTM!

model/user.go (1)

140-156: LGTM!

Also applies to: 1398-1478, 1551-1555, 1648-1682

model/user_cache.go (1)

65-86: LGTM!

Also applies to: 111-117, 224-232, 250-309

service/billing_session.go (1)

26-43: LGTM!

Also applies to: 132-138

model/checkin.go (1)

5-5: LGTM!

Also applies to: 117-121

model/log_test.go (1)

861-861: LGTM!

model/main.go (2)

285-301: LGTM!

Also applies to: 345-366


683-698: 🗄️ Data Integrity & Integration

Bigint migration covers MySQL/PostgreSQL and skips SQLite.

model/token.go (1)

23-28: LGTM!

Also applies to: 433-471

model/user_update_test.go (1)

98-114: LGTM!

Also applies to: 223-229, 329-348

service/billing_session_test.go (1)

97-132: LGTM!

service/pre_consume_quota.go (1)

4-4: LGTM!

Also applies to: 56-57, 71-79

Comment thread model/subscription.go
Comment on lines +838 to 842
if chargedQuota > 0 || upgradeGroup != "" {
if cacheErr := invalidateUserQuotaCache(userId); cacheErr != nil {
common.SysLog("failed to invalidate user quota cache after subscription purchase: " + cacheErr.Error())
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make committed user-cache invalidation complete and retryable.

  • model/subscription.go#L838-L842: use the group-aware invalidateUserCache when upgradeGroup != ""; quota-only invalidation cannot evict a cached old group. Queue failed invalidations for retry.
  • model/token.go#L472-L475: enqueue invalidateUserQuotaCache failures after the committed deduction instead of only logging them.

Otherwise stale quota can permit excess consumption, while stale groups can delay or incorrectly preserve access.

📍 Affects 2 files
  • model/subscription.go#L838-L842 (this comment)
  • model/token.go#L472-L475
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/subscription.go` around lines 838 - 842, Update model/subscription.go
at lines 838-842 to use group-aware invalidateUserCache when upgradeGroup is
non-empty, while retaining quota-only invalidation otherwise, and enqueue failed
invalidations for retry instead of only logging them. Update model/token.go at
lines 472-475 to enqueue invalidateUserQuotaCache failures after the committed
deduction, preserving the existing successful invalidation path.

Comment thread model/token.go
Comment thread model/usedata.go Outdated
Comment thread model/user.go Outdated
Comment thread model/user.go
Comment thread service/billing_session.go Outdated
Comment thread service/pre_consume_quota.go
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
controller/oauth.go (1)

38-48: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Prevent leakage of internal database errors.

Directly exposing err.Error() in the JSON response can leak sensitive internal details (e.g., database connection strings, SQL syntax errors) if the underlying lookup fails at the database layer. This contradicts the security guarantees verified by TestOAuthBindMasksUniquenessLookupDatabaseError.

Log the raw error internally and return a generic error message to the client, similar to how handleOAuthIdentityLookupError masks internal errors.

🔒️ Proposed fix to mask internal errors
 func handleOAuthUserLookupError(c *gin.Context, err error) bool {
 	if err == nil {
 		return false
 	}
 	if errors.Is(err, model.ErrUserDeleted) {
 		c.JSON(http.StatusOK, gin.H{"success": false, "message": "用户已注销"})
 	} else {
+		common.SysError(fmt.Sprintf("OAuth user lookup failed: %v", err))
-		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
+		c.JSON(http.StatusOK, gin.H{"success": false, "message": "获取用户信息失败,请稍后重试"})
 	}
 	return true
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/oauth.go` around lines 38 - 48, Update handleOAuthUserLookupError
so only the ErrUserDeleted case returns its existing user-facing message; for
all other errors, log the raw error internally and return a generic client-safe
error message instead of err.Error(), matching the masking behavior of
handleOAuthIdentityLookupError.
service/billing_session.go (2)

74-82: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Handle partial initial funding application.

FundingSource.Settle returns the amount actually applied, but this path marks funding settled even when applied != delta and proceeds with the full token adjustment. Record reconciliation-pending state and stop before the token step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/billing_session.go` around lines 74 - 82, Update the
!s.fundingSettled branch around FundingSource.Settle to compare applied with
delta; when they differ, retain the applied amount, mark
fundingReconcilePending, and return before applying the token adjustment. Only
set fundingSettled and clear reconciliation state when the full delta is
applied, preserving compensationFailed handling.

46-63: 🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift

Make settlement mutations idempotent before retrying or accepting ambiguous outcomes. The current state machine can finalize token usage without matching funding or apply a token delta twice.

  • service/billing_session.go#L46-L63: guard retries with a durable settlement operation ID.
  • service/billing_session.go#L83-L94: keep ambiguous reconciliation unresolved and block token adjustment.
  • service/billing_session.go#L123-L131: do not assume failed compensation left the original funding delta committed.
  • service/billing_session_test.go#L103-L130: cover compensation-committed and compensation-not-committed outcomes.
  • service/billing_session_test.go#L258-L274: require exactly-once behavior under an ambiguous token commit before asserting retries.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/billing_session.go` around lines 46 - 63, Make settlement mutations
idempotent across retries and ambiguous outcomes: update
service/billing_session.go lines 46-63 in BillingSession.Settle to create and
reuse a durable settlement operation ID; keep ambiguous reconciliation
unresolved and prevent token adjustment at lines 83-94; at lines 123-131, only
treat the original funding delta as committed when compensation confirms it,
rather than assuming failed compensation succeeded. Extend
service/billing_session_test.go lines 103-130 to cover compensation-committed
and compensation-not-committed outcomes, and lines 258-274 to verify
exactly-once behavior for ambiguous token commits before retry assertions.
model/user_cache.go (1)

111-117: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Enqueue failed auth-cache invalidations.

Both paths return on invalidation failure without using the new retry helper. Since User.Update only logs this error, stale role or status data can remain authoritative until cache expiry.

Proposed pattern
-if err := invalidateUserCache(userId); err != nil {
-	return err
+if err := invalidateUserCache(userId); err != nil {
+	enqueueUserCacheInvalidationRetry(userId, err)
+	return err
 }

Also applies to: 289-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/user_cache.go` around lines 111 - 117, Update updateUserCache and the
corresponding second invalidation path to enqueue failed auth-cache
invalidations through the new retry helper instead of returning the invalidation
error directly. Preserve the Redis-disabled behavior and ensure failures from
invalidateUserCache are handed to the retry mechanism before returning.
model/log_test.go (1)

861-865: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clean up the snapshot marker created by this test.

The synchronous save now inserts a QuotaDataSnapshot, but cleanup only deletes QuotaData, leaking state into subsequent tests.

Proposed fix
 require.NoError(t, DB.AutoMigrate(&QuotaData{}, &QuotaDataSnapshot{}))
 require.NoError(t, DB.Where("1 = 1").Delete(&QuotaData{}).Error)
+require.NoError(t, DB.Where("1 = 1").Delete(&QuotaDataSnapshot{}).Error)
 t.Cleanup(func() {
 	require.NoError(t, DB.Where("1 = 1").Delete(&QuotaData{}).Error)
+	require.NoError(t, DB.Where("1 = 1").Delete(&QuotaDataSnapshot{}).Error)
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/log_test.go` around lines 861 - 865, Update the test cleanup around the
synchronous save to also delete all QuotaDataSnapshot records, both before the
test and in the t.Cleanup callback, alongside the existing QuotaData deletion.
Use the existing DB cleanup pattern and preserve the QuotaData cleanup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/option.go`:
- Around line 704-710: Update the group_ratio_setting/group_ratio branch to
apply ratio_setting.UpdateGroupRatioByJSONString(value) without returning early,
then continue into config.UpdateConfigFromMap(cfg, configMap) so the registered
config is updated as well.

In `@model/token_cache.go`:
- Around line 106-112: Update the pending-entry deadline logic in the token
cache around the deleteEntry handling, including the corresponding block near
the second referenced location, so failed deletion attempts receive the same
bounded retry window as invalidations instead of remaining pending indefinitely.
When the deadline expires, stop retrying and emit a terminal metric or log
identifying the expired deletion.

In `@model/usedata.go`:
- Around line 19-20: The quota data migration around Usedata must populate
AggregateKey for legacy rows, merge duplicate buckets, and create the unique
aggregate-key index only after cleanup, with SQL compatible with SQLite, MySQL
5.7.8+, and PostgreSQL 9.6+. Add coverage in model/usedata_test.go:101-108 for a
populated legacy table and verify later aggregate-key upserts increment the
migrated row; update model/usedata.go:19-20 for the migration, while the test
site requires the corresponding migration and upsert assertions.

In `@model/user.go`:
- Line 1094: Update the user deletion flow around deleteUserCache so cache
deletion failures are logged and scheduled through the persistent delete-mode
retry mechanism, without propagating the cache error after the database deletion
commits. Return success once the DB deletion succeeds, and apply the same
behavior to the corresponding path near the second referenced location.
- Around line 1261-1283: Enforce database-level uniqueness for the provider
identity fields checked by IsWeChatIdAlreadyTaken, IsGitHubIdAlreadyTaken,
IsDiscordIdAlreadyTaken, IsOidcIdAlreadyTaken, and IsTelegramIdAlreadyTaken.
Update the migration to convert empty IDs to NULL, then add compatible unique
constraints for SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+ (or reuse the uniquely
constrained binding table), ensuring legacy duplicate rows are handled before
constraints are created.
- Around line 153-155: Update the error branch after invalidateUserCache in the
user-setting mutation flow to enqueue the existing cache-invalidation retry,
matching the retry behavior used by quota mutations. Preserve the current error
logging and pass the affected userId through the established retry mechanism.

---

Outside diff comments:
In `@controller/oauth.go`:
- Around line 38-48: Update handleOAuthUserLookupError so only the
ErrUserDeleted case returns its existing user-facing message; for all other
errors, log the raw error internally and return a generic client-safe error
message instead of err.Error(), matching the masking behavior of
handleOAuthIdentityLookupError.

In `@model/log_test.go`:
- Around line 861-865: Update the test cleanup around the synchronous save to
also delete all QuotaDataSnapshot records, both before the test and in the
t.Cleanup callback, alongside the existing QuotaData deletion. Use the existing
DB cleanup pattern and preserve the QuotaData cleanup behavior.

In `@model/user_cache.go`:
- Around line 111-117: Update updateUserCache and the corresponding second
invalidation path to enqueue failed auth-cache invalidations through the new
retry helper instead of returning the invalidation error directly. Preserve the
Redis-disabled behavior and ensure failures from invalidateUserCache are handed
to the retry mechanism before returning.

In `@service/billing_session.go`:
- Around line 74-82: Update the !s.fundingSettled branch around
FundingSource.Settle to compare applied with delta; when they differ, retain the
applied amount, mark fundingReconcilePending, and return before applying the
token adjustment. Only set fundingSettled and clear reconciliation state when
the full delta is applied, preserving compensationFailed handling.
- Around line 46-63: Make settlement mutations idempotent across retries and
ambiguous outcomes: update service/billing_session.go lines 46-63 in
BillingSession.Settle to create and reuse a durable settlement operation ID;
keep ambiguous reconciliation unresolved and prevent token adjustment at lines
83-94; at lines 123-131, only treat the original funding delta as committed when
compensation confirms it, rather than assuming failed compensation succeeded.
Extend service/billing_session_test.go lines 103-130 to cover
compensation-committed and compensation-not-committed outcomes, and lines
258-274 to verify exactly-once behavior for ambiguous token commits before retry
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c685da08-03a6-46e6-8326-b0061e7df29c

📥 Commits

Reviewing files that changed from the base of the PR and between 8ae50e9 and f7980b0.

📒 Files selected for processing (34)
  • common/init.go
  • common/redis.go
  • common/redis_version_test.go
  • controller/discord.go
  • controller/github.go
  • controller/linuxdo.go
  • controller/oauth.go
  • controller/oauth_test.go
  • controller/oidc.go
  • controller/telegram.go
  • controller/wechat.go
  • model/checkin.go
  • model/log_test.go
  • model/main.go
  • model/option.go
  • model/payment_method_guard_test.go
  • model/subscription.go
  • model/token.go
  • model/token_cache.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_cache.go
  • model/user_oauth_binding.go
  • model/user_update_test.go
  • oauth/discord.go
  • oauth/generic.go
  • oauth/github.go
  • oauth/linuxdo.go
  • oauth/oidc.go
  • oauth/provider.go
  • service/billing_session.go
  • service/billing_session_test.go
  • service/pre_consume_quota.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • model/log_test.go
  • oauth/provider.go
  • oauth/github.go
  • oauth/oidc.go
  • model/checkin.go
  • common/init.go
  • oauth/discord.go
  • controller/oidc.go
  • model/option.go
  • oauth/generic.go
  • model/payment_method_guard_test.go
  • controller/telegram.go
  • controller/linuxdo.go
  • service/pre_consume_quota.go
  • controller/github.go
  • oauth/linuxdo.go
  • common/redis_version_test.go
  • controller/oauth_test.go
  • controller/discord.go
  • model/token_cache.go
  • controller/oauth.go
  • model/subscription.go
  • model/usedata_test.go
  • model/main.go
  • service/billing_session_test.go
  • model/usedata.go
  • service/billing_session.go
  • controller/wechat.go
  • model/user_oauth_binding.go
  • model/user_cache.go
  • model/token.go
  • model/user_update_test.go
  • common/redis.go
  • model/user.go
🔇 Additional comments (33)
common/init.go (1)

131-131: LGTM!

model/payment_method_guard_test.go (1)

219-238: LGTM!

model/subscription.go (1)

41-50: LGTM!

Also applies to: 663-663, 748-748, 848-849, 954-954, 995-995, 1211-1211

controller/oauth_test.go (1)

4-34: LGTM!

Also applies to: 59-69, 71-93, 95-132

oauth/discord.go (1)

157-159: LGTM!

oauth/generic.go (1)

293-295: LGTM!

oauth/github.go (1)

163-165: LGTM!

oauth/linuxdo.go (1)

170-172: LGTM!

oauth/oidc.go (1)

162-164: LGTM!

oauth/provider.go (1)

26-26: LGTM!

controller/telegram.go (1)

35-39: LGTM!

Also applies to: 99-99

model/checkin.go (1)

118-121: 🔒 Security & Privacy

No issue here. enqueueUserCacheInvalidationRetry and invalidateUserQuotaCache both clear the same user cache, and the SQLite path reaches the same quota invalidation through IncreaseUserQuota.

			> Likely an incorrect or invalid review comment.
model/token_cache.go (2)

69-73: Version-key expiration was already raised in the previous review.

The shared invalidation helper still needs a bounded TTL for generated version keys.


5-68: LGTM!

Also applies to: 74-105, 113-180, 186-264, 278-282

model/main.go (2)

285-287: LGTM!

Also applies to: 301-301, 345-347, 366-366


683-698: 🗄️ Data Integrity & Integration

No change needed.

model/usedata.go (1)

4-18: LGTM!

Also applies to: 21-124, 149-216

model/usedata_test.go (1)

4-100: LGTM!

Also applies to: 110-212

model/user_cache.go (1)

65-85: LGTM!

Also applies to: 94-105, 151-158, 181-182, 224-232, 250-287, 300-309

common/redis_version_test.go (1)

1-49: LGTM!

service/billing_session.go (1)

8-45: LGTM!

Also applies to: 64-73, 95-122, 132-155, 373-374, 386-386, 466-468

service/billing_session_test.go (1)

4-102: LGTM!

Also applies to: 132-257, 276-309

service/pre_consume_quota.go (1)

4-4: LGTM!

Also applies to: 56-57, 71-86

model/token.go (1)

23-23: LGTM!

Also applies to: 28-28, 316-316, 328-328, 337-339, 436-481, 511-511, 546-548, 584-588

model/user.go (1)

1404-1410: LGTM!

Also applies to: 1424-1430, 1442-1448, 1462-1468, 1503-1506, 1536-1539, 1558-1562, 1656-1662, 1676-1682

model/user_oauth_binding.go (1)

67-70: LGTM!

Also applies to: 86-90, 112-114, 131-148

model/user_update_test.go (1)

98-114: LGTM!

Also applies to: 223-260, 270-550, 641-660

common/redis.go (1)

84-118: LGTM!

Also applies to: 231-237, 256-293, 297-314

controller/discord.go (1)

133-141: LGTM!

Also applies to: 198-202

controller/github.go (1)

115-124: LGTM!

Also applies to: 185-189

controller/linuxdo.go (1)

50-54: LGTM!

Also applies to: 214-222

controller/oidc.go (1)

135-143: LGTM!

Also applies to: 201-205

controller/wechat.go (1)

76-84: LGTM!

Also applies to: 147-151

Comment thread model/option.go Outdated
Comment thread model/token_cache.go Outdated
Comment thread model/usedata.go Outdated
Comment thread model/user.go
Comment thread model/user.go Outdated
Comment thread model/user.go
Comment on lines +1261 to +1283
func IsWeChatIdAlreadyTaken(wechatId string) (bool, error) {
result := DB.Unscoped().Where("wechat_id = ?", wechatId).Limit(1).Find(&User{})
return result.RowsAffected > 0, result.Error
}

func IsGitHubIdAlreadyTaken(githubId string) bool {
return DB.Unscoped().Where("github_id = ?", githubId).Find(&User{}).RowsAffected == 1
func IsGitHubIdAlreadyTaken(githubId string) (bool, error) {
result := DB.Unscoped().Where("github_id = ?", githubId).Limit(1).Find(&User{})
return result.RowsAffected > 0, result.Error
}

func IsDiscordIdAlreadyTaken(discordId string) bool {
return DB.Unscoped().Where("discord_id = ?", discordId).Find(&User{}).RowsAffected == 1
func IsDiscordIdAlreadyTaken(discordId string) (bool, error) {
result := DB.Unscoped().Where("discord_id = ?", discordId).Limit(1).Find(&User{})
return result.RowsAffected > 0, result.Error
}

func IsOidcIdAlreadyTaken(oidcId string) bool {
return DB.Where("oidc_id = ?", oidcId).Find(&User{}).RowsAffected == 1
func IsOidcIdAlreadyTaken(oidcId string) (bool, error) {
result := DB.Unscoped().Where("oidc_id = ?", oidcId).Limit(1).Find(&User{})
return result.RowsAffected > 0, result.Error
}

func IsTelegramIdAlreadyTaken(telegramId string) bool {
return DB.Unscoped().Where("telegram_id = ?", telegramId).Find(&User{}).RowsAffected == 1
func IsTelegramIdAlreadyTaken(telegramId string) (bool, error) {
result := DB.Unscoped().Where("telegram_id = ?", telegramId).Limit(1).Find(&User{})
return result.RowsAffected > 0, result.Error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Enforce OAuth identity uniqueness in the database.

These checks are vulnerable to check-then-insert races because the provider-ID columns are only indexed, not unique; TestIsOidcIdAlreadyTakenHandlesDuplicateLegacyRows confirms duplicates are accepted. Concurrent OAuth registrations can therefore bind one provider identity to multiple users.

Migrate empty IDs to NULL and add unique constraints, or move these identities to the already uniquely constrained binding table.

As per coding guidelines, migrations must work across SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.

Also applies to: 1691-1693

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/user.go` around lines 1261 - 1283, Enforce database-level uniqueness
for the provider identity fields checked by IsWeChatIdAlreadyTaken,
IsGitHubIdAlreadyTaken, IsDiscordIdAlreadyTaken, IsOidcIdAlreadyTaken, and
IsTelegramIdAlreadyTaken. Update the migration to convert empty IDs to NULL,
then add compatible unique constraints for SQLite, MySQL 5.7.8+, and PostgreSQL
9.6+ (or reuse the uniquely constrained binding table), ensuring legacy
duplicate rows are handled before constraints are created.

Source: Coding guidelines

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
setting/config/config.go (1)

212-239: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject fractional and overflowing numeric values.

The float fallback silently converts values such as "2.9" to 2, and SetInt/SetUint can truncate values outside narrower field ranges. Require an integral finite value and check field.OverflowInt, field.OverflowUint, or field.OverflowFloat before assignment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@setting/config/config.go` around lines 212 - 239, Update the numeric parsing
branches in the configuration conversion logic to accept float-formatted
fallbacks only when the parsed value is finite and integral, rejecting
fractional values. Before each field.SetInt, field.SetUint, or field.SetFloat
call, validate the parsed value with the corresponding reflection overflow
check, and return the existing invalid-value error when validation fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/usedata.go`:
- Around line 128-140: Update the migration flow around
mergeQuotaDataAggregateRows and quotaDataAggregateKeyIndexName so the existing
aggregate uniqueness index is never dropped during backfill. Preserve the
constraint while clearing stale keys to NULL, merging/deleting duplicate rows,
and assigning keys to survivors; ensure the SQL and migration behavior remain
compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.
- Around line 159-204: Replace the unbounded read-and-per-group writes in
mergeQuotaDataAggregateRows with a bounded-batch or portable set-based migration
that avoids holding the entire quota_data table in memory or one long
transaction. Preserve aggregate grouping, survivor selection by ascending id,
summed count/quota/tokenUsed values, duplicate deletion, and error propagation
while ensuring the implementation works on SQLite, MySQL 5.7.8+, and PostgreSQL
9.6+.

In `@types/rw_map.go`:
- Around line 98-103: Update the method containing the m.data assignment so the
mutex is unlocked before invoking onSuccess. Avoid deferring the unlock across
the callback; retain the locked section for updating m.data, then call onSuccess
after releasing the lock so callbacks may safely use ReadAll, Set, or other
mutex-protected methods.

---

Outside diff comments:
In `@setting/config/config.go`:
- Around line 212-239: Update the numeric parsing branches in the configuration
conversion logic to accept float-formatted fallbacks only when the parsed value
is finite and integral, rejecting fractional values. Before each field.SetInt,
field.SetUint, or field.SetFloat call, validate the parsed value with the
corresponding reflection overflow check, and return the existing invalid-value
error when validation fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f653cf6-f096-4316-b841-27acff04564a

📥 Commits

Reviewing files that changed from the base of the PR and between f7980b0 and 1f41bda.

📒 Files selected for processing (14)
  • model/log_test.go
  • model/main.go
  • model/option.go
  • model/option_test.go
  • model/token_cache.go
  • model/token_cache_test.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_cache.go
  • model/user_update_test.go
  • setting/config/config.go
  • setting/config/config_test.go
  • types/rw_map.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • model/token_cache_test.go
  • model/option.go
  • model/option_test.go
  • setting/config/config_test.go
  • model/log_test.go
  • types/rw_map.go
  • model/token_cache.go
  • setting/config/config.go
  • model/usedata.go
  • model/main.go
  • model/usedata_test.go
  • model/user_cache.go
  • model/user_update_test.go
  • model/user.go
🔇 Additional comments (14)
model/token_cache_test.go (1)

1-78: LGTM!

model/option.go (1)

704-714: LGTM!

model/option_test.go (1)

7-7: LGTM!

Also applies to: 87-88, 99-105

model/token_cache.go (1)

83-89: LGTM!

Also applies to: 114-114, 123-123, 183-189, 285-289

model/main.go (1)

39-53: LGTM!

Also applies to: 104-110, 307-309, 323-323, 344-349, 373-375, 394-394, 435-440, 470-615, 863-878

model/usedata.go (1)

17-127: LGTM!

Also applies to: 141-158, 205-206, 216-331

model/usedata_test.go (1)

1-281: LGTM!

model/user_cache.go (1)

5-5: LGTM!

Also applies to: 17-50, 88-306, 315-326, 336-356, 372-379, 402-403, 453-482, 493-530

model/user.go (1)

153-155: LGTM!

Also applies to: 583-594, 1095-1110, 1163-1177, 1192-1237, 1264-1286, 1366-1390, 1407-1433, 1445-1471, 1493-1565, 1659-1685, 1694-1703

model/user_update_test.go (1)

12-231: LGTM!

Also applies to: 233-734, 874-931

model/log_test.go (1)

861-862: LGTM!

setting/config/config.go (1)

4-4: LGTM!

Also applies to: 62-62, 137-148, 168-211, 240-277

types/rw_map.go (1)

14-22: LGTM!

Also applies to: 81-97, 104-104

setting/config/config_test.go (1)

5-20: LGTM!

Also applies to: 105-132

Comment thread model/usedata.go
Comment thread model/usedata.go
Comment thread types/rw_map.go
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/usedata_test.go`:
- Around line 303-315: Extend the assertions in the QuotaData migration test to
validate row B’s repaired Count, Quota, and TokenUsed values, matching the
expected counters established for that row’s fixture. Keep the existing ordering
and AggregateKey assertions unchanged.

In `@model/usedata.go`:
- Around line 114-124: Update quotaDataAggregateMigrationMember and
quotaDataAggregateMigrationSummary to use int64 for Id, SurvivorID, and every
accumulated total field (CountSum, QuotaSum, and TokenUsedSum), and set their
GORM column types to BIGINT where explicit types are defined. Apply the same
64-bit identifier and total-field changes to the related migration definitions
around the referenced aggregate migration code, preserving SQLite, MySQL, and
PostgreSQL compatibility.
- Around line 240-263: The quota aggregate migration spanning
buildQuotaDataAggregateMigrationTables and
insertQuotaDataAggregateMigrationBatch must not overwrite concurrent quota
writes or clear keys that are concurrently recreated. Protect the complete
row-scan and apply operation with the existing cross-database mechanism that
quiesces quota writes, or redesign the apply as an atomic reconciliation
compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; do not rely on a
database-specific locking feature.
- Around line 168-190: Update mergeQuotaDataAggregateRows to acquire a
database-backed migration lock before resetQuotaDataAggregateMigrationTables and
hold it through the final dropQuotaDataAggregateMigrationTables cleanup,
releasing it on every return path. Use the project’s cross-database locking
mechanism compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+, and ensure
lock acquisition or release errors are handled without allowing concurrent
migration runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 021feef7-36be-44dd-9fcd-b472d6b2bf38

📥 Commits

Reviewing files that changed from the base of the PR and between 1f41bda and 7ca7906.

📒 Files selected for processing (4)
  • model/usedata.go
  • model/usedata_test.go
  • types/rw_map.go
  • types/rw_map_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • types/rw_map_test.go
  • types/rw_map.go
  • model/usedata_test.go
  • model/usedata.go
🪛 ast-grep (0.44.1)
model/usedata.go

[error] 232-232: SQL query is built by concatenating a string literal with a variable and passed to a database/sql call (Query, Exec, QueryRow, Prepare, or their Context variants). String concatenation lets attacker-controlled input alter the query structure, enabling SQL injection. Use parameterized queries with placeholders ('?' or '') and pass the values as separate arguments instead of concatenating them into the query string.
Context: DB.Exec("DROP TABLE IF EXISTS " + quoteDBIdentifier(table))
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-query-string-concat-go)

🪛 OpenGrep (1.25.0)
model/usedata.go

[ERROR] 233-233: SQL query built via fmt.Sprintf or string concatenation passed to a database method. Use parameterized queries with placeholder arguments.

(coderabbit.sql-injection.go-query-format)

🔇 Additional comments (6)
types/rw_map.go (1)

100-100: LGTM!

types/rw_map_test.go (1)

1-34: LGTM!

model/usedata_test.go (1)

4-41: LGTM!

Also applies to: 202-302

model/usedata.go (3)

19-20: LGTM!

Also applies to: 34-43


231-238: LGTM!

Also applies to: 302-316


46-46: 🚀 Performance & Scalability

Bound retention for QuotaDataSnapshot markers. saveQuotaData writes a durable row per snapshot, but no cleanup path is visible for quota_data_snapshots. If these markers are only needed for the retry window, add explicit pruning so the table doesn’t grow without bound.

Comment thread model/usedata_test.go
Comment thread model/usedata.go Outdated
Comment thread model/usedata.go Outdated
Comment thread model/usedata.go Outdated
@CSCITech

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
model/usedata.go (2)

875-913: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return individual quota persistence failures.

When saveQuotaDataLocked fails, the snapshot is requeued but the callback returns nil, leaving flushErr nil. Callers therefore receive false success and may terminate while the retry exists only in memory.

Proposed fix
 var flushErr error
+var saveErr error
 ...
 if err := saveQuotaDataLocked(ctx, quotaData); err != nil {
     common.SysLog(fmt.Sprintf("saveQuotaData error: %s", err))
     failed = append(failed, quotaData)
+    if saveErr == nil {
+        saveErr = err
+    }
 }
 ...
-return flushErr
+if flushErr != nil {
+    return flushErr
+}
+return saveErr
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/usedata.go` around lines 875 - 913, Update the quota cache flush
callback around saveQuotaDataLocked so an individual persistence failure is
propagated through flushErr instead of returning nil. Preserve requeueing failed
quota data, but record and return the encountered error after processing or
immediately according to the existing retry semantics, ensuring callers receive
a non-nil failure when any save fails.

577-603: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Use the database clock for distributed lock expiry.

expires_at is calculated and compared using each application host’s clock. A fast node can steal a lock still held by a slower node, allowing concurrent migrations or quota flushes. Calculate both now and the new expiry atomically from the database clock, with dialect-specific expressions where necessary.

As per coding guidelines, all database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.

Also applies to: 669-675

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/usedata.go` around lines 577 - 603, The quota lock acquisition flow
around quotaDataOperationLock must use the database server clock rather than
common.GetTimestamp() for both expiry comparisons and new expiry values. Replace
the application-time now and expiry expressions in the lock creation and
expired-lock Updates paths with dialect-compatible database-time expressions
supporting SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+, while preserving the
existing lock lease duration and acquisition behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@model/usedata.go`:
- Around line 875-913: Update the quota cache flush callback around
saveQuotaDataLocked so an individual persistence failure is propagated through
flushErr instead of returning nil. Preserve requeueing failed quota data, but
record and return the encountered error after processing or immediately
according to the existing retry semantics, ensuring callers receive a non-nil
failure when any save fails.
- Around line 577-603: The quota lock acquisition flow around
quotaDataOperationLock must use the database server clock rather than
common.GetTimestamp() for both expiry comparisons and new expiry values. Replace
the application-time now and expiry expressions in the lock creation and
expired-lock Updates paths with dialect-compatible database-time expressions
supporting SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+, while preserving the
existing lock lease duration and acquisition behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b20b078-43a6-4c1d-a3e7-e2b3573f7a0d

📥 Commits

Reviewing files that changed from the base of the PR and between 0d021af and 12190b1.

📒 Files selected for processing (5)
  • main.go
  • main_test.go
  • model/log.go
  • model/usedata.go
  • model/usedata_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • main_test.go
  • model/log.go
  • main.go
  • model/usedata.go
  • model/usedata_test.go
🔇 Additional comments (5)
main_test.go (1)

33-38: LGTM!

model/log.go (1)

206-206: LGTM!

main.go (1)

242-250: LGTM!

Also applies to: 288-295

model/usedata_test.go (1)

111-119: LGTM!

Also applies to: 415-415, 472-472, 517-585, 614-614, 669-669, 726-726

model/usedata.go (1)

4-4: LGTM!

Also applies to: 21-22, 79-91, 170-200, 283-318, 557-576, 613-624, 681-681, 729-798, 916-949

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/usedata.go`:
- Around line 573-580: Make MySQL lock acquisition independent of RowsAffected:
update createQuotaDataOperationLockAttempt and its callers to use an explicit
insert-versus-duplicate signal, or ensure the MySQL DSN path forces
clientFoundRows=false. Update the MySQL setup at model/usedata.go:628-636
accordingly, and revise model/usedata_test.go:917-919 and
model/usedata_test.go:943-960 to assert acquisition semantics rather than
generated SQL text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 11a40516-0346-4e03-9502-1e0e0e296ca6

📥 Commits

Reviewing files that changed from the base of the PR and between 0d021af and c9417da.

📒 Files selected for processing (11)
  • README.en.md
  • README.fr.md
  • README.ja.md
  • README.md
  • README.zh_CN.md
  • README.zh_TW.md
  • main.go
  • main_test.go
  • model/log.go
  • model/usedata.go
  • model/usedata_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • model/log.go
  • main.go
  • main_test.go
  • model/usedata_test.go
  • model/usedata.go
🪛 LanguageTool
README.fr.md

[typographical] ~118-~118: Il manque une espace après le point.
Context: ...e n'est requis pour un essai local. > [!WARNING] > SQLite est réservé à l'évaluation lo...

(ESPACE_APRES_POINT)

🔇 Additional comments (12)
model/log.go (1)

206-206: LGTM!

README.zh_TW.md (1)

126-128: LGTM!

Also applies to: 493-494

README.fr.md (1)

118-120: LGTM!

Also applies to: 430-431

README.md (1)

128-130: LGTM!

Also applies to: 495-496

README.zh_CN.md (1)

126-128: LGTM!

Also applies to: 493-494

README.ja.md (1)

118-120: LGTM!

Also applies to: 430-431

main.go (1)

242-250: LGTM!

Also applies to: 288-296

main_test.go (1)

33-39: LGTM!

README.en.md (1)

126-128: LGTM!

Also applies to: 493-494

model/usedata.go (2)

764-800: Lock loss still does not cancel in-flight database work.

The heartbeat only records lostErr; callback queries continue using the original context and may commit after another owner claims the lease. Use a guard-owned cancellable context for all protected DB operations and cancel it from setLost.

Also applies to: 809-817, 917-934


4-4: LGTM!

Also applies to: 83-91, 171-171, 191-197, 287-287, 557-571, 583-600, 602-627, 637-660, 701-716, 892-916, 935-956, 971-979

model/usedata_test.go (1)

111-119: LGTM!

Also applies to: 131-159, 445-445, 502-502, 547-615, 644-644, 699-699, 756-756, 888-916, 920-942

Comment thread model/usedata.go
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
model/usedata.go (1)

67-68: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Bound QuotaDataSnapshot marker retention.

Each unique flush permanently inserts a snapshot marker, but no cleanup path is present. This table and its primary index will grow with usage indefinitely. Delete markers older than a safe retry horizon via a bounded, portable cleanup job.

As per coding guidelines, the cleanup must support SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/usedata.go` around lines 67 - 68, Add a bounded, portable cleanup job
for QuotaDataSnapshot that deletes markers older than a safe retry horizon,
using database-compatible operations for SQLite, MySQL 5.7.8+, and PostgreSQL
9.6+. Integrate it with the existing snapshot flush or maintenance flow,
preserve recent markers needed for retries, and ensure cleanup errors are
handled through the established error path.

Source: Coding guidelines

model/main.go (1)

470-500: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Prevent OAuth writes between duplicate cleanup and index creation.

Line 490 commits the cleanup before Lines 493-499 install uniqueness. During rolling startup, another instance can reinsert a duplicate in that gap, causing index creation—and application startup—to fail. Quiesce OAuth identity mutations or hold a database-specific lock across cleanup and constraint installation.

As per coding guidelines, the locking and migration strategy must support SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/main.go` around lines 470 - 500, Update
migrateUserOAuthIdentityConstraints to prevent concurrent OAuth identity writes
across both duplicate cleanup and ensureUserOAuthIdentityUniqueIndex: hold an
appropriate database lock or quiesce mutations until the index installation
completes. Implement the locking strategy compatibly for SQLite, MySQL 5.7.8+,
and PostgreSQL 9.6+, while preserving the existing transaction cleanup and
migration behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@model/main.go`:
- Around line 470-500: Update migrateUserOAuthIdentityConstraints to prevent
concurrent OAuth identity writes across both duplicate cleanup and
ensureUserOAuthIdentityUniqueIndex: hold an appropriate database lock or quiesce
mutations until the index installation completes. Implement the locking strategy
compatibly for SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+, while preserving the
existing transaction cleanup and migration behavior.

In `@model/usedata.go`:
- Around line 67-68: Add a bounded, portable cleanup job for QuotaDataSnapshot
that deletes markers older than a safe retry horizon, using database-compatible
operations for SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+. Integrate it with the
existing snapshot flush or maintenance flow, preserve recent markers needed for
retries, and ensure cleanup errors are handled through the established error
path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 55ae8c30-b77e-412b-ad75-897171a881a0

📥 Commits

Reviewing files that changed from the base of the PR and between b8941bb and 1fa40ea.

📒 Files selected for processing (5)
  • model/main.go
  • model/main_migration_test.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user_update_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • model/main_migration_test.go
  • model/main.go
  • model/usedata.go
  • model/user_update_test.go
  • model/usedata_test.go
🔇 Additional comments (5)
model/main.go (1)

39-53: LGTM!

Also applies to: 104-110, 297-349, 369-440, 862-953

model/main_migration_test.go (1)

5-23: LGTM!

Also applies to: 45-63

model/usedata.go (1)

21-22: LGTM!

Also applies to: 37-61, 176-176, 229-273, 873-883, 1032-1108

model/usedata_test.go (1)

48-365: LGTM!

model/user_update_test.go (1)

900-930: LGTM!

@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/main.go`:
- Around line 622-629: Update the generated-column definition in the migration
flow around identity.mysqlGeneratedColumn so it does not truncate values from
the source identity column. Prefer a column type that preserves the full source
value, or consistently add matching max-length validation to the OAuth identity
fields in model/user.go and the generated column.

In `@model/usedata.go`:
- Around line 44-46: Update the snapshot cleanup and retry flow associated with
quotaDataSnapshotRetentionSeconds so idempotency markers are not deleted while
their snapshots remain retryable. Retain each marker until its snapshot is
durably retired, or enforce and persist a retry deadline shorter than the marker
retention period before cleanup removes it. Ensure the persistence and cleanup
changes use SQL supported by SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+.

In `@model/user_update_test.go`:
- Around line 947-961: The migration test’s fixed delay does not guarantee
worker startup, and its final receive can hang; add an explicit worker-start
handshake and replace unbounded completion receives with timeout-based waits.
Apply this to model/user_update_test.go ranges 947-961, 985-999, and 1027-1041,
covering migrateUserOAuthIdentityConstraints and the corresponding OAuth and
field update workers; preserve the lock-held assertion while ensuring each
worker has started before checking blocked completion.

In `@model/user.go`:
- Around line 270-305: Update the PostgreSQL and MySQL lock-handling branches
around releasePostgreSQLAdvisoryLock and releaseMySQLNamedLock so released is
set to true only after a successful unlock; allow the deferred cleanup to retry
when release fails. Preserve both fn(conn) and release errors when both occur,
returning the combined failure instead of discarding the release error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 319d2ec9-20c9-4bd1-a1c3-f844635b084c

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa40ea and 3c7ac13.

📒 Files selected for processing (7)
  • .env.example
  • controller/oauth.go
  • model/main.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_update_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • controller/oauth.go
  • model/main.go
  • model/usedata.go
  • model/user_update_test.go
  • model/user.go
  • model/usedata_test.go
🪛 ast-grep (0.44.1)
model/main.go

[error] 530-530: SQL query is built by concatenating a string literal with a variable and passed to a database/sql call (Query, Exec, QueryRow, Prepare, or their Context variants). String concatenation lets attacker-controlled input alter the query structure, enabling SQL injection. Use parameterized queries with placeholders ('?' or '') and pass the values as separate arguments instead of concatenating them into the query string.
Context: tx.Exec("LOCK TABLE " + quoteDBIdentifier("users") + " IN SHARE ROW EXCLUSIVE MODE")
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-query-string-concat-go)

🪛 OpenGrep (1.25.0)
model/main.go

[ERROR] 531-531: SQL query built via fmt.Sprintf or string concatenation passed to a database method. Use parameterized queries with placeholder arguments.

(coderabbit.sql-injection.go-query-format)

🔇 Additional comments (5)
model/usedata_test.go (1)

291-321: LGTM!

.env.example (1)

36-38: LGTM!

controller/oauth.go (1)

402-402: The write lock does not close the OAuth uniqueness race.

The lock starts after the checks at Lines 284 and 305. Two requests can both observe “not taken” before serializing their inserts, so the previously reported need for database-enforced uniqueness remains.

model/main.go (2)

470-545: LGTM!


633-652: LGTM!

Comment thread model/main.go
Comment thread model/usedata.go
Comment thread model/user_update_test.go Outdated
Comment thread model/user.go Outdated
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
model/user.go (1)

264-307: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Do not globally serialize every user update behind the OAuth migration lock.

Update and UpdateFields acquire this single lock even for unrelated fields, serializing writes across all users; MySQL callers can also fail after the 10-second lock timeout. Restrict coordination to OAuth-identity mutations or use migration-specific read/write fencing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/user.go` around lines 264 - 307, The user OAuth identity lock must not
guard unrelated user updates. Update the callers of
withUserOAuthIdentityMutationLock, including Update and UpdateFields, so only
mutations of OAuth identity fields use this lock; leave ordinary user-field
writes on their existing path. Preserve coordination for OAuth
migration-sensitive changes without imposing a global per-user-update lock or
MySQL timeout failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/usedata.go`:
- Around line 1043-1073: The cleanup logic around quotaDataRetryableSnapshotIDs
must use durable retry state instead of the process-local CacheQuotaData before
deleting QuotaDataSnapshot markers. Update model/usedata.go lines 1043-1073 to
persist and query retry eligibility using a schema compatible with SQLite, MySQL
5.7.8+, and PostgreSQL 9.6+, while preserving batch limits and deletion
behavior; update model/usedata_test.go lines 323-359 to simulate an empty local
cache while another instance owns the retry and verify the marker is retained.

---

Outside diff comments:
In `@model/user.go`:
- Around line 264-307: The user OAuth identity lock must not guard unrelated
user updates. Update the callers of withUserOAuthIdentityMutationLock, including
Update and UpdateFields, so only mutations of OAuth identity fields use this
lock; leave ordinary user-field writes on their existing path. Preserve
coordination for OAuth migration-sensitive changes without imposing a global
per-user-update lock or MySQL timeout failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 430bd715-4966-4f84-b666-2527a3ff10b4

📥 Commits

Reviewing files that changed from the base of the PR and between 3c7ac13 and 21e262c.

📒 Files selected for processing (6)
  • controller/oauth.go
  • model/main.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
  • model/user_update_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: In Go business code, all JSON marshal/unmarshal operations must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) and must not directly call encoding/json for actual encode/decode work.
All database code must be compatible with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+; prefer GORM abstractions over raw SQL, avoid direct AUTO_INCREMENT/SERIAL, use commonGroupCol/commonKeyCol and commonTrueVal/commonFalseVal for DB-specific SQL, branch with common.UsingPostgreSQL/common.UsingSQLite/common.UsingMySQL, avoid unsupported DB-specific functions/operators without fallback, and make migrations work across all three databases.
When implementing a new relay channel, confirm whether the provider supports StreamOptions; if it does, add that channel to streamSupportedChannels.
For request structs parsed from client JSON and re-marshaled to upstream providers, optional scalar fields must use pointer types with omitempty so explicit zero/false values are preserved instead of dropped.
When working on tiered/dynamic billing expression code, read pkg/billingexpr/expr.md first and follow its documented expression language, architecture, token normalization, quota conversion, and versioning patterns.

**/*.go: All JSON marshal/unmarshal operations in Go business code must use the wrapper functions in common/json.go (common.Marshal, common.Unmarshal, common.UnmarshalJsonStr, common.DecodeJson, common.GetJsonType) instead of directly importing or calling encoding/json for actual marshal/unmarshal work.
All database code in Go must remain compatible with SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6; prefer GORM abstractions, avoid raw SQL unless necessary, use the shared DB helper variables for reserved words and boolean literals, branch with the common.UsingPostgreSQL / common.UsingSQLite / common.UsingMySQL flags when need...

Files:

  • controller/oauth.go
  • model/main.go
  • model/user_update_test.go
  • model/usedata.go
  • model/usedata_test.go
  • model/user.go
🔇 Additional comments (6)
controller/oauth.go (1)

284-315: LGTM!

Also applies to: 402-412

model/main.go (1)

509-513: LGTM!

Also applies to: 551-563, 642-643, 672-680

model/usedata_test.go (1)

233-259: LGTM!

model/user.go (1)

10-23: LGTM!

Also applies to: 69-73, 86-96, 310-321, 378-400, 962-988, 1330-1339

model/user_update_test.go (2)

980-993: The startup handshake still permits false positives.

started closes before run() attempts the lock, so the goroutine can be descheduled there and the 50 ms branch can pass without testing contention. Use an acquisition-attempt test hook or equivalent deterministic synchronization; this affects all three helper callers.


821-857: LGTM!

Comment thread model/usedata.go Outdated
@CSCITech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@CSCITech
CSCITech merged commit 75ab64a into main Jul 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant