chore: refactor routes (#1992)

This commit is contained in:
David Nguyen
2025-08-25 21:00:35 +10:00
committed by GitHub
parent 7eb882aea8
commit 44f5da95b3
16 changed files with 89 additions and 43 deletions

View File

@ -3,12 +3,12 @@ import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { useNavigate } from 'react-router';
import { match } from 'ts-pattern';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import type { TGetUserResponse } from '@documenso/trpc/server/admin-router/get-user.types';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import {
@ -25,7 +25,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type AdminUserDeleteDialogProps = {
className?: string;
user: User;
user: TGetUserResponse;
};
export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialogProps) => {

View File

@ -3,11 +3,11 @@ import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { match } from 'ts-pattern';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import type { TGetUserResponse } from '@documenso/trpc/server/admin-router/get-user.types';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import {
@ -24,7 +24,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type AdminUserDisableDialogProps = {
className?: string;
userToDisable: User;
userToDisable: TGetUserResponse;
};
export const AdminUserDisableDialog = ({

View File

@ -3,11 +3,11 @@ import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { match } from 'ts-pattern';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import type { TGetUserResponse } from '@documenso/trpc/server/admin-router/get-user.types';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import {
@ -24,7 +24,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type AdminUserEnableDialogProps = {
className?: string;
userToEnable: User;
userToEnable: TGetUserResponse;
};
export const AdminUserEnableDialog = ({ className, userToEnable }: AdminUserEnableDialogProps) => {

View File

@ -3,12 +3,12 @@ import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { useRevalidator } from 'react-router';
import { match } from 'ts-pattern';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import type { TGetUserResponse } from '@documenso/trpc/server/admin-router/get-user.types';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import {
@ -25,7 +25,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
export type AdminUserResetTwoFactorDialogProps = {
className?: string;
user: User;
user: TGetUserResponse;
};
export const AdminUserResetTwoFactorDialog = ({

View File

@ -2,13 +2,13 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { User } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { useRevalidator } from 'react-router';
import { Link } from 'react-router';
import type { z } from 'zod';
import { trpc } from '@documenso/trpc/react';
import type { TGetUserResponse } from '@documenso/trpc/server/admin-router/get-user.types';
import { ZUpdateUserRequestSchema } from '@documenso/trpc/server/admin-router/update-user.types';
import { Button } from '@documenso/ui/primitives/button';
import {
@ -38,7 +38,7 @@ const ZUserFormSchema = ZUpdateUserRequestSchema.omit({ id: true });
type TUserFormSchema = z.infer<typeof ZUserFormSchema>;
export default function UserPage({ params }: { params: { id: number } }) {
const { data: user, isLoading: isLoadingUser } = trpc.profile.getUser.useQuery(
const { data: user, isLoading: isLoadingUser } = trpc.admin.user.get.useQuery(
{
id: Number(params.id),
},
@ -78,7 +78,7 @@ export default function UserPage({ params }: { params: { id: number } }) {
return <AdminUserPage user={user} />;
}
const AdminUserPage = ({ user }: { user: User }) => {
const AdminUserPage = ({ user }: { user: TGetUserResponse }) => {
const { _ } = useLingui();
const { toast } = useToast();
const { revalidate } = useRevalidator();

View File

@ -45,6 +45,9 @@ export async function loader({ params, request }: Route.LoaderArgs) {
mode: 'insensitive',
},
},
select: {
id: true,
},
});
// Directly convert the team member invite to a team member if they already have an account.

View File

@ -111,6 +111,10 @@ export const handleOAuthCallbackUrl = async (options: HandleOAuthCallbackUrlOpti
where: {
email: email,
},
select: {
id: true,
emailVerified: true,
},
});
// Handle existing user but no account.

View File

@ -42,6 +42,11 @@ export const run = async ({
where: {
id: userId,
},
select: {
id: true,
email: true,
name: true,
},
}),
prisma.document.findFirstOrThrow({
where: {

View File

@ -10,12 +10,6 @@ export type UpdateUserOptions = {
};
export const updateUser = async ({ id, name, email, roles }: UpdateUserOptions) => {
await prisma.user.findFirstOrThrow({
where: {
id,
},
});
await prisma.user.update({
where: {
id,

View File

@ -1,13 +1,31 @@
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
export interface GetUserByIdOptions {
id: number;
}
export const getUserById = async ({ id }: GetUserByIdOptions) => {
return await prisma.user.findFirstOrThrow({
const user = await prisma.user.findFirst({
where: {
id,
},
select: {
id: true,
name: true,
email: true,
emailVerified: true,
roles: true,
disabled: true,
twoFactorEnabled: true,
signature: true,
},
});
if (!user) {
throw new AppError(AppErrorCode.NOT_FOUND);
}
return user;
};

View File

@ -24,7 +24,7 @@ export const updateProfile = async ({
},
});
return await prisma.$transaction(async (tx) => {
await prisma.$transaction(async (tx) => {
await tx.userSecurityAuditLog.create({
data: {
userId,
@ -34,7 +34,7 @@ export const updateProfile = async ({
},
});
return await tx.user.update({
await tx.user.update({
where: {
id: userId,
},

View File

@ -0,0 +1,19 @@
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { adminProcedure } from '../trpc';
import { ZGetUserRequestSchema, ZGetUserResponseSchema } from './get-user.types';
export const getUserRoute = adminProcedure
.input(ZGetUserRequestSchema)
.output(ZGetUserResponseSchema)
.query(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
return await getUserById({ id });
});

View File

@ -0,0 +1,21 @@
import { z } from 'zod';
import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
export const ZGetUserRequestSchema = z.object({
id: z.number().min(1),
});
export const ZGetUserResponseSchema = UserSchema.pick({
id: true,
name: true,
email: true,
emailVerified: true,
roles: true,
disabled: true,
twoFactorEnabled: true,
signature: true,
});
export type TGetUserRequest = z.infer<typeof ZGetUserRequestSchema>;
export type TGetUserResponse = z.infer<typeof ZGetUserResponseSchema>;

View File

@ -11,6 +11,7 @@ import { findAdminOrganisationsRoute } from './find-admin-organisations';
import { findDocumentsRoute } from './find-documents';
import { findSubscriptionClaimsRoute } from './find-subscription-claims';
import { getAdminOrganisationRoute } from './get-admin-organisation';
import { getUserRoute } from './get-user';
import { resealDocumentRoute } from './reseal-document';
import { resetTwoFactorRoute } from './reset-two-factor-authentication';
import { updateAdminOrganisationRoute } from './update-admin-organisation';
@ -36,6 +37,7 @@ export const adminRouter = router({
createCustomer: createStripeCustomerRoute,
},
user: {
get: getUserRoute,
update: updateUserRoute,
delete: deleteUserRoute,
enable: enableUserRoute,

View File

@ -3,14 +3,12 @@ import type { SetAvatarImageOptions } from '@documenso/lib/server-only/profile/s
import { setAvatarImage } from '@documenso/lib/server-only/profile/set-avatar-image';
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { submitSupportTicket } from '@documenso/lib/server-only/user/submit-support-ticket';
import { updateProfile } from '@documenso/lib/server-only/user/update-profile';
import { adminProcedure, authenticatedProcedure, router } from '../trpc';
import { authenticatedProcedure, router } from '../trpc';
import {
ZFindUserSecurityAuditLogsSchema,
ZRetrieveUserByIdQuerySchema,
ZSetProfileImageMutationSchema,
ZSubmitSupportTicketMutationSchema,
ZUpdateProfileMutationSchema,
@ -26,24 +24,12 @@ export const profileRouter = router({
});
}),
getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input, ctx }) => {
const { id } = input;
ctx.logger.info({
input: {
id,
},
});
return await getUserById({ id });
}),
updateProfile: authenticatedProcedure
.input(ZUpdateProfileMutationSchema)
.mutation(async ({ input, ctx }) => {
const { name, signature } = input;
return await updateProfile({
await updateProfile({
userId: ctx.user.id,
name,
signature,
@ -52,7 +38,7 @@ export const profileRouter = router({
}),
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
return await deleteUser({
await deleteUser({
id: ctx.user.id,
});
}),

View File

@ -7,12 +7,6 @@ export const ZFindUserSecurityAuditLogsSchema = z.object({
export type TFindUserSecurityAuditLogsSchema = z.infer<typeof ZFindUserSecurityAuditLogsSchema>;
export const ZRetrieveUserByIdQuerySchema = z.object({
id: z.number().min(1),
});
export type TRetrieveUserByIdQuerySchema = z.infer<typeof ZRetrieveUserByIdQuerySchema>;
export const ZUpdateProfileMutationSchema = z.object({
name: z.string().min(1),
signature: z.string(),