mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 17:21:41 +10:00
feat: migrate nextjs to rr7
This commit is contained in:
@ -1 +0,0 @@
|
||||
export * from '@trpc/server/adapters/next';
|
||||
@ -11,8 +11,7 @@ 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 { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { DocumentStatus } from '@documenso/prisma/client';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
|
||||
import { adminProcedure, router } from '../trpc';
|
||||
import {
|
||||
@ -70,7 +69,7 @@ export const adminRouter = router({
|
||||
|
||||
const document = await getEntireDocument({ id });
|
||||
|
||||
const isResealing = document.status === DocumentStatus.COMPLETED;
|
||||
const isResealing = isDocumentCompleted(document.status);
|
||||
|
||||
return await sealDocument({ documentId: id, isResealing });
|
||||
}),
|
||||
@ -117,7 +116,7 @@ export const adminRouter = router({
|
||||
|
||||
return await superDeleteDocument({
|
||||
id,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||
import { deleteTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id';
|
||||
import { getUserTokens } from '@documenso/lib/server-only/public-api/get-all-user-tokens';
|
||||
import { getApiTokenById } from '@documenso/lib/server-only/public-api/get-api-token-by-id';
|
||||
import { getApiTokens } from '@documenso/lib/server-only/public-api/get-api-tokens';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
@ -12,7 +12,7 @@ import {
|
||||
|
||||
export const apiTokenRouter = router({
|
||||
getTokens: authenticatedProcedure.query(async ({ ctx }) => {
|
||||
return await getUserTokens({ userId: ctx.user.id });
|
||||
return await getApiTokens({ userId: ctx.user.id, teamId: ctx.teamId });
|
||||
}),
|
||||
|
||||
getTokenById: authenticatedProcedure
|
||||
|
||||
@ -1,22 +1,13 @@
|
||||
import type { RegistrationResponseJSON } from '@simplewebauthn/types';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { parse } from 'cookie-es';
|
||||
import { env } from 'next-runtime-env';
|
||||
|
||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { formatSecureCookieName } from '@documenso/lib/constants/auth';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { jobsClient } from '@documenso/lib/jobs/client';
|
||||
import { ErrorCode } from '@documenso/lib/next-auth/error-codes';
|
||||
import { createPasskey } from '@documenso/lib/server-only/auth/create-passkey';
|
||||
import { createPasskeyAuthenticationOptions } from '@documenso/lib/server-only/auth/create-passkey-authentication-options';
|
||||
import { createPasskeyRegistrationOptions } from '@documenso/lib/server-only/auth/create-passkey-registration-options';
|
||||
import { createPasskeySigninOptions } from '@documenso/lib/server-only/auth/create-passkey-signin-options';
|
||||
import { deletePasskey } from '@documenso/lib/server-only/auth/delete-passkey';
|
||||
import { findPasskeys } from '@documenso/lib/server-only/auth/find-passkeys';
|
||||
import { compareSync } from '@documenso/lib/server-only/auth/hash';
|
||||
import { updatePasskey } from '@documenso/lib/server-only/auth/update-passkey';
|
||||
import { createUser } from '@documenso/lib/server-only/user/create-user';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
@ -24,60 +15,10 @@ import {
|
||||
ZCreatePasskeyMutationSchema,
|
||||
ZDeletePasskeyMutationSchema,
|
||||
ZFindPasskeysQuerySchema,
|
||||
ZSignUpMutationSchema,
|
||||
ZUpdatePasskeyMutationSchema,
|
||||
ZVerifyPasswordMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
const NEXT_PUBLIC_DISABLE_SIGNUP = () => env('NEXT_PUBLIC_DISABLE_SIGNUP');
|
||||
|
||||
export const authRouter = router({
|
||||
signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => {
|
||||
if (NEXT_PUBLIC_DISABLE_SIGNUP() === 'true') {
|
||||
throw new AppError('SIGNUP_DISABLED', {
|
||||
message: 'Signups are disabled.',
|
||||
});
|
||||
}
|
||||
|
||||
const { name, email, password, signature, url } = input;
|
||||
|
||||
if (IS_BILLING_ENABLED() && url && url.length < 6) {
|
||||
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
|
||||
message: 'Only subscribers can have a username shorter than 6 characters',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await createUser({ name, email, password, signature, url });
|
||||
|
||||
await jobsClient.triggerJob({
|
||||
name: 'send.signup.confirmation.email',
|
||||
payload: {
|
||||
email: user.email,
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
}),
|
||||
|
||||
verifyPassword: authenticatedProcedure
|
||||
.input(ZVerifyPasswordMutationSchema)
|
||||
.mutation(({ ctx, input }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
const { password } = input;
|
||||
|
||||
if (!user.password) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: ErrorCode.INCORRECT_PASSWORD,
|
||||
});
|
||||
}
|
||||
|
||||
const valid = compareSync(password, user.password);
|
||||
|
||||
return valid;
|
||||
}),
|
||||
|
||||
createPasskey: authenticatedProcedure
|
||||
.input(ZCreatePasskeyMutationSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
@ -107,20 +48,17 @@ export const authRouter = router({
|
||||
});
|
||||
}),
|
||||
|
||||
createPasskeySigninOptions: procedure.mutation(async ({ ctx }) => {
|
||||
const cookies = parse(ctx.req.headers.cookie ?? '');
|
||||
|
||||
const sessionIdToken =
|
||||
cookies[formatSecureCookieName('__Host-next-auth.csrf-token')] ||
|
||||
cookies[formatSecureCookieName('next-auth.csrf-token')];
|
||||
|
||||
if (!sessionIdToken) {
|
||||
throw new Error('Missing CSRF token');
|
||||
}
|
||||
createPasskeySigninOptions: procedure.mutation(async () => {
|
||||
const sessionIdToken = nanoid(16);
|
||||
|
||||
const [sessionId] = decodeURI(sessionIdToken).split('|');
|
||||
|
||||
return await createPasskeySigninOptions({ sessionId });
|
||||
const options = await createPasskeySigninOptions({ sessionId });
|
||||
|
||||
return {
|
||||
options,
|
||||
sessionId,
|
||||
};
|
||||
}),
|
||||
|
||||
deletePasskey: authenticatedProcedure
|
||||
|
||||
@ -71,5 +71,3 @@ export const ZFindPasskeysQuerySchema = ZFindSearchParamsSchema.extend({
|
||||
});
|
||||
|
||||
export type TSignUpMutationSchema = z.infer<typeof ZSignUpMutationSchema>;
|
||||
|
||||
export const ZVerifyPasswordMutationSchema = ZSignUpMutationSchema.pick({ password: true });
|
||||
|
||||
@ -1,45 +1,40 @@
|
||||
import type { Session } from '@prisma/client';
|
||||
import type { Context } from 'hono';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
|
||||
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
|
||||
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import type { CreateNextContextOptions } from './adapters/next';
|
||||
|
||||
type CreateTrpcContext = CreateNextContextOptions & {
|
||||
requestSource: 'apiV1' | 'apiV2' | 'app';
|
||||
type CreateTrpcContextOptions = {
|
||||
c: Context;
|
||||
requestSource: 'app' | 'apiV1' | 'apiV2';
|
||||
};
|
||||
|
||||
export const createTrpcContext = async ({
|
||||
req,
|
||||
res,
|
||||
c,
|
||||
requestSource,
|
||||
}: Omit<CreateTrpcContext, 'info'>) => {
|
||||
const { session, user } = await getServerSession({ req, res });
|
||||
}: CreateTrpcContextOptions): Promise<TrpcContext> => {
|
||||
const { session, user } = await getOptionalSession(c);
|
||||
|
||||
const req = c.req.raw;
|
||||
|
||||
const metadata: ApiRequestMetadata = {
|
||||
requestMetadata: extractNextApiRequestMetadata(req),
|
||||
requestMetadata: extractRequestMetadata(req),
|
||||
source: requestSource,
|
||||
auth: null,
|
||||
};
|
||||
|
||||
const rawTeamId = req.headers.get('x-team-id') || undefined;
|
||||
|
||||
const teamId = z.coerce
|
||||
.number()
|
||||
.optional()
|
||||
.catch(() => undefined)
|
||||
.parse(req.headers['x-team-id']);
|
||||
.parse(rawTeamId);
|
||||
|
||||
if (!session) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
teamId,
|
||||
req,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
if (!session || !user) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
@ -58,4 +53,17 @@ export const createTrpcContext = async ({
|
||||
};
|
||||
};
|
||||
|
||||
export type TrpcContext = Awaited<ReturnType<typeof createTrpcContext>>;
|
||||
export type TrpcContext = (
|
||||
| {
|
||||
session: null;
|
||||
user: null;
|
||||
}
|
||||
| {
|
||||
session: Session;
|
||||
user: SessionUser;
|
||||
}
|
||||
) & {
|
||||
teamId: number | undefined;
|
||||
req: Request;
|
||||
metadata: ApiRequestMetadata;
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { DocumentDataType } from '@prisma/client';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
|
||||
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
||||
@ -17,14 +17,16 @@ import { findDocuments } from '@documenso/lib/server-only/document/find-document
|
||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
|
||||
import { getStats } from '@documenso/lib/server-only/document/get-stats';
|
||||
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
|
||||
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
|
||||
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
|
||||
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
||||
import { DocumentDataType, DocumentStatus } from '@documenso/prisma/client';
|
||||
import { isDocumentCompleted } from '@documenso/lib/utils/document';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
@ -39,6 +41,8 @@ import {
|
||||
ZDuplicateDocumentRequestSchema,
|
||||
ZDuplicateDocumentResponseSchema,
|
||||
ZFindDocumentAuditLogsQuerySchema,
|
||||
ZFindDocumentsInternalRequestSchema,
|
||||
ZFindDocumentsInternalResponseSchema,
|
||||
ZFindDocumentsRequestSchema,
|
||||
ZFindDocumentsResponseSchema,
|
||||
ZGenericSuccessResponse,
|
||||
@ -50,7 +54,6 @@ import {
|
||||
ZMoveDocumentToTeamSchema,
|
||||
ZResendDocumentMutationSchema,
|
||||
ZSearchDocumentsMutationSchema,
|
||||
ZSetPasswordForDocumentMutationSchema,
|
||||
ZSetSigningOrderForDocumentMutationSchema,
|
||||
ZSuccessResponseSchema,
|
||||
ZUpdateDocumentRequestSchema,
|
||||
@ -124,6 +127,74 @@ export const documentRouter = router({
|
||||
return documents;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Internal endpoint for /documents page to additionally return getStats.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
findDocumentsInternal: authenticatedProcedure
|
||||
.input(ZFindDocumentsInternalRequestSchema)
|
||||
.output(ZFindDocumentsInternalResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { user, teamId } = ctx;
|
||||
|
||||
const {
|
||||
query,
|
||||
templateId,
|
||||
page,
|
||||
perPage,
|
||||
orderByDirection,
|
||||
orderByColumn,
|
||||
source,
|
||||
status,
|
||||
period,
|
||||
senderIds,
|
||||
} = input;
|
||||
|
||||
const getStatOptions: GetStatsInput = {
|
||||
user,
|
||||
period,
|
||||
search: query,
|
||||
};
|
||||
|
||||
if (teamId) {
|
||||
const team = await getTeamById({ userId: user.id, teamId });
|
||||
|
||||
getStatOptions.team = {
|
||||
teamId: team.id,
|
||||
teamEmail: team.teamEmail?.email,
|
||||
senderIds,
|
||||
currentTeamMemberRole: team.currentTeamMember?.role,
|
||||
currentUserEmail: user.email,
|
||||
userId: user.id,
|
||||
};
|
||||
}
|
||||
|
||||
const [stats, documents] = await Promise.all([
|
||||
getStats(getStatOptions),
|
||||
findDocuments({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
query,
|
||||
templateId,
|
||||
page,
|
||||
perPage,
|
||||
source,
|
||||
status,
|
||||
period,
|
||||
senderIds,
|
||||
orderBy: orderByColumn
|
||||
? { column: orderByColumn, direction: orderByDirection }
|
||||
: undefined,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
...documents,
|
||||
stats,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
@ -371,35 +442,6 @@ export const documentRouter = router({
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setPasswordForDocument: authenticatedProcedure
|
||||
.input(ZSetPasswordForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
const { documentId, password } = input;
|
||||
|
||||
const key = DOCUMENSO_ENCRYPTION_KEY;
|
||||
|
||||
if (!key) {
|
||||
throw new Error('Missing encryption key');
|
||||
}
|
||||
|
||||
const securePassword = symmetricEncrypt({
|
||||
data: password,
|
||||
key,
|
||||
});
|
||||
|
||||
await upsertDocumentMeta({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
documentId,
|
||||
password: securePassword,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
@ -618,7 +660,7 @@ export const documentRouter = router({
|
||||
teamId,
|
||||
});
|
||||
|
||||
if (document.status !== DocumentStatus.COMPLETED) {
|
||||
if (!isDocumentCompleted(document.status)) {
|
||||
throw new AppError('DOCUMENT_NOT_COMPLETE');
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
import {
|
||||
DocumentDistributionMethod,
|
||||
DocumentSigningOrder,
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
DocumentVisibility,
|
||||
FieldType,
|
||||
} from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { VALID_DATE_FORMAT_VALUES } from '@documenso/lib/constants/date-formats';
|
||||
@ -23,14 +31,7 @@ import {
|
||||
import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
||||
import {
|
||||
DocumentDistributionMethod,
|
||||
DocumentSigningOrder,
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
DocumentVisibility,
|
||||
FieldType,
|
||||
} from '@documenso/prisma/client';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
|
||||
import { ZCreateRecipientSchema } from '../recipient-router/schema';
|
||||
|
||||
@ -131,6 +132,26 @@ export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
|
||||
|
||||
export type TFindDocumentsResponse = z.infer<typeof ZFindDocumentsResponseSchema>;
|
||||
|
||||
export const ZFindDocumentsInternalRequestSchema = ZFindDocumentsRequestSchema.extend({
|
||||
period: z.enum(['7d', '14d', '30d']).optional(),
|
||||
senderIds: z.array(z.number()).optional(),
|
||||
status: z.nativeEnum(ExtendedDocumentStatus).optional(),
|
||||
});
|
||||
|
||||
export const ZFindDocumentsInternalResponseSchema = ZFindResultResponse.extend({
|
||||
data: ZDocumentManySchema.array(),
|
||||
stats: z.object({
|
||||
[ExtendedDocumentStatus.DRAFT]: z.number(),
|
||||
[ExtendedDocumentStatus.PENDING]: z.number(),
|
||||
[ExtendedDocumentStatus.COMPLETED]: z.number(),
|
||||
[ExtendedDocumentStatus.REJECTED]: z.number(),
|
||||
[ExtendedDocumentStatus.INBOX]: z.number(),
|
||||
[ExtendedDocumentStatus.ALL]: z.number(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type TFindDocumentsInternalResponse = z.infer<typeof ZFindDocumentsInternalResponseSchema>;
|
||||
|
||||
export const ZFindDocumentAuditLogsQuerySchema = ZFindSearchParamsSchema.extend({
|
||||
documentId: z.number().min(1),
|
||||
cursor: z.string().optional(),
|
||||
|
||||
@ -9,7 +9,6 @@ import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-field
|
||||
import { signFieldWithToken } from '@documenso/lib/server-only/field/sign-field-with-token';
|
||||
import { updateDocumentFields } from '@documenso/lib/server-only/field/update-document-fields';
|
||||
import { updateTemplateFields } from '@documenso/lib/server-only/field/update-template-fields';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
@ -452,11 +451,11 @@ export const fieldRouter = router({
|
||||
return await signFieldWithToken({
|
||||
token,
|
||||
fieldId,
|
||||
value,
|
||||
value: value ?? '',
|
||||
isBase64,
|
||||
userId: ctx.user?.id,
|
||||
authOptions,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
|
||||
@ -471,7 +470,7 @@ export const fieldRouter = router({
|
||||
return await removeSignedFieldWithToken({
|
||||
token,
|
||||
fieldId,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { FieldType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZRecipientActionAuthSchema } from '@documenso/lib/types/document-auth';
|
||||
@ -10,7 +11,6 @@ import {
|
||||
ZFieldWidthSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZFieldAndMetaSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
|
||||
const ZCreateFieldSchema = ZFieldAndMetaSchema.and(
|
||||
z.object({
|
||||
@ -153,7 +153,7 @@ export const ZSetFieldsForTemplateResponseSchema = z.object({
|
||||
export const ZSignFieldWithTokenMutationSchema = z.object({
|
||||
token: z.string(),
|
||||
fieldId: z.number(),
|
||||
value: z.string().trim(),
|
||||
value: z.string().trim().optional(),
|
||||
isBase64: z.boolean().optional(),
|
||||
authOptions: ZRecipientActionAuthSchema.optional(),
|
||||
});
|
||||
|
||||
@ -1,28 +1,23 @@
|
||||
import { SubscriptionStatus } from '@prisma/client';
|
||||
|
||||
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 { AppError } from '@documenso/lib/errors/app-error';
|
||||
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 { createBillingPortal } from '@documenso/lib/server-only/user/create-billing-portal';
|
||||
import { createCheckoutSession } from '@documenso/lib/server-only/user/create-checkout-session';
|
||||
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 { adminProcedure, authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZConfirmEmailMutationSchema,
|
||||
ZCreateCheckoutSessionRequestSchema,
|
||||
ZFindUserSecurityAuditLogsSchema,
|
||||
ZForgotPasswordFormSchema,
|
||||
ZResetPasswordFormSchema,
|
||||
ZRetrieveUserByIdQuerySchema,
|
||||
ZSetProfileImageMutationSchema,
|
||||
ZUpdatePasswordMutationSchema,
|
||||
ZUpdateProfileMutationSchema,
|
||||
ZUpdatePublicProfileMutationSchema,
|
||||
} from './schema';
|
||||
@ -43,6 +38,31 @@ export const profileRouter = router({
|
||||
return await getUserById({ id });
|
||||
}),
|
||||
|
||||
createBillingPortal: authenticatedProcedure.mutation(async ({ ctx }) => {
|
||||
return await createBillingPortal({
|
||||
user: {
|
||||
id: ctx.user.id,
|
||||
customerId: ctx.user.customerId,
|
||||
email: ctx.user.email,
|
||||
name: ctx.user.name,
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
createCheckoutSession: authenticatedProcedure
|
||||
.input(ZCreateCheckoutSessionRequestSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return await createCheckoutSession({
|
||||
user: {
|
||||
id: ctx.user.id,
|
||||
customerId: ctx.user.customerId,
|
||||
email: ctx.user.email,
|
||||
name: ctx.user.name,
|
||||
},
|
||||
priceId: input.priceId,
|
||||
});
|
||||
}),
|
||||
|
||||
updateProfile: authenticatedProcedure
|
||||
.input(ZUpdateProfileMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
@ -69,7 +89,7 @@ export const profileRouter = router({
|
||||
);
|
||||
|
||||
if (subscriptions.length === 0) {
|
||||
throw new AppError(AppErrorCode.PREMIUM_PROFILE_URL, {
|
||||
throw new AppError('PREMIUM_PROFILE_URL', {
|
||||
message: 'Only subscribers can have a username shorter than 6 characters',
|
||||
});
|
||||
}
|
||||
@ -87,50 +107,6 @@ export const profileRouter = router({
|
||||
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: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
|
||||
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,
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZCurrentPasswordSchema, ZPasswordSchema } from '../auth-router/schema';
|
||||
|
||||
export const MAX_PROFILE_BIO_LENGTH = 256;
|
||||
|
||||
export const ZFindUserSecurityAuditLogsSchema = z.object({
|
||||
@ -17,6 +15,10 @@ export const ZRetrieveUserByIdQuerySchema = z.object({
|
||||
|
||||
export type TRetrieveUserByIdQuerySchema = z.infer<typeof ZRetrieveUserByIdQuerySchema>;
|
||||
|
||||
export const ZCreateCheckoutSessionRequestSchema = z.object({
|
||||
priceId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const ZUpdateProfileMutationSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
signature: z.string(),
|
||||
@ -45,32 +47,6 @@ export const ZUpdatePublicProfileMutationSchema = z.object({
|
||||
|
||||
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(),
|
||||
|
||||
@ -9,7 +9,6 @@ import { setDocumentRecipients } from '@documenso/lib/server-only/recipient/set-
|
||||
import { setTemplateRecipients } from '@documenso/lib/server-only/recipient/set-template-recipients';
|
||||
import { updateDocumentRecipients } from '@documenso/lib/server-only/recipient/update-document-recipients';
|
||||
import { updateTemplateRecipients } from '@documenso/lib/server-only/recipient/update-template-recipients';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
@ -444,7 +443,7 @@ export const recipientRouter = router({
|
||||
documentId,
|
||||
authOptions,
|
||||
userId: ctx.user?.id,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
|
||||
@ -460,7 +459,7 @@ export const recipientRouter = router({
|
||||
token,
|
||||
documentId,
|
||||
reason,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { RecipientRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
@ -6,7 +7,6 @@ import {
|
||||
ZRecipientActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZRecipientLiteSchema, ZRecipientSchema } from '@documenso/lib/types/recipient';
|
||||
import { RecipientRole } from '@documenso/prisma/client';
|
||||
|
||||
export const ZGetRecipientRequestSchema = z.object({
|
||||
recipientId: z.number(),
|
||||
|
||||
@ -9,7 +9,6 @@ import { shareLinkRouter } from './share-link-router/router';
|
||||
import { teamRouter } from './team-router/router';
|
||||
import { templateRouter } from './template-router/router';
|
||||
import { router } from './trpc';
|
||||
import { twoFactorAuthenticationRouter } from './two-factor-authentication-router/router';
|
||||
import { webhookRouter } from './webhook-router/router';
|
||||
|
||||
export const appRouter = router({
|
||||
@ -24,7 +23,6 @@ export const appRouter = router({
|
||||
team: teamRouter,
|
||||
template: templateRouter,
|
||||
webhook: webhookRouter,
|
||||
twoFactorAuthentication: twoFactorAuthenticationRouter,
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { DocumentVisibility, TeamMemberRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
|
||||
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
|
||||
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { ZUpdatePublicProfileMutationSchema } from '../profile-router/schema';
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import type { Document } from '@prisma/client';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { jobs } from '@documenso/lib/jobs/client';
|
||||
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import {
|
||||
@ -20,11 +24,11 @@ import { getTemplateById } from '@documenso/lib/server-only/template/get-templat
|
||||
import { moveTemplateToTeam } from '@documenso/lib/server-only/template/move-template-to-team';
|
||||
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
|
||||
import { updateTemplate } from '@documenso/lib/server-only/template/update-template';
|
||||
import type { Document } from '@documenso/prisma/client';
|
||||
|
||||
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
|
||||
import { authenticatedProcedure, maybeAuthenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZBulkSendTemplateMutationSchema,
|
||||
ZCreateDocumentFromDirectTemplateRequestSchema,
|
||||
ZCreateDocumentFromTemplateRequestSchema,
|
||||
ZCreateDocumentFromTemplateResponseSchema,
|
||||
@ -223,7 +227,8 @@ export const templateRouter = router({
|
||||
.output(ZCreateDocumentFromTemplateResponseSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const { teamId } = ctx;
|
||||
const { templateId, recipients, distributeDocument, customDocumentDataId } = input;
|
||||
const { templateId, recipients, distributeDocument, customDocumentDataId, prefillFields } =
|
||||
input;
|
||||
|
||||
const limits = await getServerLimits({ email: ctx.user.email, teamId });
|
||||
|
||||
@ -238,6 +243,7 @@ export const templateRouter = router({
|
||||
recipients,
|
||||
customDocumentDataId,
|
||||
requestMetadata: ctx.metadata,
|
||||
prefillFields,
|
||||
});
|
||||
|
||||
if (distributeDocument) {
|
||||
@ -414,4 +420,48 @@ export const templateRouter = router({
|
||||
userId,
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
uploadBulkSend: authenticatedProcedure
|
||||
.input(ZBulkSendTemplateMutationSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const { templateId, teamId, csv, sendImmediately } = input;
|
||||
const { user } = ctx;
|
||||
|
||||
if (csv.length > 4 * 1024 * 1024) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'File size exceeds 4MB limit',
|
||||
});
|
||||
}
|
||||
|
||||
const template = await getTemplateById({
|
||||
id: templateId,
|
||||
teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Template not found',
|
||||
});
|
||||
}
|
||||
|
||||
await jobs.triggerJob({
|
||||
name: 'internal.bulk-send-template',
|
||||
payload: {
|
||||
userId: user.id,
|
||||
teamId,
|
||||
templateId,
|
||||
csvContent: csv,
|
||||
sendImmediately,
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}),
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DocumentSigningOrder, DocumentVisibility, TemplateType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZDocumentSchema } from '@documenso/lib/types/document';
|
||||
@ -6,14 +7,14 @@ import {
|
||||
ZDocumentActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||
import { ZFieldMetaPrefillFieldsSchema } from '@documenso/lib/types/field-meta';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import {
|
||||
ZTemplateLiteSchema,
|
||||
ZTemplateManySchema,
|
||||
ZTemplateSchema,
|
||||
} from '@documenso/lib/types/template';
|
||||
import { DocumentSigningOrder, DocumentVisibility, TemplateType } from '@documenso/prisma/client';
|
||||
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
|
||||
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
|
||||
|
||||
import {
|
||||
ZDocumentMetaDateFormatSchema,
|
||||
@ -67,6 +68,12 @@ export const ZCreateDocumentFromTemplateRequestSchema = z.object({
|
||||
'The data ID of an alternative PDF to use when creating the document. If not provided, the PDF attached to the template will be used.',
|
||||
)
|
||||
.optional(),
|
||||
prefillFields: z
|
||||
.array(ZFieldMetaPrefillFieldsSchema)
|
||||
.describe(
|
||||
'The fields to prefill on the document before sending it out. Useful when you want to create a document from an existing template and pre-fill the fields with specific values.',
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentFromTemplateResponseSchema = ZDocumentSchema;
|
||||
@ -188,6 +195,14 @@ export const ZMoveTemplateToTeamRequestSchema = z.object({
|
||||
|
||||
export const ZMoveTemplateToTeamResponseSchema = ZTemplateLiteSchema;
|
||||
|
||||
export const ZBulkSendTemplateMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
teamId: z.number().optional(),
|
||||
csv: z.string().min(1),
|
||||
sendImmediately: z.boolean(),
|
||||
});
|
||||
|
||||
export type TCreateTemplateMutationSchema = z.infer<typeof ZCreateTemplateMutationSchema>;
|
||||
export type TDuplicateTemplateMutationSchema = z.infer<typeof ZDuplicateTemplateMutationSchema>;
|
||||
export type TDeleteTemplateMutationSchema = z.infer<typeof ZDeleteTemplateMutationSchema>;
|
||||
export type TBulkSendTemplateMutationSchema = z.infer<typeof ZBulkSendTemplateMutationSchema>;
|
||||
|
||||
@ -3,10 +3,9 @@ import SuperJSON from 'superjson';
|
||||
import type { AnyZodObject } from 'zod';
|
||||
|
||||
import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error';
|
||||
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
||||
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { isAdmin } from '@documenso/lib/utils/is-admin';
|
||||
|
||||
import type { TrpcContext } from './context';
|
||||
|
||||
@ -67,7 +66,7 @@ const t = initTRPC
|
||||
* Middlewares
|
||||
*/
|
||||
export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
|
||||
const authorizationHeader = ctx.req.headers.authorization;
|
||||
const authorizationHeader = ctx.req.headers.get('authorization');
|
||||
|
||||
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
|
||||
if (authorizationHeader) {
|
||||
@ -131,8 +130,6 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
|
||||
});
|
||||
|
||||
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
|
||||
const requestMetadata = extractNextApiRequestMetadata(ctx.req);
|
||||
|
||||
return await next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
@ -147,7 +144,6 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) =
|
||||
email: ctx.user.email,
|
||||
}
|
||||
: undefined,
|
||||
requestMetadata,
|
||||
auth: ctx.session ? 'session' : null,
|
||||
} satisfies ApiRequestMetadata,
|
||||
},
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
import { disableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/disable-2fa';
|
||||
import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/enable-2fa';
|
||||
import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa';
|
||||
import { viewBackupCodes } from '@documenso/lib/server-only/2fa/view-backup-codes';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZDisableTwoFactorAuthenticationMutationSchema,
|
||||
ZEnableTwoFactorAuthenticationMutationSchema,
|
||||
ZViewRecoveryCodesMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const twoFactorAuthenticationRouter = router({
|
||||
setup: authenticatedProcedure.mutation(async ({ ctx }) => {
|
||||
return await setupTwoFactorAuthentication({
|
||||
user: ctx.user,
|
||||
});
|
||||
}),
|
||||
|
||||
enable: authenticatedProcedure
|
||||
.input(ZEnableTwoFactorAuthenticationMutationSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
const { code } = input;
|
||||
|
||||
return await enableTwoFactorAuthentication({
|
||||
user,
|
||||
code,
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
|
||||
disable: authenticatedProcedure
|
||||
.input(ZDisableTwoFactorAuthenticationMutationSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const user = ctx.user;
|
||||
|
||||
return await disableTwoFactorAuthentication({
|
||||
user,
|
||||
totpCode: input.totpCode,
|
||||
backupCode: input.backupCode,
|
||||
requestMetadata: ctx.metadata.requestMetadata,
|
||||
});
|
||||
}),
|
||||
|
||||
viewRecoveryCodes: authenticatedProcedure
|
||||
.input(ZViewRecoveryCodesMutationSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return await viewBackupCodes({
|
||||
user: ctx.user,
|
||||
token: input.token,
|
||||
});
|
||||
}),
|
||||
});
|
||||
@ -1,24 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZEnableTwoFactorAuthenticationMutationSchema = z.object({
|
||||
code: z.string().min(6).max(6),
|
||||
});
|
||||
|
||||
export type TEnableTwoFactorAuthenticationMutationSchema = z.infer<
|
||||
typeof ZEnableTwoFactorAuthenticationMutationSchema
|
||||
>;
|
||||
|
||||
export const ZDisableTwoFactorAuthenticationMutationSchema = z.object({
|
||||
totpCode: z.string().trim().optional(),
|
||||
backupCode: z.string().trim().optional(),
|
||||
});
|
||||
|
||||
export type TDisableTwoFactorAuthenticationMutationSchema = z.infer<
|
||||
typeof ZDisableTwoFactorAuthenticationMutationSchema
|
||||
>;
|
||||
|
||||
export const ZViewRecoveryCodesMutationSchema = z.object({
|
||||
token: z.string().trim().min(1),
|
||||
});
|
||||
|
||||
export type TViewRecoveryCodesMutationSchema = z.infer<typeof ZViewRecoveryCodesMutationSchema>;
|
||||
@ -1,7 +1,6 @@
|
||||
import { WebhookTriggerEvents } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
export const ZGetTeamWebhooksQuerySchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user