fix: refactor admin router (#1982)

This commit is contained in:
David Nguyen
2025-08-25 08:23:48 +10:00
committed by GitHub
parent d7e5a9eec7
commit 5a5bfe6e34
29 changed files with 353 additions and 255 deletions

View File

@ -34,7 +34,7 @@ export const AdminDocumentDeleteDialog = ({ document }: AdminDocumentDeleteDialo
const [reason, setReason] = useState(''); const [reason, setReason] = useState('');
const { mutateAsync: deleteDocument, isPending: isDeletingDocument } = const { mutateAsync: deleteDocument, isPending: isDeletingDocument } =
trpc.admin.deleteDocument.useMutation(); trpc.admin.document.delete.useMutation();
const handleDeleteDocument = async () => { const handleDeleteDocument = async () => {
try { try {

View File

@ -35,7 +35,7 @@ export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialog
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const { mutateAsync: deleteUser, isPending: isDeletingUser } = const { mutateAsync: deleteUser, isPending: isDeletingUser } =
trpc.admin.deleteUser.useMutation(); trpc.admin.user.delete.useMutation();
const onDeleteAccount = async () => { const onDeleteAccount = async () => {
try { try {

View File

@ -37,7 +37,7 @@ export const AdminUserDisableDialog = ({
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const { mutateAsync: disableUser, isPending: isDisablingUser } = const { mutateAsync: disableUser, isPending: isDisablingUser } =
trpc.admin.disableUser.useMutation(); trpc.admin.user.disable.useMutation();
const onDisableAccount = async () => { const onDisableAccount = async () => {
try { try {

View File

@ -34,7 +34,7 @@ export const AdminUserEnableDialog = ({ className, userToEnable }: AdminUserEnab
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const { mutateAsync: enableUser, isPending: isEnablingUser } = const { mutateAsync: enableUser, isPending: isEnablingUser } =
trpc.admin.enableUser.useMutation(); trpc.admin.user.enable.useMutation();
const onEnableAccount = async () => { const onEnableAccount = async () => {
try { try {

View File

@ -52,7 +52,7 @@ export const AdminDocumentRecipientItemTable = ({ recipient }: RecipientItemProp
}, },
}); });
const { mutateAsync: updateRecipient } = trpc.admin.updateRecipient.useMutation(); const { mutateAsync: updateRecipient } = trpc.admin.recipient.update.useMutation();
const columns = useMemo(() => { const columns = useMemo(() => {
return [ return [

View File

@ -48,7 +48,7 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
const { toast } = useToast(); const { toast } = useToast();
const { mutate: resealDocument, isPending: isResealDocumentLoading } = const { mutate: resealDocument, isPending: isResealDocumentLoading } =
trpc.admin.resealDocument.useMutation({ trpc.admin.document.reseal.useMutation({
onSuccess: () => { onSuccess: () => {
toast({ toast({
title: _(msg`Success`), title: _(msg`Success`),

View File

@ -33,7 +33,7 @@ export default function AdminDocumentsPage() {
const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined; const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined;
const { data: findDocumentsData, isPending: isFindDocumentsLoading } = const { data: findDocumentsData, isPending: isFindDocumentsLoading } =
trpc.admin.findDocuments.useQuery( trpc.admin.document.find.useQuery(
{ {
query: debouncedTerm, query: debouncedTerm,
page: page || 1, page: page || 1,

View File

@ -9,7 +9,7 @@ import { Link } from 'react-router';
import type { z } from 'zod'; import type { z } from 'zod';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { ZAdminUpdateProfileMutationSchema } from '@documenso/trpc/server/admin-router/schema'; import { ZUpdateUserRequestSchema } from '@documenso/trpc/server/admin-router/update-user.types';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { import {
Form, Form,
@ -33,7 +33,7 @@ import { AdminOrganisationsTable } from '~/components/tables/admin-organisations
import { MultiSelectRoleCombobox } from '../../../components/general/multiselect-role-combobox'; import { MultiSelectRoleCombobox } from '../../../components/general/multiselect-role-combobox';
const ZUserFormSchema = ZAdminUpdateProfileMutationSchema.omit({ id: true }); const ZUserFormSchema = ZUpdateUserRequestSchema.omit({ id: true });
type TUserFormSchema = z.infer<typeof ZUserFormSchema>; type TUserFormSchema = z.infer<typeof ZUserFormSchema>;
@ -85,7 +85,7 @@ const AdminUserPage = ({ user }: { user: User }) => {
const roles = user.roles ?? []; const roles = user.roles ?? [];
const { mutateAsync: updateUserMutation } = trpc.admin.updateUser.useMutation(); const { mutateAsync: updateUserMutation } = trpc.admin.user.update.useMutation();
const form = useForm<TUserFormSchema>({ const form = useForm<TUserFormSchema>({
resolver: zodResolver(ZUserFormSchema), resolver: zodResolver(ZUserFormSchema),

View File

@ -16,7 +16,7 @@ export const updateUser = async ({ id, name, email, roles }: UpdateUserOptions)
}, },
}); });
return await prisma.user.update({ await prisma.user.update({
where: { where: {
id, id,
}, },

View File

@ -0,0 +1,28 @@
import { sendDeleteEmail } from '@documenso/lib/server-only/document/send-delete-email';
import { superDeleteDocument } from '@documenso/lib/server-only/document/super-delete-document';
import { adminProcedure } from '../trpc';
import {
ZDeleteDocumentRequestSchema,
ZDeleteDocumentResponseSchema,
} from './delete-document.types';
export const deleteDocumentRoute = adminProcedure
.input(ZDeleteDocumentRequestSchema)
.output(ZDeleteDocumentResponseSchema)
.mutation(async ({ ctx, input }) => {
const { id, reason } = input;
ctx.logger.info({
input: {
id,
},
});
await sendDeleteEmail({ documentId: id, reason });
await superDeleteDocument({
id,
requestMetadata: ctx.metadata.requestMetadata,
});
});

View File

@ -0,0 +1,11 @@
import { z } from 'zod';
export const ZDeleteDocumentRequestSchema = z.object({
id: z.number().min(1),
reason: z.string(),
});
export const ZDeleteDocumentResponseSchema = z.void();
export type TDeleteDocumentRequest = z.infer<typeof ZDeleteDocumentRequestSchema>;
export type TDeleteDocumentResponse = z.infer<typeof ZDeleteDocumentResponseSchema>;

View File

@ -0,0 +1,19 @@
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { adminProcedure } from '../trpc';
import { ZDeleteUserRequestSchema, ZDeleteUserResponseSchema } from './delete-user.types';
export const deleteUserRoute = adminProcedure
.input(ZDeleteUserRequestSchema)
.output(ZDeleteUserResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
await deleteUser({ id });
});

View File

@ -0,0 +1,10 @@
import { z } from 'zod';
export const ZDeleteUserRequestSchema = z.object({
id: z.number().min(1),
});
export const ZDeleteUserResponseSchema = z.void();
export type TDeleteUserRequest = z.infer<typeof ZDeleteUserRequestSchema>;
export type TDeleteUserResponse = z.infer<typeof ZDeleteUserResponseSchema>;

View File

@ -0,0 +1,29 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { disableUser } from '@documenso/lib/server-only/user/disable-user';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { adminProcedure } from '../trpc';
import { ZDisableUserRequestSchema, ZDisableUserResponseSchema } from './disable-user.types';
export const disableUserRoute = adminProcedure
.input(ZDisableUserRequestSchema)
.output(ZDisableUserResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const user = await getUserById({ id }).catch(() => null);
if (!user) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'User not found',
});
}
await disableUser({ id });
});

View File

@ -0,0 +1,10 @@
import { z } from 'zod';
export const ZDisableUserRequestSchema = z.object({
id: z.number().min(1),
});
export const ZDisableUserResponseSchema = z.void();
export type TDisableUserRequest = z.infer<typeof ZDisableUserRequestSchema>;
export type TDisableUserResponse = z.infer<typeof ZDisableUserResponseSchema>;

View File

@ -0,0 +1,29 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { enableUser } from '@documenso/lib/server-only/user/enable-user';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { adminProcedure } from '../trpc';
import { ZEnableUserRequestSchema, ZEnableUserResponseSchema } from './enable-user.types';
export const enableUserRoute = adminProcedure
.input(ZEnableUserRequestSchema)
.output(ZEnableUserResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const user = await getUserById({ id }).catch(() => null);
if (!user) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'User not found',
});
}
await enableUser({ id });
});

View File

@ -0,0 +1,10 @@
import { z } from 'zod';
export const ZEnableUserRequestSchema = z.object({
id: z.number().min(1),
});
export const ZEnableUserResponseSchema = z.void();
export type TEnableUserRequest = z.infer<typeof ZEnableUserRequestSchema>;
export type TEnableUserResponse = z.infer<typeof ZEnableUserResponseSchema>;

View File

@ -0,0 +1,13 @@
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
import { adminProcedure } from '../trpc';
import { ZFindDocumentsRequestSchema, ZFindDocumentsResponseSchema } from './find-documents.types';
export const findDocumentsRoute = adminProcedure
.input(ZFindDocumentsRequestSchema)
.output(ZFindDocumentsResponseSchema)
.query(async ({ input }) => {
const { query, page, perPage } = input;
return await findDocuments({ query, page, perPage });
});

View File

@ -0,0 +1,17 @@
import { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
export const ZFindDocumentsRequestSchema = ZFindSearchParamsSchema.extend({
perPage: z.number().optional().default(20),
});
export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
data: ZDocumentManySchema.omit({
team: true,
}).array(),
});
export type TFindDocumentsRequest = z.infer<typeof ZFindDocumentsRequestSchema>;
export type TFindDocumentsResponse = z.infer<typeof ZFindDocumentsResponseSchema>;

View File

@ -0,0 +1,28 @@
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
import { sealDocument } from '@documenso/lib/server-only/document/seal-document';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { adminProcedure } from '../trpc';
import {
ZResealDocumentRequestSchema,
ZResealDocumentResponseSchema,
} from './reseal-document.types';
export const resealDocumentRoute = adminProcedure
.input(ZResealDocumentRequestSchema)
.output(ZResealDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const document = await getEntireDocument({ id });
const isResealing = isDocumentCompleted(document.status);
await sealDocument({ documentId: id, isResealing });
});

View File

@ -0,0 +1,10 @@
import { z } from 'zod';
export const ZResealDocumentRequestSchema = z.object({
id: z.number().min(1),
});
export const ZResealDocumentResponseSchema = z.void();
export type TResealDocumentRequest = z.infer<typeof ZResealDocumentRequestSchema>;
export type TResealDocumentResponse = z.infer<typeof ZResealDocumentResponseSchema>;

View File

@ -1,40 +1,23 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { router } from '../trpc';
import { findDocuments } from '@documenso/lib/server-only/admin/get-all-documents';
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
import { updateRecipient } from '@documenso/lib/server-only/admin/update-recipient';
import { updateUser } from '@documenso/lib/server-only/admin/update-user';
import { sealDocument } from '@documenso/lib/server-only/document/seal-document';
import { sendDeleteEmail } from '@documenso/lib/server-only/document/send-delete-email';
import { superDeleteDocument } from '@documenso/lib/server-only/document/super-delete-document';
import { upsertSiteSetting } from '@documenso/lib/server-only/site-settings/upsert-site-setting';
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { disableUser } from '@documenso/lib/server-only/user/disable-user';
import { enableUser } from '@documenso/lib/server-only/user/enable-user';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { adminProcedure, router } from '../trpc';
import { createAdminOrganisationRoute } from './create-admin-organisation'; import { createAdminOrganisationRoute } from './create-admin-organisation';
import { createStripeCustomerRoute } from './create-stripe-customer'; import { createStripeCustomerRoute } from './create-stripe-customer';
import { createSubscriptionClaimRoute } from './create-subscription-claim'; import { createSubscriptionClaimRoute } from './create-subscription-claim';
import { deleteDocumentRoute } from './delete-document';
import { deleteSubscriptionClaimRoute } from './delete-subscription-claim'; import { deleteSubscriptionClaimRoute } from './delete-subscription-claim';
import { deleteUserRoute } from './delete-user';
import { disableUserRoute } from './disable-user';
import { enableUserRoute } from './enable-user';
import { findAdminOrganisationsRoute } from './find-admin-organisations'; import { findAdminOrganisationsRoute } from './find-admin-organisations';
import { findDocumentsRoute } from './find-documents';
import { findSubscriptionClaimsRoute } from './find-subscription-claims'; import { findSubscriptionClaimsRoute } from './find-subscription-claims';
import { getAdminOrganisationRoute } from './get-admin-organisation'; import { getAdminOrganisationRoute } from './get-admin-organisation';
import { resealDocumentRoute } from './reseal-document';
import { resetTwoFactorRoute } from './reset-two-factor-authentication'; import { resetTwoFactorRoute } from './reset-two-factor-authentication';
import {
ZAdminDeleteDocumentMutationSchema,
ZAdminDeleteUserMutationSchema,
ZAdminDisableUserMutationSchema,
ZAdminEnableUserMutationSchema,
ZAdminFindDocumentsQuerySchema,
ZAdminResealDocumentMutationSchema,
ZAdminUpdateProfileMutationSchema,
ZAdminUpdateRecipientMutationSchema,
ZAdminUpdateSiteSettingMutationSchema,
} from './schema';
import { updateAdminOrganisationRoute } from './update-admin-organisation'; import { updateAdminOrganisationRoute } from './update-admin-organisation';
import { updateRecipientRoute } from './update-recipient';
import { updateSiteSettingRoute } from './update-site-setting';
import { updateSubscriptionClaimRoute } from './update-subscription-claim'; import { updateSubscriptionClaimRoute } from './update-subscription-claim';
import { updateUserRoute } from './update-user';
export const adminRouter = router({ export const adminRouter = router({
organisation: { organisation: {
@ -53,156 +36,19 @@ export const adminRouter = router({
createCustomer: createStripeCustomerRoute, createCustomer: createStripeCustomerRoute,
}, },
user: { user: {
update: updateUserRoute,
delete: deleteUserRoute,
enable: enableUserRoute,
disable: disableUserRoute,
resetTwoFactor: resetTwoFactorRoute, resetTwoFactor: resetTwoFactorRoute,
}, },
document: {
// Todo: migrate old routes find: findDocumentsRoute,
findDocuments: adminProcedure.input(ZAdminFindDocumentsQuerySchema).query(async ({ input }) => { delete: deleteDocumentRoute,
const { query, page, perPage } = input; reseal: resealDocumentRoute,
return await findDocuments({ query, page, perPage });
}),
updateUser: adminProcedure
.input(ZAdminUpdateProfileMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id, name, email, roles } = input;
ctx.logger.info({
input: {
id,
roles,
}, },
}); recipient: {
update: updateRecipientRoute,
return await updateUser({ id, name, email, roles });
}),
updateRecipient: adminProcedure
.input(ZAdminUpdateRecipientMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id, name, email } = input;
ctx.logger.info({
input: {
id,
}, },
}); updateSiteSetting: updateSiteSettingRoute,
return await updateRecipient({ id, name, email });
}),
updateSiteSetting: adminProcedure
.input(ZAdminUpdateSiteSettingMutationSchema)
.mutation(async ({ ctx, input }) => {
const { id, enabled, data } = input;
ctx.logger.info({
input: {
id,
},
});
return await upsertSiteSetting({
id,
enabled,
data,
userId: ctx.user.id,
});
}),
resealDocument: adminProcedure
.input(ZAdminResealDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const document = await getEntireDocument({ id });
const isResealing = isDocumentCompleted(document.status);
return await sealDocument({ documentId: id, isResealing });
}),
enableUser: adminProcedure
.input(ZAdminEnableUserMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const user = await getUserById({ id }).catch(() => null);
if (!user) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'User not found',
});
}
return await enableUser({ id });
}),
disableUser: adminProcedure
.input(ZAdminDisableUserMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
const user = await getUserById({ id }).catch(() => null);
if (!user) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'User not found',
});
}
return await disableUser({ id });
}),
deleteUser: adminProcedure
.input(ZAdminDeleteUserMutationSchema)
.mutation(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
return await deleteUser({ id });
}),
deleteDocument: adminProcedure
.input(ZAdminDeleteDocumentMutationSchema)
.mutation(async ({ ctx, input }) => {
const { id, reason } = input;
ctx.logger.info({
input: {
id,
},
});
await sendDeleteEmail({ documentId: id, reason });
return await superDeleteDocument({
id,
requestMetadata: ctx.metadata.requestMetadata,
});
}),
}); });

View File

@ -1,67 +0,0 @@
import { Role } from '@prisma/client';
import z from 'zod';
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
export const ZAdminFindDocumentsQuerySchema = ZFindSearchParamsSchema.extend({
perPage: z.number().optional().default(20),
});
export type TAdminFindDocumentsQuerySchema = z.infer<typeof ZAdminFindDocumentsQuerySchema>;
export const ZAdminUpdateProfileMutationSchema = z.object({
id: z.number().min(1),
name: z.string().nullish(),
email: z.string().email().optional(),
roles: z.array(z.nativeEnum(Role)).optional(),
});
export type TAdminUpdateProfileMutationSchema = z.infer<typeof ZAdminUpdateProfileMutationSchema>;
export const ZAdminUpdateRecipientMutationSchema = z.object({
id: z.number().min(1),
name: z.string().optional(),
email: z.string().email().optional(),
});
export type TAdminUpdateRecipientMutationSchema = z.infer<
typeof ZAdminUpdateRecipientMutationSchema
>;
export const ZAdminUpdateSiteSettingMutationSchema = ZSiteSettingSchema;
export type TAdminUpdateSiteSettingMutationSchema = z.infer<
typeof ZAdminUpdateSiteSettingMutationSchema
>;
export const ZAdminResealDocumentMutationSchema = z.object({
id: z.number().min(1),
});
export type TAdminResealDocumentMutationSchema = z.infer<typeof ZAdminResealDocumentMutationSchema>;
export const ZAdminDeleteUserMutationSchema = z.object({
id: z.number().min(1),
});
export type TAdminDeleteUserMutationSchema = z.infer<typeof ZAdminDeleteUserMutationSchema>;
export const ZAdminEnableUserMutationSchema = z.object({
id: z.number().min(1),
});
export type TAdminEnableUserMutationSchema = z.infer<typeof ZAdminEnableUserMutationSchema>;
export const ZAdminDisableUserMutationSchema = z.object({
id: z.number().min(1),
});
export type TAdminDisableUserMutationSchema = z.infer<typeof ZAdminDisableUserMutationSchema>;
export const ZAdminDeleteDocumentMutationSchema = z.object({
id: z.number().min(1),
reason: z.string(),
});
export type TAdminDeleteDocomentMutationSchema = z.infer<typeof ZAdminDeleteDocumentMutationSchema>;

View File

@ -0,0 +1,22 @@
import { updateRecipient } from '@documenso/lib/server-only/admin/update-recipient';
import { adminProcedure } from '../trpc';
import {
ZUpdateRecipientRequestSchema,
ZUpdateRecipientResponseSchema,
} from './update-recipient.types';
export const updateRecipientRoute = adminProcedure
.input(ZUpdateRecipientRequestSchema)
.output(ZUpdateRecipientResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id, name, email } = input;
ctx.logger.info({
input: {
id,
},
});
await updateRecipient({ id, name, email });
});

View File

@ -0,0 +1,12 @@
import { z } from 'zod';
export const ZUpdateRecipientRequestSchema = z.object({
id: z.number().min(1),
name: z.string().optional(),
email: z.string().email().optional(),
});
export const ZUpdateRecipientResponseSchema = z.void();
export type TUpdateRecipientRequest = z.infer<typeof ZUpdateRecipientRequestSchema>;
export type TUpdateRecipientResponse = z.infer<typeof ZUpdateRecipientResponseSchema>;

View File

@ -0,0 +1,27 @@
import { upsertSiteSetting } from '@documenso/lib/server-only/site-settings/upsert-site-setting';
import { adminProcedure } from '../trpc';
import {
ZUpdateSiteSettingRequestSchema,
ZUpdateSiteSettingResponseSchema,
} from './update-site-setting.types';
export const updateSiteSettingRoute = adminProcedure
.input(ZUpdateSiteSettingRequestSchema)
.output(ZUpdateSiteSettingResponseSchema)
.mutation(async ({ ctx, input }) => {
const { id, enabled, data } = input;
ctx.logger.info({
input: {
id,
},
});
await upsertSiteSetting({
id,
enabled,
data,
userId: ctx.user.id,
});
});

View File

@ -0,0 +1,10 @@
import { z } from 'zod';
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
export const ZUpdateSiteSettingRequestSchema = ZSiteSettingSchema;
export const ZUpdateSiteSettingResponseSchema = z.void();
export type TUpdateSiteSettingRequest = z.infer<typeof ZUpdateSiteSettingRequestSchema>;
export type TUpdateSiteSettingResponse = z.infer<typeof ZUpdateSiteSettingResponseSchema>;

View File

@ -0,0 +1,20 @@
import { updateUser } from '@documenso/lib/server-only/admin/update-user';
import { adminProcedure } from '../trpc';
import { ZUpdateUserRequestSchema, ZUpdateUserResponseSchema } from './update-user.types';
export const updateUserRoute = adminProcedure
.input(ZUpdateUserRequestSchema)
.output(ZUpdateUserResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id, name, email, roles } = input;
ctx.logger.info({
input: {
id,
roles,
},
});
await updateUser({ id, name, email, roles });
});

View File

@ -0,0 +1,14 @@
import { Role } from '@prisma/client';
import { z } from 'zod';
export const ZUpdateUserRequestSchema = z.object({
id: z.number().min(1),
name: z.string().nullish(),
email: z.string().email().optional(),
roles: z.array(z.nativeEnum(Role)).optional(),
});
export const ZUpdateUserResponseSchema = z.void();
export type TUpdateUserRequest = z.infer<typeof ZUpdateUserRequestSchema>;
export type TUpdateUserResponse = z.infer<typeof ZUpdateUserResponseSchema>;