Skip to content

feat: add optional invitation-code registration - #6317

Open
fengzhiyushui wants to merge 8 commits into
QuantumNous:mainfrom
fengzhiyushui:feat/invitation-registration-clean
Open

feat: add optional invitation-code registration#6317
fengzhiyushui wants to merge 8 commits into
QuantumNous:mainfrom
fengzhiyushui:feat/invitation-registration-clean

Conversation

@fengzhiyushui

@fengzhiyushui fengzhiyushui commented Jul 19, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

邀请码功能默认关闭,关闭时保持现有注册行为。下述验证结果对应提交 97bd0d79,该提交已合入验证时的 upstream/main@c3db4140

📝 变更描述 / Description

本 PR 为新用户注册增加可选、可按注册方式配置的邀请码验证机制:

  • 超级管理员可以原子更新邀请配置,并在后台生成、停用、搜索和删除邀请码。
  • 邀请码在服务端注册事务中校验并原子核销,避免并发重复使用;用户、默认 Token 及第三方身份创建失败时会一并回滚。
  • 已有用户登录不需要邀请码,管理员创建用户不消耗邀请码;只有需要创建新用户的注册或第三方身份流程才会执行校验。
  • 明文邀请码仅在创建时返回一次,数据库仅保存 SHA-256 哈希和脱敏前缀;邀请码不写入 OAuth state、URL、Session、Token 或日志。
  • 实现覆盖 Classic 和 Default 两套前端,包括管理页面、注册流程、OAuth/AuthIdentity 绑定及旧版浏览器本地存储中的邀请码字段清理。

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 描述复核: 本描述已依据当前实现和验证证据逐项整理,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 本 PR 属于新功能,并已关联对应的功能建议 Issue。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 验证完成: 已运行后端、前端、并发和数据库矩阵验证,结果及现有基线问题如下记录。
  • 安全合规: 代码中无敏感凭据,邀请码存储、传输和日志边界已进行复核。

🧪 验证结果 / Validation

当前验证对象为合并提交 97bd0d79f9e75b1c09774f05be286c3690ed9b8b

  • Go: 根模块 go test ./... -count=1 通过,relaykit 子模块全量测试通过。
  • 前端: bun test 共 27 个文件、140 个测试通过;另行筛选的认证流程共 11 个文件、65 个测试也通过;typecheck 和生产构建通过。
  • SQLite: 邀请码并发单次消费、注册/身份/配置事务回滚及迁移定向测试通过;邀请码 race 测试 20/20 通过。
  • PostgreSQL 16.14: 88 个 Go JSON run/pass 事件,0 失败、0 跳过;应用启动后 /api/status 返回 HTTP 200。
  • MySQL 8.4.10: 86 个 Go JSON run/pass 事件,0 失败、0 跳过;应用启动后 /api/status 返回 HTTP 200。
  • OpenAPI: 候选新增本地 $ref 全部可解析,未引入 unresolved reference。
  • 安全与范围: 邀请码泄漏边界、身份唯一绑定和事务原子性已人工复核;验证日志高置信秘密扫描 0 命中。

ℹ️ 已知基线与覆盖边界 / Known Baselines and Coverage

  • 完整 -race 门禁会检出 logger.logHelper 中的既有 logCount 数据竞争。在本次 Candidate/Base A/B 重复测试中,54/54 次独立运行均复现同一缺陷,相关文件逐字节一致;这不是本 PR 引入的回归。
  • 全仓库 bun run lint 当前未通过(386 errors / 82 warnings),bun run format:check 当前也未通过(2 个文件)。这些诊断模式和两处格式问题均已存在于 upstream/main;前端测试、类型检查和生产构建已通过。
  • OpenAPI 的两项 Combination 问题在 base 和 candidate 中数量相同,属于上游基线。
  • 尚未使用完整历史 PostgreSQL/MySQL 数据库快照执行一次性升级测试;部分注册/OAuth 失败回滚专用用例仍以 SQLite fixture 为主。
  • 前端覆盖仍有缺口:邀请配置页和邀请码管理页尚无直接测试,oauth-create-flow.test.ts 未调用真实生产 API 函数。

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

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

Walkthrough

Adds invitation-code management, atomic invitation settings, transactional registration enforcement, provider identity migration, OAuth/WeChat/Telegram integration, root-only administration APIs, audit/log redaction, and OpenAPI documentation.

Changes

Invitation registration platform

