-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Update domain/link transfer modals & logic #2995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughBackend and frontend transfer flows were simplified and hardened: transfers now validate domains/workspaces, reset per-link metrics and folder/tags on move, remove analytics/usage adjustments, and modals were refactored to use WorkspaceSelector/Combobox with verification steps; the InputSelect component was removed from the UI package. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant Modal as Transfer Modal (UI)
participant API as Transfer API Route
participant DB as Database
User->>Modal: Select workspace + enter verification
Modal->>API: POST /api/domains/[domain]/transfer (newWorkspaceId)
API->>API: Validate target workspace (not same)
API->>DB: Transfer domain ownership
API->>DB: For each moved link -- reset metrics, null timestamps, clear folderId
API-->>Modal: 200 OK
Modal->>User: Trigger onSuccess, refresh domains, close
sequenceDiagram
autonumber
participant User
participant Modal as Transfer Link Modal (UI)
participant API as Link Transfer Route
participant DB as Database
User->>Modal: Select target workspace + enter "confirm transfer link"
Modal->>API: POST /api/links/{linkId}/transfer (newWorkspaceId)
API->>API: Check isDubDomain(link.domain)
alt non-Dub domain or same-workspace
API-->>Modal: 400 bad_request (reject)
Modal->>User: Show error toast
else valid
API->>DB: Move link to new workspace
API->>DB: Reset stats (clicks/leads/sales/conversions), null timestamps, clear tags & folderId
API-->>Modal: 200 OK
Modal->>User: Close modal, refresh links
end
Estimated code review effortπ― 4 (Complex) | β±οΈ ~45 minutes Rationale: changes span API and UI layers, include deletion of a large UI component (InputSelect) which affects exports and multiple modals, add validation and repeated metric-reset logic, and introduce new modal structures β review requires checking backend correctness, UI replacements, and export surface impact. Possibly related PRs
Poem
Pre-merge checks and finishing touchesβ Failed checks (1 warning)
β Passed checks (2 passed)
β¨ Finishing touches
π§ͺ Generate unit tests (beta)
π Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro π Files selected for processing (3)
π€ Files with no reviewable changes (2)
𧰠Additional context used𧬠Code graph analysis (1)apps/web/ui/modals/send-test-webhook-modal.tsx (2)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
π Additional comments (1)
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. Comment |
There was a problem hiding this comment.
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)
apps/web/app/(ee)/api/cron/domains/transfer/route.ts (1)
118-121: Harden error logging for non-Error throwables.
errorisunknown;error.messagecan throw. Guard it.- await log({ - message: `Error transferring domain: ${error.message}`, + const msg = + error instanceof Error ? error.message : JSON.stringify(error); + await log({ + message: `Error transferring domain: ${msg}`, type: "cron", });
π§Ή Nitpick comments (5)
apps/web/app/(ee)/api/cron/domains/transfer/route.ts (1)
29-36: Optional: fetch only needed fields to reduce payload.
findManypulls full rows; you only useid(and fields mirrored in cache/analytics). Selecting minimal fields lowers memory and network I/O.- const links = await prisma.link.findMany({ + const links = await prisma.link.findMany({ where: { domain, projectId: currentWorkspaceId }, take: 100, orderBy: { createdAt: "desc", }, + select: { + id: true, + domain: true, + key: true, + url: true, + projectId: true, + folderId: true, + clicks: true, + leads: true, + sales: true, + saleAmount: true, + conversions: true, + lastClicked: true, + lastLeadAt: true, + lastConversionAt: true, + }, });apps/web/app/api/links/[linkId]/transfer/route.ts (1)
55-69: Destination role check (optional but safer).You fetch
rolebut only validate membership. If viewers exist, consider requiring a role with write privileges.- if (!newWorkspace || newWorkspace.users.length === 0) { + const member = newWorkspace?.users[0]; + if (!newWorkspace || !member) { throw new DubApiError({ code: "not_found", message: "New workspace not found.", }); } + if (member.role === "viewer") { + throw new DubApiError({ + code: "forbidden", + message: + "You don't have permission to add links in the destination workspace.", + }); + }apps/web/app/api/domains/[domain]/transfer/route.ts (1)
105-115: Optional: preflight for destination domain conflicts and wrap in try/catch to tailor errors.While unlikely, defensive checks can turn a DB unique error into a user-friendly response.
- const domainResponse = await prisma.domain.update({ + const existsInDest = newWorkspace.domains.some((d) => d.slug === domain); + if (existsInDest) { + throw new DubApiError({ + code: "conflict", + message: `Workspace ${newWorkspace.name} already has this domain.`, + }); + } + const domainResponse = await prisma.domain.update({ where: { slug: domain, projectId: workspace.id }, data: { projectId: newWorkspaceId, primary: newWorkspace.domains.length === 0, }, include: { registeredDomain: true, }, });apps/web/ui/modals/transfer-domain-modal.tsx (1)
182-191: Optional: includepropsandonSuccessin hook deps to avoid stale closures.Keeps the modal in sync if these change.
- }, [showTransferDomainModal, setShowTransferDomainModal]); + }, [showTransferDomainModal, setShowTransferDomainModal, props, onSuccess]);apps/web/ui/modals/transfer-link-modal.tsx (1)
174-184: Optional: includepropsandonSuccessin hook deps to avoid stale closures.Matches the domain modal hook tweak.
- }, [showTransferLinkModal, setShowTransferLinkModal]); + }, [showTransferLinkModal, setShowTransferLinkModal, props, onSuccess]);
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (5)
apps/web/app/(ee)/api/cron/domains/transfer/route.ts(1 hunks)apps/web/app/api/domains/[domain]/transfer/route.ts(2 hunks)apps/web/app/api/links/[linkId]/transfer/route.ts(4 hunks)apps/web/ui/modals/transfer-domain-modal.tsx(4 hunks)apps/web/ui/modals/transfer-link-modal.tsx(3 hunks)
π§° Additional context used
π§ Learnings (1)
π Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
PR: dubinc/dub#2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.
Applied to files:
apps/web/ui/modals/transfer-link-modal.tsx
𧬠Code graph analysis (2)
apps/web/ui/modals/transfer-domain-modal.tsx (1)
apps/web/lib/types.ts (1)
DomainProps(258-274)
apps/web/app/api/links/[linkId]/transfer/route.ts (1)
apps/web/lib/api/errors.ts (1)
DubApiError(75-92)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
π Additional comments (4)
apps/web/app/api/links/[linkId]/transfer/route.ts (3)
29-35: Domain guard reads well.Clear message and correct error code for non-default-domain links.
48-53: Self-transfer guard is correct.Prevents no-op transfers with a precise error.
85-105: Remove preflight check for domain+key collision
The Prisma Link model defines no@@unique([projectId, domain, key]), so updatingprojectIdcannot violate a non-existent database constraint.Likely an incorrect or invalid review comment.
apps/web/app/api/domains/[domain]/transfer/route.ts (1)
33-35: Error text improvement LGTM.Clearer message for same-workspace transfers.
Summary by CodeRabbit
New Features
Bug Fixes
Chores