Files
calendar/packages/trpc/server/routers/viewer/admin/_router.ts
T
Hariom BalharaGitHubbot_apkDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
dc43eba23b feat: add Move Team to Organization admin migration page (#25067)
* feat: restore moveTeamToOrg admin endpoint for organization migration

- Add moveTeamToOrg and removeTeamFromOrg functions to orgMigration.ts
- Restore API endpoint at /api/orgMigration/moveTeamToOrg
- Restore admin UI page at /settings/admin/orgMigrations/moveTeamToOrg
- Add helper functions for team redirect management
- Support moving team members along with the team

This endpoint allows admins to migrate teams to organizations after org creation,
which is needed as a temporary solution until proper org admin permissions are implemented.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: move moveTeamToOrg to lib/orgMigration and fix redirect URL

- Move moveTeamToOrg and removeTeamFromOrg functions from playwright/lib to lib/orgMigration.ts
- Update API endpoint to import from lib/orgMigration instead of playwright/lib
- Fix redirect URL format: use / instead of /team/
- Fix import path: use ../playwright/lib/orgMigration instead of ./playwright/lib/orgMigration
- Rename unused _dbRemoveTeamFromOrg in playwright file to satisfy linter
- Remove duplicate functions from playwright/lib/orgMigration.ts

This fixes the Vercel deployment failure caused by importing from test-only directories
in production API routes, and corrects the redirect URL format to match the original implementation.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* refactor: reuse existing createTeamsHandler for moveTeamToOrg endpoint

- Remove custom orgMigration.ts implementation
- Update API endpoint to call existing createTeamsHandler with org owner impersonation
- Remove moveMembers option from UI (always moves members by design)
- Fix Vercel deployment by removing playwright import from production code
- Use OrganizationRepository.adminFindById to fetch org owner

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* refactor: migrate moveTeamToOrg admin page and API to App Router

- Move admin page from pages/settings/admin/orgMigrations to app/(use-page-wrapper)/settings/(admin-layout)/admin/orgMigrations
- Convert API route from pages/api/orgMigration/moveTeamToOrg.ts to app/api/orgMigration/moveTeamToOrg/route.ts
- Create client view component in modules/settings/admin/org-migrations/
- Remove old pages directory files and getServerSideProps

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: correct import paths for App Router compatibility

- Fix @calcom/lib/server to @calcom/lib/server/i18n for getTranslation
- Fix @calcom/ui barrel import to specific component paths

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: use TFunction type for getFormSchema parameter

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: use buildLegacyRequest for App Router session compatibility

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* Remove unused fn

* cleanup

* cleanup

* fix: ui

* fix: handle slug conflict error when moving team to organization

- Intercept Prisma P2002 unique constraint error when moving a team
- Convert to user-friendly CONFLICT error with clear message
- Add test case for slug conflict scenario

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: use isPending instead of isLoading for tRPC mutation

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fixes

* fix: remove PII (emails) from admin log statement

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* refactor: use instanceof pattern for Prisma error detection

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fixes

* fix: use i18n key for slug conflict error message instead of hardcoded English string

Co-Authored-By: bot_apk <apk@cognition.ai>

* fix: narrow P2002 catch scope to only prisma.team.update call

Separates the try-catch for prisma.team.update (slug conflict) from
creditService.moveCreditsFromTeamToOrg to avoid misattributing credit
service P2002 errors as slug conflicts.

Co-Authored-By: bot_apk <apk@cognition.ai>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
2026-03-19 09:55:02 +05:30

129 lines
6.2 KiB
TypeScript

import { authedAdminProcedure } from "../../../procedures/authedProcedure";
import { router } from "../../../trpc";
import { ZAdminAssignFeatureToTeamSchema } from "./assignFeatureToTeam.schema";
import { ZBillingPortalLinkSchema } from "./billingPortalLink.schema";
import { ZCreateCouponSchema } from "./createCoupon.schema";
import { ZCreateSelfHostedLicenseSchema } from "./createSelfHostedLicenseKey.schema";
import { ZAdminGetTeamsForFeatureSchema } from "./getTeamsForFeature.schema";
import { ZListMembersSchema } from "./listPaginated.schema";
import { ZAdminLockUserAccountSchema } from "./lockUserAccount.schema";
import { ZMoveTeamToOrgSchema } from "./moveTeamToOrg.schema";
import { ZAdminRemoveTwoFactor } from "./removeTwoFactor.schema";
import { ZResendPurchaseCompleteEmailSchema } from "./resendPurchaseCompleteEmail.schema";
import { ZAdminPasswordResetSchema } from "./sendPasswordReset.schema";
import { ZSetSMSLockState } from "./setSMSLockState.schema";
import { toggleFeatureFlag } from "./toggleFeatureFlag.procedure";
import { ZAdminUnassignFeatureFromTeamSchema } from "./unassignFeatureFromTeam.schema";
import { ZAdminVerifyWorkflowsSchema } from "./verifyWorkflows.schema";
import { watchlistRouter } from "./watchlist/_router";
import { ZWhitelistUserWorkflows } from "./whitelistUserWorkflows.schema";
import {
workspacePlatformCreateSchema,
workspacePlatformToggleEnabledSchema,
workspacePlatformUpdateSchema,
workspacePlatformUpdateServiceAccountSchema,
} from "./workspacePlatform/schema";
const NAMESPACE = "admin";
const namespaced = (s: string) => `${NAMESPACE}.${s}`;
export const adminRouter = router({
listPaginated: authedAdminProcedure.input(ZListMembersSchema).query(async (opts) => {
const { default: handler } = await import("./listPaginated.handler");
return handler(opts);
}),
sendPasswordReset: authedAdminProcedure.input(ZAdminPasswordResetSchema).mutation(async (opts) => {
const { default: handler } = await import("./sendPasswordReset.handler");
return handler(opts);
}),
lockUserAccount: authedAdminProcedure.input(ZAdminLockUserAccountSchema).mutation(async (opts) => {
const { default: handler } = await import("./lockUserAccount.handler");
return handler(opts);
}),
toggleFeatureFlag,
removeTwoFactor: authedAdminProcedure.input(ZAdminRemoveTwoFactor).mutation(async (opts) => {
const { default: handler } = await import("./removeTwoFactor.handler");
return handler(opts);
}),
getSMSLockStateTeamsUsers: authedAdminProcedure.query(async (opts) => {
const { default: handler } = await import("./getSMSLockStateTeamsUsers.handler");
return handler(opts);
}),
setSMSLockState: authedAdminProcedure.input(ZSetSMSLockState).mutation(async (opts) => {
const { default: handler } = await import("./setSMSLockState.handler");
return handler(opts);
}),
createSelfHostedLicense: authedAdminProcedure
.input(ZCreateSelfHostedLicenseSchema)
.mutation(async (opts) => {
const { default: handler } = await import("./createSelfHostedLicenseKey.handler");
return handler(opts);
}),
createCoupon: authedAdminProcedure.input(ZCreateCouponSchema).mutation(async (opts) => {
const { default: handler } = await import("./createCoupon.handler");
return handler(opts);
}),
resendPurchaseCompleteEmail: authedAdminProcedure
.input(ZResendPurchaseCompleteEmailSchema)
.mutation(async (opts) => {
const { default: handler } = await import("./resendPurchaseCompleteEmail.handler");
return handler(opts);
}),
billingPortalLink: authedAdminProcedure.input(ZBillingPortalLinkSchema).mutation(async (opts) => {
const { default: handler } = await import("./billingPortalLink.handler");
return handler(opts);
}),
verifyWorkflows: authedAdminProcedure.input(ZAdminVerifyWorkflowsSchema).mutation(async (opts) => {
const { default: handler } = await import("./verifyWorkflows.handler");
return handler(opts);
}),
whitelistUserWorkflows: authedAdminProcedure.input(ZWhitelistUserWorkflows).mutation(async (opts) => {
const { default: handler } = await import("./whitelistUserWorkflows.handler");
return handler(opts);
}),
getTeamsForFeature: authedAdminProcedure.input(ZAdminGetTeamsForFeatureSchema).query(async (opts) => {
const { default: handler } = await import("./getTeamsForFeature.handler");
return handler(opts);
}),
assignFeatureToTeam: authedAdminProcedure.input(ZAdminAssignFeatureToTeamSchema).mutation(async (opts) => {
const { default: handler } = await import("./assignFeatureToTeam.handler");
return handler(opts);
}),
unassignFeatureFromTeam: authedAdminProcedure
.input(ZAdminUnassignFeatureFromTeamSchema)
.mutation(async (opts) => {
const { default: handler } = await import("./unassignFeatureFromTeam.handler");
return handler(opts);
}),
moveTeamToOrg: authedAdminProcedure.input(ZMoveTeamToOrgSchema).mutation(async (opts) => {
const { default: handler } = await import("./moveTeamToOrg.handler");
return handler(opts);
}),
workspacePlatform: router({
list: authedAdminProcedure.query(async () => {
const { default: handler } = await import("./workspacePlatform/list.handler");
return handler();
}),
add: authedAdminProcedure.input(workspacePlatformCreateSchema).mutation(async (opts) => {
const { default: handler } = await import("./workspacePlatform/add.handler");
return handler(opts);
}),
update: authedAdminProcedure.input(workspacePlatformUpdateSchema).mutation(async (opts) => {
const { default: handler } = await import("./workspacePlatform/update.handler");
return handler(opts);
}),
updateServiceAccount: authedAdminProcedure
.input(workspacePlatformUpdateServiceAccountSchema)
.mutation(async (opts) => {
const { default: handler } = await import("./workspacePlatform/updateServiceAccount.handler");
return handler(opts);
}),
toggleEnabled: authedAdminProcedure.input(workspacePlatformToggleEnabledSchema).mutation(async (opts) => {
const { default: handler } = await import("./workspacePlatform/toggleEnabled.handler");
return handler(opts);
}),
}),
watchlist: watchlistRouter,
});