fix: refactor api routes

This commit is contained in:
David Nguyen
2024-12-30 21:01:03 +11:00
parent df33fbf91b
commit 22665543c0
100 changed files with 2268 additions and 2303 deletions

View File

@ -0,0 +1,9 @@
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { authenticatedProcedure } from '../trpc';
export const deleteAccountRoute = authenticatedProcedure.mutation(async ({ ctx }) => {
return await deleteUser({
id: ctx.user.id,
});
});

View File

@ -0,0 +1,19 @@
import { z } from 'zod';
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs';
import { authenticatedProcedure } from '../trpc';
export const ZFindUserSecurityAuditLogsRequestSchema = z.object({
page: z.number().optional(),
perPage: z.number().optional(),
});
export const findUserSecurityAuditLogsRoute = authenticatedProcedure
.input(ZFindUserSecurityAuditLogsRequestSchema)
.query(async ({ input, ctx }) => {
return await findUserSecurityAuditLogs({
userId: ctx.user.id,
...input,
});
});

View File

@ -0,0 +1,19 @@
import { z } from 'zod';
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password';
import { procedure } from '../trpc';
export const ZForgotPasswordRequestSchema = z.object({
email: z.string().email().min(1),
});
export const forgotPasswordRoute = procedure
.input(ZForgotPasswordRequestSchema)
.mutation(async ({ input }) => {
const { email } = input;
return await forgotPassword({
email,
});
});

View File

