mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
Merge branch 'main' into feat/public-api
This commit is contained in:
@ -1,9 +1,25 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCurrentPasswordSchema = z
|
||||
.string()
|
||||
.min(6, { message: 'Must be at least 6 characters in length' })
|
||||
.max(72);
|
||||
|
||||
export const ZPasswordSchema = z
|
||||
.string()
|
||||
.regex(new RegExp('.*[A-Z].*'), { message: 'One uppercase character' })
|
||||
.regex(new RegExp('.*[a-z].*'), { message: 'One lowercase character' })
|
||||
.regex(new RegExp('.*\\d.*'), { message: 'One number' })
|
||||
.regex(new RegExp('.*[`~<>?,./!@#$%^&*()\\-_+="\'|{}\\[\\];:\\\\].*'), {
|
||||
message: 'One special character is required',
|
||||
})
|
||||
.min(8, { message: 'Must be at least 8 characters in length' })
|
||||
.max(72, { message: 'Cannot be more than 72 characters in length' });
|
||||
|
||||
export const ZSignUpMutationSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
password: z.string().min(6),
|
||||
password: ZPasswordSchema,
|
||||
signature: z.string().min(1, { message: 'A signature is required.' }),
|
||||
});
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
||||
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
||||
|
||||
import { getServerSession } from '@documenso/lib/next-auth/get-server-session';
|
||||
|
||||
@ -9,6 +9,7 @@ export const createTrpcContext = async ({ req, res }: CreateNextContextOptions)
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
req,
|
||||
};
|
||||
}
|
||||
|
||||
@ -16,12 +17,14 @@ export const createTrpcContext = async ({ req, res }: CreateNextContextOptions)
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
req,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
session,
|
||||
user,
|
||||
req,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
17
packages/trpc/server/crypto/router.ts
Normal file
17
packages/trpc/server/crypto/router.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
|
||||
|
||||
import { procedure, router } from '../trpc';
|
||||
import { ZEncryptSecondaryDataMutationSchema } from './schema';
|
||||
|
||||
export const cryptoRouter = router({
|
||||
encryptSecondaryData: procedure
|
||||
.input(ZEncryptSecondaryDataMutationSchema)
|
||||
.mutation(({ input }) => {
|
||||
try {
|
||||
return encryptSecondaryData(input);
|
||||
} catch {
|
||||
// Never leak errors for crypto.
|
||||
throw new Error('Failed to encrypt data');
|
||||
}
|
||||
}),
|
||||
});
|
||||
15
packages/trpc/server/crypto/schema.ts
Normal file
15
packages/trpc/server/crypto/schema.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZEncryptSecondaryDataMutationSchema = z.object({
|
||||
data: z.string(),
|
||||
expiresAt: z.number().optional(),
|
||||
});
|
||||
|
||||
export const ZDecryptDataMutationSchema = z.object({
|
||||
data: z.string(),
|
||||
});
|
||||
|
||||
export type TEncryptSecondaryDataMutationSchema = z.infer<
|
||||
typeof ZEncryptSecondaryDataMutationSchema
|
||||
>;
|
||||
export type TDecryptDataMutationSchema = z.infer<typeof ZDecryptDataMutationSchema>;
|
||||
@ -1,6 +1,7 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto';
|
||||
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
|
||||
import { createDocument } from '@documenso/lib/server-only/document/create-document';
|
||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||
@ -13,6 +14,7 @@ import { sendDocument } from '@documenso/lib/server-only/document/send-document'
|
||||
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
@ -24,6 +26,7 @@ import {
|
||||
ZSearchDocumentsMutationSchema,
|
||||
ZSendDocumentMutationSchema,
|
||||
ZSetFieldsForDocumentMutationSchema,
|
||||
ZSetPasswordForDocumentMutationSchema,
|
||||
ZSetRecipientsForDocumentMutationSchema,
|
||||
ZSetTitleForDocumentMutationSchema,
|
||||
} from './schema';
|
||||
@ -33,10 +36,8 @@ export const documentRouter = router({
|
||||
.input(ZGetDocumentByIdQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
|
||||
return await getDocumentById({
|
||||
id,
|
||||
...input,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
} catch (err) {
|
||||
@ -70,9 +71,9 @@ export const documentRouter = router({
|
||||
.input(ZCreateDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { title, documentDataId } = input;
|
||||
const { title, documentDataId, teamId } = input;
|
||||
|
||||
const { remaining } = await getServerLimits({ email: ctx.user.email });
|
||||
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
throw new TRPCError({
|
||||
@ -84,6 +85,7 @@ export const documentRouter = router({
|
||||
|
||||
return await createDocument({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
title,
|
||||
documentDataId,
|
||||
});
|
||||
@ -175,17 +177,52 @@ export const documentRouter = router({
|
||||
}
|
||||
}),
|
||||
|
||||
setPasswordForDocument: authenticatedProcedure
|
||||
.input(ZSetPasswordForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
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({
|
||||
documentId,
|
||||
password: securePassword,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to set the password for this document. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
sendDocument: authenticatedProcedure
|
||||
.input(ZSendDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { documentId, email } = input;
|
||||
const { documentId, meta } = input;
|
||||
|
||||
if (email.message || email.subject) {
|
||||
if (meta.message || meta.subject || meta.timezone || meta.dateFormat) {
|
||||
await upsertDocumentMeta({
|
||||
documentId,
|
||||
subject: email.subject,
|
||||
message: email.message,
|
||||
subject: meta.subject,
|
||||
message: meta.message,
|
||||
dateFormat: meta.dateFormat,
|
||||
timezone: meta.timezone,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -207,12 +244,9 @@ export const documentRouter = router({
|
||||
.input(ZResendDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { documentId, recipients } = input;
|
||||
|
||||
return await resendDocument({
|
||||
userId: ctx.user.id,
|
||||
documentId,
|
||||
recipients,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@ -228,14 +262,13 @@ export const documentRouter = router({
|
||||
.input(ZGetDocumentByIdQuerySchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
|
||||
return await duplicateDocumentById({
|
||||
id,
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We are unable to duplicate this document. Please try again later.',
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, FieldType, RecipientRole } from '@documenso/prisma/client';
|
||||
|
||||
export const ZGetDocumentByIdQuerySchema = z.object({
|
||||
id: z.number().min(1),
|
||||
teamId: z.number().min(1).optional(),
|
||||
});
|
||||
|
||||
export type TGetDocumentByIdQuerySchema = z.infer<typeof ZGetDocumentByIdQuerySchema>;
|
||||
@ -17,6 +18,7 @@ export type TGetDocumentByTokenQuerySchema = z.infer<typeof ZGetDocumentByTokenQ
|
||||
export const ZCreateDocumentMutationSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
documentDataId: z.string().min(1),
|
||||
teamId: z.number().optional(),
|
||||
});
|
||||
|
||||
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
||||
@ -35,6 +37,7 @@ export const ZSetRecipientsForDocumentMutationSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
email: z.string().min(1).email(),
|
||||
name: z.string(),
|
||||
role: z.nativeEnum(RecipientRole),
|
||||
}),
|
||||
),
|
||||
});
|
||||
@ -65,15 +68,27 @@ export type TSetFieldsForDocumentMutationSchema = z.infer<
|
||||
|
||||
export const ZSendDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
email: z.object({
|
||||
meta: z.object({
|
||||
subject: z.string(),
|
||||
message: z.string(),
|
||||
timezone: z.string(),
|
||||
dateFormat: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ZSetPasswordForDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
password: z.string(),
|
||||
});
|
||||
|
||||
export type TSetPasswordForDocumentMutationSchema = z.infer<
|
||||
typeof ZSetPasswordForDocumentMutationSchema
|
||||
>;
|
||||
|
||||
export const ZResendDocumentMutationSchema = z.object({
|
||||
documentId: z.number(),
|
||||
recipients: z.array(z.number()).min(1),
|
||||
teamId: z.number().min(1).optional(),
|
||||
});
|
||||
|
||||
export type TSendDocumentMutationSchema = z.infer<typeof ZSendDocumentMutationSchema>;
|
||||
|
||||
@ -2,11 +2,13 @@ import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { removeSignedFieldWithToken } from '@documenso/lib/server-only/field/remove-signed-field-with-token';
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-fields-for-template';
|
||||
import { signFieldWithToken } from '@documenso/lib/server-only/field/sign-field-with-token';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
ZAddFieldsMutationSchema,
|
||||
ZAddTemplateFieldsMutationSchema,
|
||||
ZRemovedSignedFieldWithTokenMutationSchema,
|
||||
ZSignFieldWithTokenMutationSchema,
|
||||
} from './schema';
|
||||
@ -37,11 +39,32 @@ export const fieldRouter = router({
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to sign this field. Please try again later.',
|
||||
message: 'We were unable to set this field. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
addTemplateFields: authenticatedProcedure
|
||||
.input(ZAddTemplateFieldsMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { templateId, fields } = input;
|
||||
|
||||
await setFieldsForTemplate({
|
||||
userId: ctx.user.id,
|
||||
templateId,
|
||||
fields: fields.map((field) => ({
|
||||
id: field.nativeId,
|
||||
signerEmail: field.signerEmail,
|
||||
type: field.type,
|
||||
pageNumber: field.pageNumber,
|
||||
pageX: field.pageX,
|
||||
pageY: field.pageY,
|
||||
pageWidth: field.pageWidth,
|
||||
pageHeight: field.pageHeight,
|
||||
})),
|
||||
});
|
||||
}),
|
||||
|
||||
signFieldWithToken: procedure
|
||||
.input(ZSignFieldWithTokenMutationSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
|
||||
@ -21,6 +21,25 @@ export const ZAddFieldsMutationSchema = z.object({
|
||||
|
||||
export type TAddFieldsMutationSchema = z.infer<typeof ZAddFieldsMutationSchema>;
|
||||
|
||||
export const ZAddTemplateFieldsMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
fields: z.array(
|
||||
z.object({
|
||||
formId: z.string().min(1),
|
||||
nativeId: z.number().optional(),
|
||||
type: z.nativeEnum(FieldType),
|
||||
signerEmail: z.string().min(1),
|
||||
pageNumber: z.number().min(1),
|
||||
pageX: z.number().min(0),
|
||||
pageY: z.number().min(0),
|
||||
pageWidth: z.number().min(0),
|
||||
pageHeight: z.number().min(0),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export type TAddTemplateFieldsMutationSchema = z.infer<typeof ZAddTemplateFieldsMutationSchema>;
|
||||
|
||||
export const ZSignFieldWithTokenMutationSchema = z.object({
|
||||
token: z.string(),
|
||||
fieldId: z.number(),
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
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 { sendConfirmationToken } from '@documenso/lib/server-only/user/send-confirmation-token';
|
||||
import { updatePassword } from '@documenso/lib/server-only/user/update-password';
|
||||
import { updateProfile } from '@documenso/lib/server-only/user/update-profile';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import { adminProcedure, authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
ZConfirmEmailMutationSchema,
|
||||
ZFindUserSecurityAuditLogsSchema,
|
||||
ZForgotPasswordFormSchema,
|
||||
ZResetPasswordFormSchema,
|
||||
ZRetrieveUserByIdQuerySchema,
|
||||
@ -18,6 +21,22 @@ import {
|
||||
} from './schema';
|
||||
|
||||
export const profileRouter = router({
|
||||
findUserSecurityAuditLogs: authenticatedProcedure
|
||||
.input(ZFindUserSecurityAuditLogsSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findUserSecurityAuditLogs({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to find user security audit logs. Please try again.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
getUser: adminProcedure.input(ZRetrieveUserByIdQuerySchema).query(async ({ input }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
@ -41,6 +60,7 @@ export const profileRouter = router({
|
||||
userId: ctx.user.id,
|
||||
name,
|
||||
signature,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
@ -63,6 +83,7 @@ export const profileRouter = router({
|
||||
userId: ctx.user.id,
|
||||
password,
|
||||
currentPassword,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
});
|
||||
} catch (err) {
|
||||
let message =
|
||||
@ -91,13 +112,14 @@ export const profileRouter = router({
|
||||
}
|
||||
}),
|
||||
|
||||
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input }) => {
|
||||
resetPassword: procedure.input(ZResetPasswordFormSchema).mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { password, token } = input;
|
||||
|
||||
return await resetPassword({
|
||||
token,
|
||||
password,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
});
|
||||
} catch (err) {
|
||||
let message = 'We were unable to reset your password. Please try again.';
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZCurrentPasswordSchema, ZPasswordSchema } from '../auth-router/schema';
|
||||
|
||||
export const ZFindUserSecurityAuditLogsSchema = z.object({
|
||||
page: z.number().optional(),
|
||||
perPage: z.number().optional(),
|
||||
});
|
||||
|
||||
export const ZRetrieveUserByIdQuerySchema = z.object({
|
||||
id: z.number().min(1),
|
||||
});
|
||||
@ -10,8 +17,8 @@ export const ZUpdateProfileMutationSchema = z.object({
|
||||
});
|
||||
|
||||
export const ZUpdatePasswordMutationSchema = z.object({
|
||||
currentPassword: z.string().min(6),
|
||||
password: z.string().min(6),
|
||||
currentPassword: ZCurrentPasswordSchema,
|
||||
password: ZPasswordSchema,
|
||||
});
|
||||
|
||||
export const ZForgotPasswordFormSchema = z.object({
|
||||
@ -19,7 +26,7 @@ export const ZForgotPasswordFormSchema = z.object({
|
||||
});
|
||||
|
||||
export const ZResetPasswordFormSchema = z.object({
|
||||
password: z.string().min(6),
|
||||
password: ZPasswordSchema,
|
||||
token: z.string().min(1),
|
||||
});
|
||||
|
||||
@ -27,6 +34,7 @@ export const ZConfirmEmailMutationSchema = z.object({
|
||||
email: z.string().email().min(1),
|
||||
});
|
||||
|
||||
export type TFindUserSecurityAuditLogsSchema = z.infer<typeof ZFindUserSecurityAuditLogsSchema>;
|
||||
export type TRetrieveUserByIdQuerySchema = z.infer<typeof ZRetrieveUserByIdQuerySchema>;
|
||||
export type TUpdateProfileMutationSchema = z.infer<typeof ZUpdateProfileMutationSchema>;
|
||||
export type TUpdatePasswordMutationSchema = z.infer<typeof ZUpdatePasswordMutationSchema>;
|
||||
|
||||
@ -2,9 +2,14 @@ import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
import { setRecipientsForTemplate } from '@documenso/lib/server-only/recipient/set-recipients-for-template';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import { ZAddSignersMutationSchema, ZCompleteDocumentWithTokenMutationSchema } from './schema';
|
||||
import {
|
||||
ZAddSignersMutationSchema,
|
||||
ZAddTemplateSignersMutationSchema,
|
||||
ZCompleteDocumentWithTokenMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const recipientRouter = router({
|
||||
addSigners: authenticatedProcedure
|
||||
@ -20,6 +25,7 @@ export const recipientRouter = router({
|
||||
id: signer.nativeId,
|
||||
email: signer.email,
|
||||
name: signer.name,
|
||||
role: signer.role,
|
||||
})),
|
||||
});
|
||||
} catch (err) {
|
||||
@ -27,7 +33,32 @@ export const recipientRouter = router({
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to sign this field. Please try again later.',
|
||||
message: 'We were unable to set this field. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
addTemplateSigners: authenticatedProcedure
|
||||
.input(ZAddTemplateSignersMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { templateId, signers } = input;
|
||||
|
||||
return await setRecipientsForTemplate({
|
||||
userId: ctx.user.id,
|
||||
templateId,
|
||||
recipients: signers.map((signer) => ({
|
||||
id: signer.nativeId,
|
||||
email: signer.email,
|
||||
name: signer.name,
|
||||
})),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to set this field. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@ -1,8 +1,34 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { RecipientRole } from '@documenso/prisma/client';
|
||||
|
||||
export const ZAddSignersMutationSchema = z
|
||||
.object({
|
||||
documentId: z.number(),
|
||||
signers: z.array(
|
||||
z.object({
|
||||
nativeId: z.number().optional(),
|
||||
email: z.string().email().min(1),
|
||||
name: z.string(),
|
||||
role: z.nativeEnum(RecipientRole),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.refine(
|
||||
(schema) => {
|
||||
const emails = schema.signers.map((signer) => signer.email.toLowerCase());
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
// Dirty hack to handle errors when .root is populated for an array type
|
||||
{ message: 'Signers must have unique emails', path: ['signers__root'] },
|
||||
);
|
||||
|
||||
export type TAddSignersMutationSchema = z.infer<typeof ZAddSignersMutationSchema>;
|
||||
|
||||
export const ZAddTemplateSignersMutationSchema = z
|
||||
.object({
|
||||
templateId: z.number(),
|
||||
signers: z.array(
|
||||
z.object({
|
||||
nativeId: z.number().optional(),
|
||||
@ -21,7 +47,7 @@ export const ZAddSignersMutationSchema = z
|
||||
{ message: 'Signers must have unique emails', path: ['signers__root'] },
|
||||
);
|
||||
|
||||
export type TAddSignersMutationSchema = z.infer<typeof ZAddSignersMutationSchema>;
|
||||
export type TAddTemplateSignersMutationSchema = z.infer<typeof ZAddTemplateSignersMutationSchema>;
|
||||
|
||||
export const ZCompleteDocumentWithTokenMutationSchema = z.object({
|
||||
token: z.string(),
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
import { adminRouter } from './admin-router/router';
|
||||
import { apiTokenRouter } from './api-token-router/router';
|
||||
import { authRouter } from './auth-router/router';
|
||||
import { cryptoRouter } from './crypto/router';
|
||||
import { documentRouter } from './document-router/router';
|
||||
import { fieldRouter } from './field-router/router';
|
||||
import { profileRouter } from './profile-router/router';
|
||||
import { recipientRouter } from './recipient-router/router';
|
||||
import { shareLinkRouter } from './share-link-router/router';
|
||||
import { singleplayerRouter } from './singleplayer-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';
|
||||
|
||||
export const appRouter = router({
|
||||
auth: authRouter,
|
||||
crypto: cryptoRouter,
|
||||
profile: profileRouter,
|
||||
document: documentRouter,
|
||||
field: fieldRouter,
|
||||
@ -20,6 +24,8 @@ export const appRouter = router({
|
||||
shareLink: shareLinkRouter,
|
||||
apiToken: apiTokenRouter,
|
||||
singleplayer: singleplayerRouter,
|
||||
team: teamRouter,
|
||||
template: templateRouter,
|
||||
twoFactorAuthentication: twoFactorAuthenticationRouter,
|
||||
});
|
||||
|
||||
|
||||
@ -63,6 +63,7 @@ export const singleplayerRouter = router({
|
||||
// Dummy data.
|
||||
id: -1,
|
||||
documentId: -1,
|
||||
templateId: null,
|
||||
recipientId: -1,
|
||||
});
|
||||
}
|
||||
|
||||
508
packages/trpc/server/team-router/router.ts
Normal file
508
packages/trpc/server/team-router/router.ts
Normal file
@ -0,0 +1,508 @@
|
||||
import { getTeamPrices } from '@documenso/ee/server-only/stripe/get-team-prices';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { acceptTeamInvitation } from '@documenso/lib/server-only/team/accept-team-invitation';
|
||||
import { createTeam } from '@documenso/lib/server-only/team/create-team';
|
||||
import { createTeamBillingPortal } from '@documenso/lib/server-only/team/create-team-billing-portal';
|
||||
import { createTeamPendingCheckoutSession } from '@documenso/lib/server-only/team/create-team-checkout-session';
|
||||
import { createTeamEmailVerification } from '@documenso/lib/server-only/team/create-team-email-verification';
|
||||
import { createTeamMemberInvites } from '@documenso/lib/server-only/team/create-team-member-invites';
|
||||
import { deleteTeam } from '@documenso/lib/server-only/team/delete-team';
|
||||
import { deleteTeamEmail } from '@documenso/lib/server-only/team/delete-team-email';
|
||||
import { deleteTeamEmailVerification } from '@documenso/lib/server-only/team/delete-team-email-verification';
|
||||
import { deleteTeamMemberInvitations } from '@documenso/lib/server-only/team/delete-team-invitations';
|
||||
import { deleteTeamMembers } from '@documenso/lib/server-only/team/delete-team-members';
|
||||
import { deleteTeamPending } from '@documenso/lib/server-only/team/delete-team-pending';
|
||||
import { deleteTeamTransferRequest } from '@documenso/lib/server-only/team/delete-team-transfer-request';
|
||||
import { findTeamInvoices } from '@documenso/lib/server-only/team/find-team-invoices';
|
||||
import { findTeamMemberInvites } from '@documenso/lib/server-only/team/find-team-member-invites';
|
||||
import { findTeamMembers } from '@documenso/lib/server-only/team/find-team-members';
|
||||
import { findTeams } from '@documenso/lib/server-only/team/find-teams';
|
||||
import { findTeamsPending } from '@documenso/lib/server-only/team/find-teams-pending';
|
||||
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||
import { getTeamEmailByEmail } from '@documenso/lib/server-only/team/get-team-email-by-email';
|
||||
import { getTeamInvitations } from '@documenso/lib/server-only/team/get-team-invitations';
|
||||
import { getTeamMembers } from '@documenso/lib/server-only/team/get-team-members';
|
||||
import { getTeams } from '@documenso/lib/server-only/team/get-teams';
|
||||
import { leaveTeam } from '@documenso/lib/server-only/team/leave-team';
|
||||
import { requestTeamOwnershipTransfer } from '@documenso/lib/server-only/team/request-team-ownership-transfer';
|
||||
import { resendTeamEmailVerification } from '@documenso/lib/server-only/team/resend-team-email-verification';
|
||||
import { resendTeamMemberInvitation } from '@documenso/lib/server-only/team/resend-team-member-invitation';
|
||||
import { updateTeam } from '@documenso/lib/server-only/team/update-team';
|
||||
import { updateTeamEmail } from '@documenso/lib/server-only/team/update-team-email';
|
||||
import { updateTeamMember } from '@documenso/lib/server-only/team/update-team-member';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZAcceptTeamInvitationMutationSchema,
|
||||
ZCreateTeamBillingPortalMutationSchema,
|
||||
ZCreateTeamEmailVerificationMutationSchema,
|
||||
ZCreateTeamMemberInvitesMutationSchema,
|
||||
ZCreateTeamMutationSchema,
|
||||
ZCreateTeamPendingCheckoutMutationSchema,
|
||||
ZDeleteTeamEmailMutationSchema,
|
||||
ZDeleteTeamEmailVerificationMutationSchema,
|
||||
ZDeleteTeamMemberInvitationsMutationSchema,
|
||||
ZDeleteTeamMembersMutationSchema,
|
||||
ZDeleteTeamMutationSchema,
|
||||
ZDeleteTeamPendingMutationSchema,
|
||||
ZDeleteTeamTransferRequestMutationSchema,
|
||||
ZFindTeamInvoicesQuerySchema,
|
||||
ZFindTeamMemberInvitesQuerySchema,
|
||||
ZFindTeamMembersQuerySchema,
|
||||
ZFindTeamsPendingQuerySchema,
|
||||
ZFindTeamsQuerySchema,
|
||||
ZGetTeamMembersQuerySchema,
|
||||
ZGetTeamQuerySchema,
|
||||
ZLeaveTeamMutationSchema,
|
||||
ZRequestTeamOwnerhsipTransferMutationSchema,
|
||||
ZResendTeamEmailVerificationMutationSchema,
|
||||
ZResendTeamMemberInvitationMutationSchema,
|
||||
ZUpdateTeamEmailMutationSchema,
|
||||
ZUpdateTeamMemberMutationSchema,
|
||||
ZUpdateTeamMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const teamRouter = router({
|
||||
acceptTeamInvitation: authenticatedProcedure
|
||||
.input(ZAcceptTeamInvitationMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await acceptTeamInvitation({
|
||||
teamId: input.teamId,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
createBillingPortal: authenticatedProcedure
|
||||
.input(ZCreateTeamBillingPortalMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createTeamBillingPortal({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
createTeam: authenticatedProcedure
|
||||
.input(ZCreateTeamMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createTeam({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
createTeamEmailVerification: authenticatedProcedure
|
||||
.input(ZCreateTeamEmailVerificationMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createTeamEmailVerification({
|
||||
teamId: input.teamId,
|
||||
userId: ctx.user.id,
|
||||
data: {
|
||||
email: input.email,
|
||||
name: input.name,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
createTeamMemberInvites: authenticatedProcedure
|
||||
.input(ZCreateTeamMemberInvitesMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createTeamMemberInvites({
|
||||
userId: ctx.user.id,
|
||||
userName: ctx.user.name ?? '',
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
createTeamPendingCheckout: authenticatedProcedure
|
||||
.input(ZCreateTeamPendingCheckoutMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createTeamPendingCheckoutSession({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeam: authenticatedProcedure
|
||||
.input(ZDeleteTeamMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeam({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamEmail: authenticatedProcedure
|
||||
.input(ZDeleteTeamEmailMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamEmail({
|
||||
userId: ctx.user.id,
|
||||
userEmail: ctx.user.email,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamEmailVerification: authenticatedProcedure
|
||||
.input(ZDeleteTeamEmailVerificationMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamEmailVerification({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamMemberInvitations: authenticatedProcedure
|
||||
.input(ZDeleteTeamMemberInvitationsMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamMemberInvitations({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamMembers: authenticatedProcedure
|
||||
.input(ZDeleteTeamMembersMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamMembers({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamPending: authenticatedProcedure
|
||||
.input(ZDeleteTeamPendingMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamPending({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTeamTransferRequest: authenticatedProcedure
|
||||
.input(ZDeleteTeamTransferRequestMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await deleteTeamTransferRequest({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
findTeamInvoices: authenticatedProcedure
|
||||
.input(ZFindTeamInvoicesQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findTeamInvoices({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
findTeamMemberInvites: authenticatedProcedure
|
||||
.input(ZFindTeamMemberInvitesQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findTeamMemberInvites({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
findTeamMembers: authenticatedProcedure
|
||||
.input(ZFindTeamMembersQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findTeamMembers({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
findTeams: authenticatedProcedure.input(ZFindTeamsQuerySchema).query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findTeams({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
findTeamsPending: authenticatedProcedure
|
||||
.input(ZFindTeamsPendingQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await findTeamsPending({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeam: authenticatedProcedure.input(ZGetTeamQuerySchema).query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await getTeamById({ teamId: input.teamId, userId: ctx.user.id });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeamEmailByEmail: authenticatedProcedure.query(async ({ ctx }) => {
|
||||
try {
|
||||
return await getTeamEmailByEmail({ email: ctx.user.email });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeamInvitations: authenticatedProcedure.query(async ({ ctx }) => {
|
||||
try {
|
||||
return await getTeamInvitations({ email: ctx.user.email });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeamMembers: authenticatedProcedure
|
||||
.input(ZGetTeamMembersQuerySchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await getTeamMembers({ teamId: input.teamId, userId: ctx.user.id });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeamPrices: authenticatedProcedure.query(async () => {
|
||||
try {
|
||||
return await getTeamPrices();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
getTeams: authenticatedProcedure.query(async ({ ctx }) => {
|
||||
try {
|
||||
return await getTeams({ userId: ctx.user.id });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
leaveTeam: authenticatedProcedure
|
||||
.input(ZLeaveTeamMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await leaveTeam({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
updateTeam: authenticatedProcedure
|
||||
.input(ZUpdateTeamMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await updateTeam({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
updateTeamEmail: authenticatedProcedure
|
||||
.input(ZUpdateTeamEmailMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await updateTeamEmail({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
updateTeamMember: authenticatedProcedure
|
||||
.input(ZUpdateTeamMemberMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await updateTeamMember({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
requestTeamOwnershipTransfer: authenticatedProcedure
|
||||
.input(ZRequestTeamOwnerhsipTransferMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await requestTeamOwnershipTransfer({
|
||||
userId: ctx.user.id,
|
||||
userName: ctx.user.name ?? '',
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
resendTeamEmailVerification: authenticatedProcedure
|
||||
.input(ZResendTeamEmailVerificationMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
await resendTeamEmailVerification({
|
||||
userId: ctx.user.id,
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
|
||||
resendTeamMemberInvitation: authenticatedProcedure
|
||||
.input(ZResendTeamMemberInvitationMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
await resendTeamMemberInvitation({
|
||||
userId: ctx.user.id,
|
||||
userName: ctx.user.name ?? '',
|
||||
...input,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw AppError.parseErrorToTRPCError(err);
|
||||
}
|
||||
}),
|
||||
});
|
||||
213
packages/trpc/server/team-router/schema.ts
Normal file
213
packages/trpc/server/team-router/schema.ts
Normal file
@ -0,0 +1,213 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
const GenericFindQuerySchema = z.object({
|
||||
term: z.string().optional(),
|
||||
page: z.number().optional(),
|
||||
perPage: z.number().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Restrict team URLs schema.
|
||||
*
|
||||
* Allowed characters:
|
||||
* - Alphanumeric
|
||||
* - Lowercase
|
||||
* - Dashes
|
||||
* - Underscores
|
||||
*
|
||||
* Conditions:
|
||||
* - 3-30 characters
|
||||
* - Cannot start and end with underscores or dashes.
|
||||
* - Cannot contain consecutive underscores or dashes.
|
||||
* - Cannot be a reserved URL in the PROTECTED_TEAM_URLS list
|
||||
*/
|
||||
export const ZTeamUrlSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, { message: 'Team URL must be at least 3 characters long.' })
|
||||
.max(30, { message: 'Team URL must not exceed 30 characters.' })
|
||||
.toLowerCase()
|
||||
.regex(/^[a-z0-9].*[^_-]$/, 'Team URL cannot start or end with dashes or underscores.')
|
||||
.regex(/^(?!.*[-_]{2})/, 'Team URL cannot contain consecutive dashes or underscores.')
|
||||
.regex(
|
||||
/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/,
|
||||
'Team URL can only contain letters, numbers, dashes and underscores.',
|
||||
)
|
||||
.refine((value) => !PROTECTED_TEAM_URLS.includes(value), {
|
||||
message: 'This URL is already in use.',
|
||||
});
|
||||
|
||||
export const ZTeamNameSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, { message: 'Team name must be at least 3 characters long.' })
|
||||
.max(30, { message: 'Team name must not exceed 30 characters.' });
|
||||
|
||||
export const ZAcceptTeamInvitationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZCreateTeamBillingPortalMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZCreateTeamMutationSchema = z.object({
|
||||
teamName: ZTeamNameSchema,
|
||||
teamUrl: ZTeamUrlSchema,
|
||||
});
|
||||
|
||||
export const ZCreateTeamEmailVerificationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
name: z.string().trim().min(1, { message: 'Please enter a valid name.' }),
|
||||
email: z.string().trim().email().toLowerCase().min(1, 'Please enter a valid email.'),
|
||||
});
|
||||
|
||||
export const ZCreateTeamMemberInvitesMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
invitations: z.array(
|
||||
z.object({
|
||||
email: z.string().email().toLowerCase(),
|
||||
role: z.nativeEnum(TeamMemberRole),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export const ZCreateTeamPendingCheckoutMutationSchema = z.object({
|
||||
interval: z.union([z.literal('monthly'), z.literal('yearly')]),
|
||||
pendingTeamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamEmailMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamEmailVerificationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamMembersMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
teamMemberIds: z.array(z.number()),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamMemberInvitationsMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
invitationIds: z.array(z.number()),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamPendingMutationSchema = z.object({
|
||||
pendingTeamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamTransferRequestMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZFindTeamInvoicesQuerySchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZFindTeamMemberInvitesQuerySchema = GenericFindQuerySchema.extend({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZFindTeamMembersQuerySchema = GenericFindQuerySchema.extend({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZFindTeamsQuerySchema = GenericFindQuerySchema;
|
||||
|
||||
export const ZFindTeamsPendingQuerySchema = GenericFindQuerySchema;
|
||||
|
||||
export const ZGetTeamQuerySchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZGetTeamMembersQuerySchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZLeaveTeamMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZUpdateTeamMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
data: z.object({
|
||||
name: ZTeamNameSchema,
|
||||
url: ZTeamUrlSchema,
|
||||
}),
|
||||
});
|
||||
|
||||
export const ZUpdateTeamEmailMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
data: z.object({
|
||||
name: z.string().trim().min(1),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ZUpdateTeamMemberMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
teamMemberId: z.number(),
|
||||
data: z.object({
|
||||
role: z.nativeEnum(TeamMemberRole),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ZRequestTeamOwnerhsipTransferMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
newOwnerUserId: z.number(),
|
||||
clearPaymentMethods: z.boolean(),
|
||||
});
|
||||
|
||||
export const ZResendTeamEmailVerificationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
});
|
||||
|
||||
export const ZResendTeamMemberInvitationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
invitationId: z.number(),
|
||||
});
|
||||
|
||||
export type TCreateTeamMutationSchema = z.infer<typeof ZCreateTeamMutationSchema>;
|
||||
export type TCreateTeamEmailVerificationMutationSchema = z.infer<
|
||||
typeof ZCreateTeamEmailVerificationMutationSchema
|
||||
>;
|
||||
export type TCreateTeamMemberInvitesMutationSchema = z.infer<
|
||||
typeof ZCreateTeamMemberInvitesMutationSchema
|
||||
>;
|
||||
export type TCreateTeamPendingCheckoutMutationSchema = z.infer<
|
||||
typeof ZCreateTeamPendingCheckoutMutationSchema
|
||||
>;
|
||||
export type TDeleteTeamEmailMutationSchema = z.infer<typeof ZDeleteTeamEmailMutationSchema>;
|
||||
export type TDeleteTeamMembersMutationSchema = z.infer<typeof ZDeleteTeamMembersMutationSchema>;
|
||||
export type TDeleteTeamMutationSchema = z.infer<typeof ZDeleteTeamMutationSchema>;
|
||||
export type TDeleteTeamPendingMutationSchema = z.infer<typeof ZDeleteTeamPendingMutationSchema>;
|
||||
export type TDeleteTeamTransferRequestMutationSchema = z.infer<
|
||||
typeof ZDeleteTeamTransferRequestMutationSchema
|
||||
>;
|
||||
export type TFindTeamMemberInvitesQuerySchema = z.infer<typeof ZFindTeamMembersQuerySchema>;
|
||||
export type TFindTeamMembersQuerySchema = z.infer<typeof ZFindTeamMembersQuerySchema>;
|
||||
export type TFindTeamsQuerySchema = z.infer<typeof ZFindTeamsQuerySchema>;
|
||||
export type TFindTeamsPendingQuerySchema = z.infer<typeof ZFindTeamsPendingQuerySchema>;
|
||||
export type TGetTeamQuerySchema = z.infer<typeof ZGetTeamQuerySchema>;
|
||||
export type TGetTeamMembersQuerySchema = z.infer<typeof ZGetTeamMembersQuerySchema>;
|
||||
export type TLeaveTeamMutationSchema = z.infer<typeof ZLeaveTeamMutationSchema>;
|
||||
export type TUpdateTeamMutationSchema = z.infer<typeof ZUpdateTeamMutationSchema>;
|
||||
export type TUpdateTeamEmailMutationSchema = z.infer<typeof ZUpdateTeamEmailMutationSchema>;
|
||||
export type TRequestTeamOwnerhsipTransferMutationSchema = z.infer<
|
||||
typeof ZRequestTeamOwnerhsipTransferMutationSchema
|
||||
>;
|
||||
export type TResendTeamEmailVerificationMutationSchema = z.infer<
|
||||
typeof ZResendTeamEmailVerificationMutationSchema
|
||||
>;
|
||||
export type TResendTeamMemberInvitationMutationSchema = z.infer<
|
||||
typeof ZResendTeamMemberInvitationMutationSchema
|
||||
>;
|
||||
103
packages/trpc/server/template-router/router.ts
Normal file
103
packages/trpc/server/template-router/router.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { createTemplate } from '@documenso/lib/server-only/template/create-template';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
import { duplicateTemplate } from '@documenso/lib/server-only/template/duplicate-template';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZCreateDocumentFromTemplateMutationSchema,
|
||||
ZCreateTemplateMutationSchema,
|
||||
ZDeleteTemplateMutationSchema,
|
||||
ZDuplicateTemplateMutationSchema,
|
||||
} from './schema';
|
||||
|
||||
export const templateRouter = router({
|
||||
createTemplate: authenticatedProcedure
|
||||
.input(ZCreateTemplateMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { teamId, title, templateDocumentDataId } = input;
|
||||
|
||||
return await createTemplate({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
title,
|
||||
templateDocumentDataId,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to create this template. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
createDocumentFromTemplate: authenticatedProcedure
|
||||
.input(ZCreateDocumentFromTemplateMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { templateId } = input;
|
||||
|
||||
const limits = await getServerLimits({ email: ctx.user.email });
|
||||
|
||||
if (limits.remaining.documents === 0) {
|
||||
throw new Error('You have reached your document limit.');
|
||||
}
|
||||
|
||||
return await createDocumentFromTemplate({
|
||||
templateId,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to create this document. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
duplicateTemplate: authenticatedProcedure
|
||||
.input(ZDuplicateTemplateMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { teamId, templateId } = input;
|
||||
|
||||
return await duplicateTemplate({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
templateId,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to duplicate the template. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTemplate: authenticatedProcedure
|
||||
.input(ZDeleteTemplateMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
|
||||
const userId = ctx.user.id;
|
||||
|
||||
return await deleteTemplate({ userId, id });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to delete this template. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
28
packages/trpc/server/template-router/schema.ts
Normal file
28
packages/trpc/server/template-router/schema.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateTemplateMutationSchema = z.object({
|
||||
title: z.string().min(1).trim(),
|
||||
teamId: z.number().optional(),
|
||||
templateDocumentDataId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
});
|
||||
|
||||
export const ZDuplicateTemplateMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
teamId: z.number().optional(),
|
||||
});
|
||||
|
||||
export const ZDeleteTemplateMutationSchema = z.object({
|
||||
id: z.number().min(1),
|
||||
});
|
||||
|
||||
export type TCreateTemplateMutationSchema = z.infer<typeof ZCreateTemplateMutationSchema>;
|
||||
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
||||
typeof ZCreateDocumentFromTemplateMutationSchema
|
||||
>;
|
||||
|
||||
export type TDuplicateTemplateMutationSchema = z.infer<typeof ZDuplicateTemplateMutationSchema>;
|
||||
export type TDeleteTemplateMutationSchema = z.infer<typeof ZDeleteTemplateMutationSchema>;
|
||||
@ -3,7 +3,7 @@ import SuperJSON from 'superjson';
|
||||
|
||||
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
||||
|
||||
import { TrpcContext } from './context';
|
||||
import type { TrpcContext } from './context';
|
||||
|
||||
const t = initTRPC.context<TrpcContext>().create({
|
||||
transformer: SuperJSON,
|
||||
|
||||
@ -6,6 +6,7 @@ import { enableTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/en
|
||||
import { getBackupCodes } from '@documenso/lib/server-only/2fa/get-backup-code';
|
||||
import { setupTwoFactorAuthentication } from '@documenso/lib/server-only/2fa/setup-2fa';
|
||||
import { compareSync } from '@documenso/lib/server-only/auth/hash';
|
||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
@ -23,7 +24,10 @@ export const twoFactorAuthenticationRouter = router({
|
||||
|
||||
const { password } = input;
|
||||
|
||||
return await setupTwoFactorAuthentication({ user, password });
|
||||
return await setupTwoFactorAuthentication({
|
||||
user,
|
||||
password,
|
||||
});
|
||||
}),
|
||||
|
||||
enable: authenticatedProcedure
|
||||
@ -34,7 +38,11 @@ export const twoFactorAuthenticationRouter = router({
|
||||
|
||||
const { code } = input;
|
||||
|
||||
return await enableTwoFactorAuthentication({ user, code });
|
||||
return await enableTwoFactorAuthentication({
|
||||
user,
|
||||
code,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@ -53,7 +61,12 @@ export const twoFactorAuthenticationRouter = router({
|
||||
|
||||
const { password, backupCode } = input;
|
||||
|
||||
return await disableTwoFactorAuthentication({ user, password, backupCode });
|
||||
return await disableTwoFactorAuthentication({
|
||||
user,
|
||||
password,
|
||||
backupCode,
|
||||
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user