Layer / File(s) Summary
Invitation settings and lifecycle
common/*, dto/registration.go, model/invitation_code.go, service/registration.go
Adds normalized invitation settings, hashed invitation-code storage and consumption, transactional registration, status handling, and validation coverage.
Auth identity migration
model/auth_identity.go, model/auth_identity_migration.go, model/user_oauth_binding.go, model/external_identity_claim.go
Moves provider ownership to transactional AuthIdentity records while migrating and preserving legacy projections.
Registration and OAuth integration
controller/user.go, controller/oauth.go, controller/wechat.go, controller/telegram.go
Applies invitation requirements to new registrations, stores only server-side OAuth invitation references, and updates provider binding flows.
Atomic options and database setup
model/option.go, model/main.go, common/database.go, model/locking.go, model/sqlite_retry.go
Adds atomic invitation option updates, SQLite timeout normalization, migration wiring, and contention handling.
Invitation administration
controller/invitation.go, controller/option.go, router/api-router.go, middleware/audit.go, controller/audit.go
Adds root-protected invitation CRUD, bulk deletion, atomic settings updates, and audit actions.
Security, tests, and documentation
middleware/logger.go, model/*_test.go, controller/*_test.go, router/*_test.go, docs/openapi/api.json, i18n/*
Redacts invitation values from logs and documents and tests the new lifecycle, concurrency, permissions, and API behavior.

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

Poem

I’m a rabbit with codes in my hat,
Hashing each carrot—imagine that!
OAuth hops through a safer gate,
Atomic paws keep records straight.
Logs hide secrets, roots hold the key—
New invitations bloom happily!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested default-disabled invitation-code registration, admin management, server-side validation, and OAuth/login handling.
Out of Scope Changes check ✅ Passed I don't see unrelated code changes; the broader auth, logging, docs, and test updates support invitation-code registration and its edge cases.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: optional invitation-code registration support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

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

⚠️ Outside diff range comments (1)
web/default/src/features/auth/sign-up/components/sign-up-form.tsx (1)

143-150: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Avoid side effects during the render phase.

Calling clearLegacyInvitationCodeStorage() directly in the component body causes a side effect during rendering. React requires render functions to be pure. Move this call into the useEffect hook that runs on mount.

💡 Proposed fix to move the side effect into the effect hook
-    clearLegacyInvitationCodeStorage()
   useEffect(() => {
+    clearLegacyInvitationCodeStorage()
     const searchParams = new URLSearchParams(window.location.search)
     const aff = searchParams.get('aff')?.trim()
     if (aff) {
       saveAffiliateCode(aff)
     }
   }, [])
🤖 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/auth/sign-up/components/sign-up-form.tsx` around
lines 143 - 150, Move the clearLegacyInvitationCodeStorage() call from the
SignUpForm component body into the existing mount-only useEffect alongside the
affiliate-code handling, keeping rendering pure and preserving both mount-time
behaviors.
🧹 Nitpick comments (4)
controller/invitation.go (1)

73-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use common.DecodeJson instead of c.ShouldBindJSON for consistency with the codebase's JSON-wrapper convention.

Both AddInvitationCodes (line 75) and UpdateInvitationCode (line 116) decode request bodies via c.ShouldBindJSON, while the sibling controller/option.go (UpdateOption, UpdateInvitationCodeOption) consistently uses common.DecodeJson(c.Request.Body, &request) for the same purpose in this same PR layer.

♻️ Proposed fix
 func AddInvitationCodes(c *gin.Context) {
 	request := invitationCodeCreateRequest{}
-	if err := c.ShouldBindJSON(&request); err != nil {
+	if err := common.DecodeJson(c.Request.Body, &request); err != nil {
 		common.ApiErrorI18n(c, i18n.MsgInvalidParams)
 		return
 	}
 func UpdateInvitationCode(c *gin.Context) {
 	request := invitationCodeUpdateRequest{}
-	if err := c.ShouldBindJSON(&request); err != nil {
+	if err := common.DecodeJson(c.Request.Body, &request); err != nil {
 		common.ApiErrorI18n(c, i18n.MsgInvalidParams)
 		return
 	}

As per coding guidelines: "All JSON marshal and unmarshal operations in business code must use the wrappers in common/json.go... rather than direct encoding/json calls."

Also applies to: 114-119

🤖 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/invitation.go` around lines 73 - 79, Replace direct
c.ShouldBindJSON usage with common.DecodeJson(c.Request.Body, &request) in both
AddInvitationCodes and UpdateInvitationCode, preserving their existing
invalid-parameter error handling and request processing.

Source: Coding guidelines

web/default/src/features/auth/components/oauth-providers.tsx (1)

59-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid destructuring component props.

As per coding guidelines, avoid destructuring objects unless necessary, especially component props. Prefer direct property access (e.g., props.registrationMode) for clarity. Since the existing code already destructures props, you may consider refactoring the component signature to use props directly in a future cleanup to align with project standards.

🤖 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/auth/components/oauth-providers.tsx` around lines 59
- 61, Refactor the OAuth providers component to accept a single props object
instead of destructuring its parameters. Update references to registrationMode,
invitationCode, and onInvitationRequired to use the props object directly,
preserving the existing behavior.

Source: Coding guidelines

web/default/src/features/system-settings/auth/invitation-code-section.tsx (1)

97-145: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align form save feedback and dirty state tracking with other sections.

The newly added BasicAuthSection includes saveConfirmed state, FormDirtyIndicator, and calls form.reset(values) upon successful save to clear the form's dirty state. Consider implementing the same pattern here for consistent UX.

✨ Proposed implementation
-import { useMemo } from 'react'
+import { useMemo, useState } from 'react'
 import { useForm } from 'react-hook-form'
 import { useTranslation } from 'react-i18next'
 import { z } from 'zod'
@@ -48,6 +48,7 @@
 import { SettingsSection } from '../components/settings-section'
 import { useResetForm } from '../hooks/use-reset-form'
 import { useUpdateInvitationCodeConfig } from '../hooks/use-update-invitation-code-config'
+import { FormDirtyIndicator } from '../components/form-dirty-indicator'
 
 const invitationCodeSchema = z
   .object({
@@ -97,6 +98,7 @@
 export function InvitationCodeSection(props: InvitationCodeSectionProps) {
   const { t } = useTranslation()
   const updateInvitationCodeConfig = useUpdateInvitationCodeConfig()
+  const [saveConfirmed, setSaveConfirmed] = useState(false)
   const formDefaults = useMemo<InvitationCodeFormValues>(() => {
     const methods = INVITATION_REGISTRATION_METHODS.filter((method) =>
       props.defaultValues.InvitationCodeMethods.includes(method)
@@ -131,6 +133,8 @@
         required: values.InvitationCodeRequired,
         methods,
       })
+      form.reset(values)
+      setSaveConfirmed(true)
     } catch {
       // The mutation owns error feedback and keeps the form unchanged.
     }
@@ -142,7 +146,13 @@
           <SettingsPageFormActions
             onSave={form.handleSubmit(onSubmit)}
             isSaving={updateInvitationCodeConfig.isPending}
+            saveLabel={
+              saveConfirmed && !form.formState.isDirty
+                ? 'Saved'
+                : 'Save Changes'
+            }
           />
+          <FormDirtyIndicator isDirty={form.formState.isDirty} />
           <FormField
             control={form.control}
             name='InvitationCodeRequired'
🤖 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/system-settings/auth/invitation-code-section.tsx`
around lines 97 - 145, The InvitationCodeSection submit flow lacks save
confirmation and dirty-state reset. Add the established saveConfirmed state and
FormDirtyIndicator pattern used by BasicAuthSection, and reset the form with the
submitted values after updateInvitationCodeConfig.mutateAsync succeeds so a
successful save clears dirty tracking; preserve the existing mutation error
handling.
web/classic/src/pages/Invitation/index.jsx (1)

216-224: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Magic status numbers 1/2 for enable/disable.

Extracting named constants (e.g. INVITATION_STATUS_ENABLED = 1, INVITATION_STATUS_DISABLED = 2) would make these call sites self-documenting.

🤖 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/classic/src/pages/Invitation/index.jsx` around lines 216 - 224, Define
named constants for the enabled and disabled invitation statuses, then replace
the literal 1 and 2 arguments in the updateStatus calls within the Invitation
component with those constants. Keep the existing enable/disable behavior
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 `@controller/invitation.go`:
- Around line 30-186: Restrict the invitation management routes to root users by
replacing AdminAuth with RootAuth in router/api-router.go. Apply the same
authorization correction to the related management routes in
controller/invitation.go (lines 30-186) and controller/option.go (lines
359-390), preserving their existing handlers and behavior.

In `@controller/option.go`:
- Around line 130-133: Update the invitation-option guard in the option handling
flow to use common.ApiErrorI18n with a dedicated i18n.Msg* key instead of
passing ErrInvitationCodeOptionRequiresAtomicUpdate directly to common.ApiError.
Preserve the existing rejection behavior while ensuring the error is localized
consistently with the other invitation-related validations.

In `@model/auth_identity.go`:
- Around line 229-268: The backfillBuiltInAuthIdentities flow should process
users in bounded batches instead of loading the full table and inserting all
identities in one transaction. Iterate through the legacy users with a stable
primary-key cursor or equivalent batching, committing each batch independently,
and have duplicate or otherwise ambiguous legacy identity conflicts be skipped
or logged without aborting the remaining backfill; preserve fatal handling for
database errors that prevent continued processing.

In `@web/classic/src/components/auth/RegisterForm.jsx`:
- Around line 464-477: Update the invitation-code field condition in the OAuth
registration form to use the per-method requirement logic, showing it only when
at least one visible OAuth method requires an invitation code rather than
relying solely on status.invitation_code_required. Reuse isInvitationRequired
with the relevant visible provider methods and keep the existing password-form
behavior unchanged.

In `@web/classic/src/i18n/locales/vi.json`:
- Line 4240: Update the Vietnamese translation for the key
“邀请码明文仅在本次生成后显示,请妥善保存。” to explicitly convey that the invitation code is shown
in plaintext and only once after generation, while retaining the instruction to
store it securely.

In `@web/classic/src/pages/Invitation/index.jsx`:
- Around line 87-156: Wrap the API request and subsequent success handling in
updateStatus, deleteOne’s Modal.confirm onOk, deleteUsed’s onOk, and createCodes
with try/catch blocks matching load()’s established error handling. On rejected
requests, call showError with the caught error details and prevent success
notifications, state updates, reloads, or unhandled promise rejections.

In `@web/default/src/features/auth/lib/invitation.test.ts`:
- Around line 19-86: Convert the tests in the “invitation code compatibility”
suite from Node’s built-in test runner to Vitest: replace the node:test imports
and assertion usage with Vitest’s describe, test, and expect APIs. Preserve all
existing test cases, inputs, and expected behavior for getInvitationCodeMethods
and isInvitationCodeRequired.

In `@web/default/src/features/auth/lib/registration.test.ts`:
- Around line 19-83: Update the registration availability tests in the public
registration availability suite to import describe, test, and expect from vitest
instead of node:test and node:assert/strict, then replace assert.equal
assertions with expect(...).toBe(...) while preserving all existing test cases
and behavior.

In `@web/default/src/features/auth/lib/storage.test.ts`:
- Around line 19-55: Replace the Node.js test imports and test API in the
clearLegacyInvitationCodeStorage test with Vitest equivalents, while preserving
the existing assertions, window mocking, and cleanup behavior.

In `@web/default/src/i18n/locales/ru.json`:
- Around line 5194-5207: Update the Russian invitation metadata translations in
the locale entries for “Created at,” “Used at,” and “Used by” to “Дата
создания,” “Дата использования,” and “Использовано пользователем” respectively;
leave the other invitation-code translations unchanged.

In `@web/default/src/routes/_authenticated/invitation-codes/index.tsx`:
- Around line 26-31: Update the access check in the invitation route’s
beforeLoad guard to require ROLE.SUPER_ADMIN instead of ROLE.ADMIN, while
preserving the existing redirect for unauthenticated or insufficiently
privileged users.

---

Outside diff comments:
In `@web/default/src/features/auth/sign-up/components/sign-up-form.tsx`:
- Around line 143-150: Move the clearLegacyInvitationCodeStorage() call from the
SignUpForm component body into the existing mount-only useEffect alongside the
affiliate-code handling, keeping rendering pure and preserving both mount-time
behaviors.

---

Nitpick comments:
In `@controller/invitation.go`:
- Around line 73-79: Replace direct c.ShouldBindJSON usage with
common.DecodeJson(c.Request.Body, &request) in both AddInvitationCodes and
UpdateInvitationCode, preserving their existing invalid-parameter error handling
and request processing.

In `@web/classic/src/pages/Invitation/index.jsx`:
- Around line 216-224: Define named constants for the enabled and disabled
invitation statuses, then replace the literal 1 and 2 arguments in the
updateStatus calls within the Invitation component with those constants. Keep
the existing enable/disable behavior unchanged.

In `@web/default/src/features/auth/components/oauth-providers.tsx`:
- Around line 59-61: Refactor the OAuth providers component to accept a single
props object instead of destructuring its parameters. Update references to
registrationMode, invitationCode, and onInvitationRequired to use the props
object directly, preserving the existing behavior.

In `@web/default/src/features/system-settings/auth/invitation-code-section.tsx`:
- Around line 97-145: The InvitationCodeSection submit flow lacks save
confirmation and dirty-state reset. Add the established saveConfirmed state and
FormDirtyIndicator pattern used by BasicAuthSection, and reset the form with the
submitted values after updateInvitationCodeConfig.mutateAsync succeeds so a
successful save clears dirty tracking; preserve the existing mutation error
handling.
🪄 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: CHILL

Plan: Pro

Run ID: 30775c00-ab2e-42e7-8788-f199fc021ea0

📥 Commits

Reviewing files that changed from the base of the PR and between 5a6c53d and 2c5de1f.

📒 Files selected for processing (123)
  • common/constants.go
  • common/database.go
  • common/invitation.go
  • common/invitation_test.go
  • controller/audit.go
  • controller/custom_oauth.go
  • controller/custom_oauth_test.go
  • controller/invitation.go
  • controller/invitation_test.go
  • controller/misc.go
  • controller/oauth.go
  • controller/oauth_invitation_test.go
  • controller/option.go
  • controller/option_invitation_test.go
  • controller/registration_matrix_test.go
  • controller/telegram.go
  • controller/telegram_registration_boundary_test.go
  • controller/user.go
  • controller/wechat.go
  • controller/wechat_invitation_test.go
  • docs/openapi/api.json
  • dto/registration.go
  • i18n/keys.go
  • i18n/locales/en.yaml
  • i18n/locales/zh-CN.yaml
  • i18n/locales/zh-TW.yaml
  • middleware/audit.go
  • middleware/logger.go
  • middleware/logger_test.go
  • model/auth_identity.go
  • model/auth_identity_test.go
  • model/custom_oauth_provider.go
  • model/invitation_code.go
  • model/invitation_code_concurrency_test.go
  • model/invitation_code_test.go
  • model/locking.go
  • model/main.go
  • model/oauth_state_grant.go
  • model/oauth_state_grant_test.go
  • model/option.go
  • model/option_invitation_test.go
  • model/sqlite_dsn_test.go
  • model/sqlite_retry.go
  • model/task_cas_test.go
  • model/user.go
  • model/user_authentication_test.go
  • model/user_oauth_binding.go
  • router/api-router.go
  • router/passkey_registration_boundary_test.go
  • service/registration.go
  • service/registration_test.go
  • web/classic/src/App.jsx
  • web/classic/src/components/auth/LoginForm.jsx
  • web/classic/src/components/auth/OAuth2Callback.jsx
  • web/classic/src/components/auth/RegisterForm.jsx
  • web/classic/src/components/layout/PageLayout.jsx
  • web/classic/src/components/layout/SiderBar.jsx
  • web/classic/src/components/settings/SystemSetting.jsx
  • web/classic/src/components/settings/personal/cards/NotificationSettings.jsx
  • web/classic/src/helpers/api.js
  • web/classic/src/helpers/index.js
  • web/classic/src/helpers/invitation.js
  • web/classic/src/helpers/invitation.test.js
  • web/classic/src/helpers/render.jsx
  • web/classic/src/hooks/common/useSidebar.js
  • web/classic/src/i18n/locales/en.json
  • web/classic/src/i18n/locales/fr.json
  • web/classic/src/i18n/locales/ja.json
  • web/classic/src/i18n/locales/ru.json
  • web/classic/src/i18n/locales/vi.json
  • web/classic/src/i18n/locales/zh-CN.json
  • web/classic/src/i18n/locales/zh-TW.json
  • web/classic/src/pages/Invitation/index.jsx
  • web/classic/src/pages/Setting/Operation/SettingsSidebarModulesAdmin.jsx
  • web/classic/src/pages/Setting/Personal/SettingsSidebarModulesUser.jsx
  • web/default/src/components/layout/components/public-header.tsx
  • web/default/src/features/auth/api.ts
  • web/default/src/features/auth/components/oauth-providers.tsx
  • web/default/src/features/auth/constants.ts
  • web/default/src/features/auth/hooks/use-oauth-login.ts
  • web/default/src/features/auth/lib/invitation.test.ts
  • web/default/src/features/auth/lib/invitation.ts
  • web/default/src/features/auth/lib/registration.test.ts
  • web/default/src/features/auth/lib/registration.ts
  • web/default/src/features/auth/lib/storage.test.ts
  • web/default/src/features/auth/lib/storage.ts
  • web/default/src/features/auth/sign-in/index.tsx
  • web/default/src/features/auth/sign-up/components/sign-up-form.tsx
  • web/default/src/features/auth/types.ts
  • web/default/src/features/invitation-codes/api.ts
  • web/default/src/features/invitation-codes/components/create-invitation-codes-dialog.tsx
  • web/default/src/features/invitation-codes/components/generated-invitation-codes-dialog.tsx
  • web/default/src/features/invitation-codes/components/invitation-code-actions.tsx
  • web/default/src/features/invitation-codes/components/invitation-codes-data-view.tsx
  • web/default/src/features/invitation-codes/components/invitation-codes-table.tsx
  • web/default/src/features/invitation-codes/constants.ts
  • web/default/src/features/invitation-codes/index.tsx
  • web/default/src/features/invitation-codes/types.ts
  • web/default/src/features/system-settings/api.ts
  • web/default/src/features/system-settings/auth/basic-auth-section.tsx
  • web/default/src/features/system-settings/auth/index.tsx
  • web/default/src/features/system-settings/auth/invitation-code-section.tsx
  • web/default/src/features/system-settings/auth/section-registry.tsx
  • web/default/src/features/system-settings/hooks/use-update-invitation-code-config.ts
  • web/default/src/features/system-settings/hooks/use-update-option.ts
  • web/default/src/features/system-settings/maintenance/config.ts
  • web/default/src/features/system-settings/maintenance/sidebar-modules-section.tsx
  • web/default/src/features/system-settings/types.ts
  • web/default/src/hooks/use-sidebar-config.ts
  • web/default/src/hooks/use-sidebar-data.ts
  • 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-TW.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/static-keys.ts
  • web/default/src/lib/oauth.ts
  • web/default/src/routeTree.gen.ts
  • web/default/src/routes/(auth)/oauth.tsx
  • web/default/src/routes/_authenticated/invitation-codes/index.tsx
  • web/default/src/routes/oauth/$provider.tsx

Comment thread controller/invitation.go
Comment thread controller/option.go
Comment thread model/auth_identity.go
Comment thread web/classic/src/components/auth/RegisterForm.jsx Outdated
Comment thread web/classic/src/i18n/locales/vi.json Outdated
Comment thread web/src/features/auth/lib/invitation.test.ts
Comment thread web/src/features/auth/lib/registration.test.ts
Comment thread web/src/features/auth/lib/storage.test.ts
Comment thread web/default/src/i18n/locales/ru.json Outdated
Comment thread web/src/routes/_authenticated/invitation-codes/index.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/auth_identity_migration.go`:
- Around line 79-84: Update both migration paths in
model/auth_identity_migration.go: the CreateAuthIdentityWithTx handling at lines
79-84 and AuthIdentityProviderKeyForCustomOAuth handling at lines 143-148.
Detect empty or invalid claim.Subject and binding.ProviderUserId values, skip
those legacy rows, and increment the existing skipped/conflict counter instead
of aborting InitializeAuthIdentities(); preserve returning unexpected errors.

In `@model/auth_identity.go`:
- Around line 105-117: Update the legacy backfill update in the auth identity
binding flow to match rows where provider_subject_value is either an empty
string or NULL. Preserve the existing id constraint and provider-subject
collision checks, ensuring NULL-valued legacy identities are populated with
providerSubject.
🪄 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: CHILL

Plan: Pro

Run ID: bb5f8a42-6baa-4806-8aff-ca7a3de26035

📥 Commits

Reviewing files that changed from the base of the PR and between 0b5a819 and f680120.

📒 Files selected for processing (32)
  • common/constants.go
  • controller/audit.go
  • controller/auth_flow_test.go
  • controller/misc.go
  • controller/oauth.go
  • controller/oauth_invitation_test.go
  • controller/option.go
  • controller/registration_matrix_test.go
  • controller/telegram.go
  • controller/telegram_registration_boundary_test.go
  • controller/telegram_test.go
  • controller/user.go
  • controller/wechat.go
  • docs/openapi/api.json
  • middleware/audit.go
  • model/auth_identity.go
  • model/auth_identity_migration.go
  • model/auth_identity_migration_test.go
  • model/auth_identity_test.go
  • model/custom_oauth_provider.go
  • model/external_identity_claim.go
  • model/external_identity_claim_test.go
  • model/invitation_code.go
  • model/invitation_code_test.go
  • model/main.go
  • model/option.go
  • model/task_cas_test.go
  • model/user.go
  • model/user_authentication_test.go
  • model/user_oauth_binding.go
  • router/api-router.go
  • router/invitation_permission_test.go
💤 Files with no reviewable changes (1)
  • controller/telegram_registration_boundary_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • common/constants.go
  • model/user_authentication_test.go
  • model/task_cas_test.go
  • controller/option.go
  • controller/misc.go
  • controller/audit.go
  • controller/user.go
  • controller/wechat.go
  • model/option.go
  • model/auth_identity_test.go

Comment thread model/auth_identity_migration.go Outdated
Comment thread model/auth_identity.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
web/src/i18n/locales/zh.json (1)

5224-5224: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid implying that every invitation code is single-use.

一次性邀请码” tells Chinese administrators that each code can be redeemed exactly once, while the feature supports configurable usage limits. Use wording such as “用于新用户注册的邀请码” unless all codes are guaranteed to be single-use.

🤖 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/src/i18n/locales/zh.json` at line 5224, Update the Chinese translation
for “Create one-time codes for new account registration” to avoid implying
single-use redemption; use wording equivalent to “用于新用户注册的邀请码” while preserving
the registration invitation-code meaning.
web/src/i18n/locales/fr.json (1)

5219-5269: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Preserve the “future” reference in the French validation message.

Line 5269 says the expiration date must be “later” but omits later than what, making the validation message incomplete. Use wording such as “La date d’expiration doit être dans le futur.”

Proposed wording
-    "Expiration time must be in the future": "La date d'expiration doit être ultérieure"
+    "Expiration time must be in the future": "La date d'expiration doit être dans le futur"
🤖 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/src/i18n/locales/fr.json` around lines 5219 - 5269, Update the French
translation for “Expiration time must be in the future” to explicitly state that
the expiration date must be in the future, preserving the intended future-time
reference in the validation message.
🤖 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 `@web/src/i18n/locales/fr.json`:
- Around line 5219-5269: Update the French translation for “Expiration time must
be in the future” to explicitly state that the expiration date must be in the
future, preserving the intended future-time reference in the validation message.

In `@web/src/i18n/locales/zh.json`:
- Line 5224: Update the Chinese translation for “Create one-time codes for new
account registration” to avoid implying single-use redemption; use wording
equivalent to “用于新用户注册的邀请码” while preserving the registration invitation-code
meaning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cce4fde3-e96d-4c37-833d-07da3350afb6

📥 Commits

Reviewing files that changed from the base of the PR and between f680120 and 8c29630.

📒 Files selected for processing (13)
  • controller/user.go
  • model/auth_identity.go
  • model/auth_identity_migration.go
  • model/auth_identity_migration_test.go
  • model/auth_identity_test.go
  • model/task_cas_test.go
  • web/src/i18n/locales/en.json
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/ru.json
  • web/src/i18n/locales/vi.json
  • web/src/i18n/locales/zh-TW.json
  • web/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • model/task_cas_test.go
  • model/auth_identity_migration_test.go
  • model/auth_identity_migration.go
  • controller/user.go
  • model/auth_identity_test.go
  • model/auth_identity.go

@fengzhiyushui

Copy link
Copy Markdown
Author

Review follow-up for the outside-diff localization notes:

  • Clarified the French future-time validation message in 16e8ec77.
  • Kept the Chinese “一次性邀请码” wording intentionally. Each invitation
    code is atomically transitioned to the used state once, stores one
    used_user_id / used_time, and the concurrency tests guarantee a
    single successful consumer. The count parameter controls how many
    codes are generated, not how many times one code may be redeemed.

Resolve integration conflicts in:
- controller/user.go
- model/main.go
- model/option.go

Preserve invitation registration semantics while incorporating the
latest upstream DTO, GORM configuration, and option validation changes.
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.

[Feature Request] 支持可选的邀请码注册机制

1 participant