@ -0,0 +1,24 @@
import { z } from 'zod';
import { resetPassword } from '@documenso/lib/server-only/user/reset-password';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { ZPasswordSchema } from '../auth-router/schema';
import { procedure } from '../trpc';
export const ZResetPasswordRequestSchema = z.object({
password: ZPasswordSchema,
token: z.string().min(1),
});
export const resetPasswordRoute = procedure
.input(ZResetPasswordRequestSchema)
.mutation(async ({ input, ctx }) => {
const { password, token } = input;
return await resetPassword({
token,
password,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
});

View File

@ -1,152 +1,22 @@
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobsClient } from '@documenso/lib/jobs/client';
import { setAvatarImage } from '@documenso/lib/server-only/profile/set-avatar-image';
import { getSubscriptionsByUserId } from '@documenso/lib/server-only/subscription/get-subscriptions-by-user-id';
import { deleteUser } from '@documenso/lib/server-only/user/delete-user';
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs';
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { resetPassword } from '@documenso/lib/server-only/user/reset-password';
import { updatePassword } from '@documenso/lib/server-only/user/update-password';
import { updateProfile } from '@documenso/lib/server-only/user/update-profile';
import { updatePublicProfile } from '@documenso/lib/server-only/user/update-public-profile';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { SubscriptionStatus } from '@documenso/prisma/client';
import { adminProcedure, authenticatedProcedure, procedure, router } from '../trpc';
import {
ZConfirmEmailMutationSchema,
ZFindUserSecurityAuditLogsSchema,
ZForgotPasswordFormSchema,
ZResetPasswordFormSchema,
ZRetrieveUserByIdQuerySchema,
ZSetProfileImageMutationSchema,
ZUpdatePasswordMutationSchema,
ZUpdateProfileMutationSchema,
ZUpdatePublicProfileMutationSchema,
} from './schema';
import { router } from '../trpc';
import { deleteAccountRoute } from './delete-account-route';
import { findUserSecurityAuditLogsRoute } from './find-user-security-audit-logs-route';
import { forgotPasswordRoute } from './forgot-password-route';
import { resetPasswordRoute } from './reset-password-route';
import { sendConfirmationEmailRoute } from './send-confirmation-email-route';
import { setProfileImageRoute } from './set-profile-image-route';
import { updatePasswordRoute } from './update-password-route';
import { updateProfileRoute } from './update-profile-route';
import { updatePublicProfileRoute } from './update-public-profile-route';
export const profileRouter = router({
findUserSecurityAuditLogs: authenticatedProcedure
.input(ZFindUserSecurityAuditLogsSchema)
.query(async ({ input, ctx }) => {
return await findUserSecurityAuditLogs({
userId: ctx.user.id,
...input,
});
}),
getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => {
const { id } = input;
return await getUserById({ id });
}),
updateProfile: authenticatedProcedure
.input(ZUpdateProfileMutationSchema)
.mutation(async ({ input, ctx }) => {
const { name, signature } = input;
return await updateProfile({
userId: ctx.user.id,
name,
signature,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
updatePublicProfile: authenticatedProcedure
.input(ZUpdatePublicProfileMutationSchema)
.mutation(async ({ input, ctx }) => {
const { url, bio, enabled } = input;
if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) {
const subscriptions = await getSubscriptionsByUserId({
userId: ctx.user.id,
}).then((subscriptions) =>
subscriptions.filter((s) => s.status === SubscriptionStatus.ACTIVE),
);
if (subscriptions.length === 0) {
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
message: 'Only subscribers can have a username shorter than 6 characters',
});
}
}
const user = await updatePublicProfile({
userId: ctx.user.id,
data: {
url,
bio,
enabled,
},
});
return { success: true, url: user.url };
}),
updatePassword: authenticatedProcedure
.input(ZUpdatePasswordMutationSchema)
.mutation(async ({ input, ctx }) => {
const { password, currentPassword } = input;
return await updatePassword({
userId: ctx.user.id,
password,
currentPassword,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
forgotPassword: procedure.input(ZForgotPasswordFormSchema).mutation(async ({ input }) => {
const { email } = input;
return await forgotPassword({
email,
});
}),
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => {
const { password, token } = input;
return await resetPassword({
token,
password,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
sendConfirmationEmail: procedure
.input(ZConfirmEmailMutationSchema)
.mutation(async ({ input }) => {
const { email } = input;
await jobsClient.triggerJob({
name: 'send.signup.confirmation.email',
payload: {
email,
},
});
}),
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => {
return await deleteUser({
id: ctx.user.id,
});
}),
setProfileImage: authenticatedProcedure
.input(ZSetProfileImageMutationSchema)
.mutation(async ({ input, ctx }) => {
const { bytes, teamId } = input;
return await setAvatarImage({
userId: ctx.user.id,
teamId,
bytes,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
}),
findUserSecurityAuditLogs: findUserSecurityAuditLogsRoute,
updateProfile: updateProfileRoute,
updatePublicProfile: updatePublicProfileRoute,
updatePassword: updatePasswordRoute,
forgotPassword: forgotPasswordRoute,
resetPassword: resetPasswordRoute,
sendConfirmationEmail: sendConfirmationEmailRoute,
deleteAccount: deleteAccountRoute,
setProfileImage: setProfileImageRoute,
});

View File

@ -1,79 +0,0 @@
import { z } from 'zod';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '../auth-router/schema';
export const MAX_PROFILE_BIO_LENGTH = 256;
export const ZFindUserSecurityAuditLogsSchema = z.object({
page: z.number().optional(),
perPage: z.number().optional(),
});
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(),
});
export type TUpdateProfileMutationSchema = z.infer<typeof ZUpdateProfileMutationSchema>;
export const ZUpdatePublicProfileMutationSchema = z.object({
bio: z
.string()
.max(MAX_PROFILE_BIO_LENGTH, {
message: `Bio must be shorter than ${MAX_PROFILE_BIO_LENGTH + 1} characters`,
})
.optional(),
enabled: z.boolean().optional(),
url: z
.string()
.trim()
.toLowerCase()
.min(1, { message: 'Please enter a valid username.' })
.regex(/^[a-z0-9-]+$/, {
message: 'Username can only container alphanumeric characters and dashes.',
})
.optional(),
});
export type TUpdatePublicProfileMutationSchema = z.infer<typeof ZUpdatePublicProfileMutationSchema>;
export const ZUpdatePasswordMutationSchema = z.object({
currentPassword: ZCurrentPasswordSchema,
password: ZPasswordSchema,
});
export type TUpdatePasswordMutationSchema = z.infer<typeof ZUpdatePasswordMutationSchema>;
export const ZForgotPasswordFormSchema = z.object({
email: z.string().email().min(1),
});
export type TForgotPasswordFormSchema = z.infer<typeof ZForgotPasswordFormSchema>;
export const ZResetPasswordFormSchema = z.object({
password: ZPasswordSchema,
token: z.string().min(1),
});
export type TResetPasswordFormSchema = z.infer<typeof ZResetPasswordFormSchema>;
export const ZConfirmEmailMutationSchema = z.object({
email: z.string().email().min(1),
});
export type TConfirmEmailMutationSchema = z.infer<typeof ZConfirmEmailMutationSchema>;
export const ZSetProfileImageMutationSchema = z.object({
bytes: z.string().nullish(),
teamId: z.number().min(1).nullish(),
});
export type TSetProfileImageMutationSchema = z.infer<typeof ZSetProfileImageMutationSchema>;

View File

@ -0,0 +1,22 @@
import { z } from 'zod';
import { jobsClient } from '@documenso/lib/jobs/client';
import { procedure } from '../trpc';
export const ZSendConfirmationEmailRequestSchema = z.object({
email: z.string().email().min(1),
});
export const sendConfirmationEmailRoute = procedure
.input(ZSendConfirmationEmailRequestSchema)
.mutation(async ({ input }) => {
const { email } = input;
await jobsClient.triggerJob({
name: 'send.signup.confirmation.email',
payload: {
email,
},
});
});

View File

@ -0,0 +1,24 @@
import { z } from 'zod';
import { setAvatarImage } from '@documenso/lib/server-only/profile/set-avatar-image';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { authenticatedProcedure } from '../trpc';
export const ZSetProfileImageRequestSchema = z.object({
bytes: z.string().nullish(),
teamId: z.number().min(1).nullish(),
});
export const setProfileImageRoute = authenticatedProcedure
.input(ZSetProfileImageRequestSchema)
.mutation(async ({ input, ctx }) => {
const { bytes, teamId } = input;
return await setAvatarImage({
userId: ctx.user.id,
teamId,
bytes,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
});

View File

@ -0,0 +1,25 @@
import { z } from 'zod';
import { updatePassword } from '@documenso/lib/server-only/user/update-password';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '../auth-router/schema';
import { authenticatedProcedure } from '../trpc';
export const ZUpdatePasswordRequestSchema = z.object({
currentPassword: ZCurrentPasswordSchema,
password: ZPasswordSchema,
});
export const updatePasswordRoute = authenticatedProcedure
.input(ZUpdatePasswordRequestSchema)
.mutation(async ({ input, ctx }) => {
const { password, currentPassword } = input;
return await updatePassword({
userId: ctx.user.id,
password,
currentPassword,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
});

View File

@ -0,0 +1,24 @@
import { z } from 'zod';
import { updateProfile } from '@documenso/lib/server-only/user/update-profile';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { authenticatedProcedure } from '../trpc';
export const ZUpdateProfileRequestSchema = z.object({
name: z.string().min(1),
signature: z.string(),
});
export const updateProfileRoute = authenticatedProcedure
.input(ZUpdateProfileRequestSchema)
.mutation(async ({ input, ctx }) => {
const { name, signature } = input;
return await updateProfile({
userId: ctx.user.id,
name,
signature,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
});

View File

@ -0,0 +1,60 @@
import { z } from 'zod';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getSubscriptionsByUserId } from '@documenso/lib/server-only/subscription/get-subscriptions-by-user-id';
import { updatePublicProfile } from '@documenso/lib/server-only/user/update-public-profile';
import { SubscriptionStatus } from '@documenso/prisma/client';
import { authenticatedProcedure } from '../trpc';
export const MAX_PROFILE_BIO_LENGTH = 256;
export const ZUpdatePublicProfileRequestSchema = z.object({
bio: z
.string()
.max(MAX_PROFILE_BIO_LENGTH, {
message: `Bio must be shorter than ${MAX_PROFILE_BIO_LENGTH + 1} characters`,
})
.optional(),
enabled: z.boolean().optional(),
url: z
.string()
.trim()
.toLowerCase()
.min(1, { message: 'Please enter a valid username.' })
.regex(/^[a-z0-9-]+$/, {
message: 'Username can only container alphanumeric characters and dashes.',
})
.optional(),
});
export const updatePublicProfileRoute = authenticatedProcedure
.input(ZUpdatePublicProfileRequestSchema)
.mutation(async ({ input, ctx }) => {
const { url, bio, enabled } = input;
if (IS_BILLING_ENABLED() && url !== undefined && url.length < 6) {
const subscriptions = await getSubscriptionsByUserId({
userId: ctx.user.id,
}).then((subscriptions) =>
subscriptions.filter((s) => s.status === SubscriptionStatus.ACTIVE),
);
if (subscriptions.length === 0) {
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
message: 'Only subscribers can have a username shorter than 6 characters',
});
}
}
const user = await updatePublicProfile({
userId: ctx.user.id,
data: {
url,
bio,
enabled,
},
});
return { success: true, url: user.url };
});