feat: add trpc openapi (#1535)

This commit is contained in:
David Nguyen
2024-12-14 01:23:35 +09:00
committed by GitHub
parent f73441ee85
commit b4a7f1887d
42 changed files with 1198 additions and 341 deletions

View File

@ -8,19 +8,40 @@ import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto';
import { AppError } from '@documenso/lib/errors/app-error';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { createDocument } from '@documenso/lib/server-only/document/create-document';
import {
ZCreateDocumentResponseSchema,
createDocument,
} from '@documenso/lib/server-only/document/create-document';
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
import {
ZDuplicateDocumentResponseSchema,
duplicateDocument,
} from '@documenso/lib/server-only/document/duplicate-document-by-id';
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import {
ZFindDocumentsResponseSchema,
findDocuments,
} from '@documenso/lib/server-only/document/find-documents';
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 { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
import {
ZGetDocumentWithDetailsByIdResponseSchema,
getDocumentWithDetailsById,
} from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import {
ZMoveDocumentToTeamResponseSchema,
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 { updateDocumentSettings } from '@documenso/lib/server-only/document/update-document-settings';
import {
ZSendDocumentResponseSchema,
sendDocument,
} from '@documenso/lib/server-only/document/send-document';
import {
ZUpdateDocumentSettingsResponseSchema,
updateDocumentSettings,
} from '@documenso/lib/server-only/document/update-document-settings';
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@ -32,12 +53,13 @@ import {
ZDeleteDocumentMutationSchema,
ZDownloadAuditLogsMutationSchema,
ZDownloadCertificateMutationSchema,
ZDuplicateDocumentMutationSchema,
ZFindDocumentAuditLogsQuerySchema,
ZFindDocumentsQuerySchema,
ZGetDocumentByIdQuerySchema,
ZGetDocumentByTokenQuerySchema,
ZGetDocumentWithDetailsByIdQuerySchema,
ZMoveDocumentsToTeamSchema,
ZMoveDocumentToTeamSchema,
ZResendDocumentMutationSchema,
ZSearchDocumentsMutationSchema,
ZSendDocumentMutationSchema,
@ -49,7 +71,9 @@ import {
} from './schema';
export const documentRouter = router({
// Internal endpoint for now.
/**
* @private
*/
getDocumentById: authenticatedProcedure
.input(ZGetDocumentByIdQuerySchema)
.query(async ({ input, ctx }) => {
@ -59,7 +83,9 @@ export const documentRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
getDocumentByToken: procedure
.input(ZGetDocumentByTokenQuerySchema)
.query(async ({ input, ctx }) => {
@ -71,6 +97,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
findDocuments: authenticatedProcedure
.meta({
openapi: {
@ -82,11 +111,21 @@ export const documentRouter = router({
},
})
.input(ZFindDocumentsQuerySchema)
.output(z.unknown())
.output(ZFindDocumentsResponseSchema)
.query(async ({ input, ctx }) => {
const { user } = ctx;
const { query, teamId, templateId, page, perPage, orderBy, source, status } = input;
const {
query,
teamId,
templateId,
page,
perPage,
orderByDirection,
orderByColumn,
source,
status,
} = input;
const documents = await findDocuments({
userId: user.id,
@ -97,12 +136,17 @@ export const documentRouter = router({
status,
page,
perPage,
orderBy,
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
});
return documents;
}),
/**
* @public
*
* Todo: Refactor to getDocumentById.
*/
getDocumentWithDetailsById: authenticatedProcedure
.meta({
openapi: {
@ -114,7 +158,7 @@ export const documentRouter = router({
},
})
.input(ZGetDocumentWithDetailsByIdQuerySchema)
.output(z.unknown())
.output(ZGetDocumentWithDetailsByIdResponseSchema)
.query(async ({ input, ctx }) => {
return await getDocumentWithDetailsById({
...input,
@ -122,6 +166,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
createDocument: authenticatedProcedure
.meta({
openapi: {
@ -132,7 +179,7 @@ export const documentRouter = router({
},
})
.input(ZCreateDocumentMutationSchema)
.output(z.unknown())
.output(ZCreateDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { title, documentDataId, teamId } = input;
@ -154,7 +201,11 @@ export const documentRouter = router({
});
}),
// Todo: Refactor to updateDocument.
/**
* @public
*
* Todo: Refactor to updateDocument.
*/
setSettingsForDocument: authenticatedProcedure
.meta({
openapi: {
@ -165,7 +216,7 @@ export const documentRouter = router({
},
})
.input(ZSetSettingsForDocumentMutationSchema)
.output(z.unknown())
.output(ZUpdateDocumentSettingsResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, data, meta } = input;
@ -194,6 +245,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
deleteDocument: authenticatedProcedure
.meta({
openapi: {
@ -204,13 +258,13 @@ export const documentRouter = router({
},
})
.input(ZDeleteDocumentMutationSchema)
.output(z.unknown())
.output(z.void())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId } = input;
const userId = ctx.user.id;
return await deleteDocument({
await deleteDocument({
id: documentId,
userId,
teamId,
@ -218,6 +272,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
moveDocumentToTeam: authenticatedProcedure
.meta({
openapi: {
@ -228,8 +285,8 @@ export const documentRouter = router({
tags: ['Documents'],
},
})
.input(ZMoveDocumentsToTeamSchema)
.output(z.unknown())
.input(ZMoveDocumentToTeamSchema)
.output(ZMoveDocumentToTeamResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, teamId } = input;
const userId = ctx.user.id;
@ -242,7 +299,9 @@ export const documentRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
// Should probably use `updateDocument`
setTitleForDocument: authenticatedProcedure
.input(ZSetTitleForDocumentMutationSchema)
@ -260,7 +319,9 @@ export const documentRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
setPasswordForDocument: authenticatedProcedure
.input(ZSetPasswordForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -285,7 +346,9 @@ export const documentRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
setSigningOrderForDocument: authenticatedProcedure
.input(ZSetSigningOrderForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -299,7 +362,9 @@ export const documentRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
updateTypedSignatureSettings: authenticatedProcedure
.input(ZUpdateTypedSignatureSettingsMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -326,8 +391,12 @@ export const documentRouter = router({
});
}),
// Todo: Refactor to distributeDocument.
// Todo: Rework before releasing API.
/**
* @public
*
* Todo: Refactor to distributeDocument.
* Todo: Rework before releasing API.
*/
sendDocument: authenticatedProcedure
.meta({
openapi: {
@ -339,7 +408,7 @@ export const documentRouter = router({
},
})
.input(ZSendDocumentMutationSchema)
.output(z.unknown())
.output(ZSendDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, meta } = input;
@ -374,6 +443,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
resendDocument: authenticatedProcedure
.meta({
openapi: {
@ -386,7 +458,7 @@ export const documentRouter = router({
},
})
.input(ZResendDocumentMutationSchema)
.output(z.unknown())
.output(z.void())
.mutation(async ({ input, ctx }) => {
return await resendDocument({
userId: ctx.user.id,
@ -395,6 +467,9 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
duplicateDocument: authenticatedProcedure
.meta({
openapi: {
@ -404,16 +479,18 @@ export const documentRouter = router({
tags: ['Documents'],
},
})
.input(ZGetDocumentByIdQuerySchema)
.output(z.unknown())
.input(ZDuplicateDocumentMutationSchema)
.output(ZDuplicateDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
return await duplicateDocumentById({
return await duplicateDocument({
userId: ctx.user.id,
...input,
});
}),
// Internal endpoint for now.
/**
* @private
*/
searchDocuments: authenticatedProcedure
.input(ZSearchDocumentsMutationSchema)
.query(async ({ input, ctx }) => {
@ -427,11 +504,21 @@ export const documentRouter = router({
return documents;
}),
// Internal endpoint for now.
/**
* @private
*/
findDocumentAuditLogs: authenticatedProcedure
.input(ZFindDocumentAuditLogsQuerySchema)
.query(async ({ input, ctx }) => {
const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input;
const {
page,
perPage,
documentId,
cursor,
filterForRecentActivity,
orderByColumn,
orderByDirection,
} = input;
return await findDocumentAuditLogs({
page,
@ -439,12 +526,14 @@ export const documentRouter = router({
documentId,
cursor,
filterForRecentActivity,
orderBy,
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
userId: ctx.user.id,
});
}),
// Internal endpoint for now.
/**
* @private
*/
downloadAuditLogs: authenticatedProcedure
.input(ZDownloadAuditLogsMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -473,7 +562,9 @@ export const documentRouter = router({
};
}),
// Internal endpoint for now.
/**
* @private
*/
downloadCertificate: authenticatedProcedure
.input(ZDownloadCertificateMutationSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -23,24 +23,16 @@ export const ZFindDocumentsQuerySchema = ZFindSearchParamsSchema.extend({
templateId: z.number().min(1).optional(),
source: z.nativeEnum(DocumentSource).optional(),
status: z.nativeEnum(DocumentStatus).optional(),
orderBy: z
.object({
column: z.enum(['createdAt']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
orderByColumn: z.enum(['createdAt']).optional(),
orderByDirection: z.enum(['asc', 'desc']).default('desc'),
});
export const ZFindDocumentAuditLogsQuerySchema = ZFindSearchParamsSchema.extend({
documentId: z.number().min(1),
cursor: z.string().optional(),
filterForRecentActivity: z.boolean().optional(),
orderBy: z
.object({
column: z.enum(['createdAt', 'type']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
orderByColumn: z.enum(['createdAt', 'type']).optional(),
orderByDirection: z.enum(['asc', 'desc']).default('desc'),
});
export const ZGetDocumentByIdQuerySchema = z.object({
@ -48,6 +40,11 @@ export const ZGetDocumentByIdQuerySchema = z.object({
teamId: z.number().min(1).optional(),
});
export const ZDuplicateDocumentMutationSchema = z.object({
documentId: z.number().min(1),
teamId: z.number().min(1).optional(),
});
export type TGetDocumentByIdQuerySchema = z.infer<typeof ZGetDocumentByIdQuerySchema>;
export const ZGetDocumentByTokenQuerySchema = z.object({
@ -223,7 +220,7 @@ export const ZDownloadCertificateMutationSchema = z.object({
teamId: z.number().optional(),
});
export const ZMoveDocumentsToTeamSchema = z.object({
export const ZMoveDocumentToTeamSchema = z.object({
documentId: z.number(),
teamId: z.number(),
});

View File

@ -1,9 +1,16 @@
import { z } from 'zod';
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
import {
ZGetFieldByIdResponseSchema,
getFieldById,
} from '@documenso/lib/server-only/field/get-field-by-id';
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 {
ZSetFieldsForDocumentResponseSchema,
setFieldsForDocument,
} from '@documenso/lib/server-only/field/set-fields-for-document';
import {
ZSetFieldsForTemplateResponseSchema,
setFieldsForTemplate,
} from '@documenso/lib/server-only/field/set-fields-for-template';
import { signFieldWithToken } from '@documenso/lib/server-only/field/sign-field-with-token';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@ -17,6 +24,9 @@ import {
} from './schema';
export const fieldRouter = router({
/**
* @public
*/
getField: authenticatedProcedure
.meta({
openapi: {
@ -28,7 +38,7 @@ export const fieldRouter = router({
},
})
.input(ZGetFieldQuerySchema)
.output(z.unknown())
.output(ZGetFieldByIdResponseSchema)
.query(async ({ input, ctx }) => {
const { fieldId, teamId } = input;
@ -39,6 +49,9 @@ export const fieldRouter = router({
});
}),
/**
* @public
*/
addFields: authenticatedProcedure
.meta({
openapi: {
@ -49,7 +62,7 @@ export const fieldRouter = router({
},
})
.input(ZAddFieldsMutationSchema)
.output(z.unknown())
.output(ZSetFieldsForDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, fields } = input;
@ -71,6 +84,9 @@ export const fieldRouter = router({
});
}),
/**
* @public
*/
addTemplateFields: authenticatedProcedure
.meta({
openapi: {
@ -81,7 +97,7 @@ export const fieldRouter = router({
},
})
.input(ZAddTemplateFieldsMutationSchema)
.output(z.unknown())
.output(ZSetFieldsForTemplateResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, fields } = input;
@ -102,7 +118,9 @@ export const fieldRouter = router({
});
}),
// Internal endpoint for now.
/**
* @internal
*/
signFieldWithToken: procedure
.input(ZSignFieldWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -119,7 +137,9 @@ export const fieldRouter = router({
});
}),
// Internal endpoint for now.
/**
* @internal
*/
removeSignedFieldWithToken: procedure
.input(ZRemovedSignedFieldWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -0,0 +1,12 @@
import { generateOpenApiDocument } from 'trpc-openapi';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { appRouter } from './router';
export const openApiDocument = generateOpenApiDocument(appRouter, {
title: 'Do not use.',
version: '0.0.0',
baseUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/api/beta`,
// docsUrl: '', // Todo
});

View File

@ -1,9 +1,13 @@
import { z } from 'zod';
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
import { rejectDocumentWithToken } from '@documenso/lib/server-only/document/reject-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 {
ZSetRecipientsForDocumentResponseSchema,
setRecipientsForDocument,
} from '@documenso/lib/server-only/recipient/set-recipients-for-document';
import {
ZSetRecipientsForTemplateResponseSchema,
setRecipientsForTemplate,
} from '@documenso/lib/server-only/recipient/set-recipients-for-template';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { authenticatedProcedure, procedure, router } from '../trpc';
@ -15,6 +19,9 @@ import {
} from './schema';
export const recipientRouter = router({
/**
* @internal
*/
addSigners: authenticatedProcedure
.meta({
openapi: {
@ -25,7 +32,7 @@ export const recipientRouter = router({
},
})
.input(ZAddSignersMutationSchema)
.output(z.unknown())
.output(ZSetRecipientsForDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, signers } = input;
@ -45,6 +52,9 @@ export const recipientRouter = router({
});
}),
/**
* @internal
*/
addTemplateSigners: authenticatedProcedure
.meta({
openapi: {
@ -55,7 +65,7 @@ export const recipientRouter = router({
},
})
.input(ZAddTemplateSignersMutationSchema)
.output(z.unknown())
.output(ZSetRecipientsForTemplateResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, signers, teamId } = input;
@ -74,7 +84,9 @@ export const recipientRouter = router({
});
}),
// Internal endpoint for now.
/**
* @internal
*/
completeDocumentWithToken: procedure
.input(ZCompleteDocumentWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -89,7 +101,9 @@ export const recipientRouter = router({
});
}),
// Internal endpoint for now.
/**
* @internal
*/
rejectDocumentWithToken: procedure
.input(ZRejectDocumentWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -79,16 +79,17 @@ export const teamRouter = router({
return await getTeams({ userId: ctx.user.id });
}),
// Todo: Public endpoint.
findTeams: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team',
summary: 'Find teams',
description: 'Find your teams based on a search criteria',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'GET',
// path: '/team',
// summary: 'Find teams',
// description: 'Find your teams based on a search criteria',
// tags: ['Teams'],
// },
// })
.input(ZFindTeamsQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
@ -98,30 +99,32 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
getTeam: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}',
summary: 'Get team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}',
// summary: 'Get team',
// tags: ['Teams'],
// },
// })
.input(ZGetTeamQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await getTeamById({ teamId: input.teamId, userId: ctx.user.id });
}),
// Todo: Public endpoint.
createTeam: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/create',
summary: 'Create team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/create',
// summary: 'Create team',
// tags: ['Teams'],
// },
// })
.input(ZCreateTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -131,15 +134,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
updateTeam: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}',
summary: 'Update team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}',
// summary: 'Update team',
// tags: ['Teams'],
// },
// })
.input(ZUpdateTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -149,15 +153,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeam: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/delete',
summary: 'Delete team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/delete',
// summary: 'Delete team',
// tags: ['Teams'],
// },
// })
.input(ZDeleteTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -167,16 +172,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
leaveTeam: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/leave',
summary: 'Leave a team',
description: '',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/leave',
// summary: 'Leave a team',
// description: '',
// tags: ['Teams'],
// },
// })
.input(ZLeaveTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -186,16 +192,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
findTeamMemberInvites: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member/invite',
summary: 'Find member invites',
description: 'Returns pending team member invites',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member/invite',
// summary: 'Find member invites',
// description: 'Returns pending team member invites',
// tags: ['Teams'],
// },
// })
.input(ZFindTeamMemberInvitesQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
@ -205,16 +212,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
createTeamMemberInvites: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite',
summary: 'Invite members',
description: 'Send email invitations to users to join the team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite',
// summary: 'Invite members',
// description: 'Send email invitations to users to join the team',
// tags: ['Teams'],
// },
// })
.input(ZCreateTeamMemberInvitesMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -225,16 +233,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
resendTeamMemberInvitation: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite/{invitationId}/resend',
summary: 'Resend member invite',
description: 'Resend an email invitation to a user to join the team',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite/{invitationId}/resend',
// summary: 'Resend member invite',
// description: 'Resend an email invitation to a user to join the team',
// tags: ['Teams'],
// },
// })
.input(ZResendTeamMemberInvitationMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -245,16 +254,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeamMemberInvitations: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite/delete',
summary: 'Delete member invite',
description: 'Delete a pending team member invite',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite/delete',
// summary: 'Delete member invite',
// description: 'Delete a pending team member invite',
// tags: ['Teams'],
// },
// })
.input(ZDeleteTeamMemberInvitationsMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -264,31 +274,33 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
getTeamMembers: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member',
summary: 'Get members',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member',
// summary: 'Get members',
// tags: ['Teams'],
// },
// })
.input(ZGetTeamMembersQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await getTeamMembers({ teamId: input.teamId, userId: ctx.user.id });
}),
// Todo: Public endpoint.
findTeamMembers: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member/find',
summary: 'Find members',
description: 'Find team members based on a search criteria',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member/find',
// summary: 'Find members',
// description: 'Find team members based on a search criteria',
// tags: ['Teams'],
// },
// })
.input(ZFindTeamMembersQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
@ -298,15 +310,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
updateTeamMember: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/{teamMemberId}',
summary: 'Update member',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/{teamMemberId}',
// summary: 'Update member',
// tags: ['Teams'],
// },
// })
.input(ZUpdateTeamMemberMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -316,16 +329,17 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeamMembers: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/delete',
summary: 'Delete members',
description: '',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/delete',
// summary: 'Delete members',
// description: '',
// tags: ['Teams'],
// },
// })
.input(ZDeleteTeamMembersMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@ -374,16 +388,17 @@ export const teamRouter = router({
return await getTeamInvitations({ email: ctx.user.email });
}),
// Todo: Public endpoint.
updateTeamPublicProfile: authenticatedProcedure
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/profile',
summary: 'Update a team public profile',
description: '',
tags: ['Teams'],
},
})
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/profile',
// summary: 'Update a team public profile',
// description: '',
// tags: ['Teams'],
// },
// })
.input(ZUpdateTeamPublicProfileMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {

View File

@ -4,19 +4,50 @@ import { z } from 'zod';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import {
ZGetDocumentWithDetailsByIdResponseSchema,
getDocumentWithDetailsById,
} from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { createDocumentFromDirectTemplate } from '@documenso/lib/server-only/template/create-document-from-direct-template';
import {
ZCreateDocumentFromDirectTemplateResponseSchema,
createDocumentFromDirectTemplate,
} from '@documenso/lib/server-only/template/create-document-from-direct-template';
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
import { createTemplate } from '@documenso/lib/server-only/template/create-template';
import { createTemplateDirectLink } from '@documenso/lib/server-only/template/create-template-direct-link';
import {
ZCreateTemplateResponseSchema,
createTemplate,
} from '@documenso/lib/server-only/template/create-template';
import {
ZCreateTemplateDirectLinkResponseSchema,
createTemplateDirectLink,
} from '@documenso/lib/server-only/template/create-template-direct-link';
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
import { deleteTemplateDirectLink } from '@documenso/lib/server-only/template/delete-template-direct-link';
import { duplicateTemplate } from '@documenso/lib/server-only/template/duplicate-template';
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
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 { updateTemplateSettings } from '@documenso/lib/server-only/template/update-template-settings';
import {
ZDuplicateTemplateResponseSchema,
duplicateTemplate,
} from '@documenso/lib/server-only/template/duplicate-template';
import {
ZFindTemplatesResponseSchema,
findTemplates,
} from '@documenso/lib/server-only/template/find-templates';
import {
ZGetTemplateByIdResponseSchema,
getTemplateById,
} from '@documenso/lib/server-only/template/get-template-by-id';
import {
ZMoveTemplateToTeamResponseSchema,
moveTemplateToTeam,
} from '@documenso/lib/server-only/template/move-template-to-team';
import {
ZToggleTemplateDirectLinkResponseSchema,
toggleTemplateDirectLink,
} from '@documenso/lib/server-only/template/toggle-template-direct-link';
import {
ZUpdateTemplateSettingsResponseSchema,
updateTemplateSettings,
} from '@documenso/lib/server-only/template/update-template-settings';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { Document } from '@documenso/prisma/client';
@ -39,6 +70,9 @@ import {
} from './schema';
export const templateRouter = router({
/**
* @public
*/
findTemplates: authenticatedProcedure
.meta({
openapi: {
@ -50,7 +84,7 @@ export const templateRouter = router({
},
})
.input(ZFindTemplatesQuerySchema)
.output(z.unknown())
.output(ZFindTemplatesResponseSchema)
.query(async ({ input, ctx }) => {
return await findTemplates({
userId: ctx.user.id,
@ -58,6 +92,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
getTemplateById: authenticatedProcedure
.meta({
openapi: {
@ -68,7 +105,7 @@ export const templateRouter = router({
},
})
.input(ZGetTemplateByIdQuerySchema)
.output(z.unknown())
.output(ZGetTemplateByIdResponseSchema)
.query(async ({ input, ctx }) => {
const { templateId, teamId } = input;
@ -79,6 +116,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
createTemplate: authenticatedProcedure
.meta({
openapi: {
@ -90,7 +130,7 @@ export const templateRouter = router({
},
})
.input(ZCreateTemplateMutationSchema)
.output(z.unknown())
.output(ZCreateTemplateResponseSchema)
.mutation(async ({ input, ctx }) => {
const { teamId, title, templateDocumentDataId } = input;
@ -102,6 +142,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
updateTemplate: authenticatedProcedure
.meta({
openapi: {
@ -112,7 +155,7 @@ export const templateRouter = router({
},
})
.input(ZUpdateTemplateSettingsMutationSchema)
.output(z.unknown())
.output(ZUpdateTemplateSettingsResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, data, meta } = input;
@ -133,6 +176,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
duplicateTemplate: authenticatedProcedure
.meta({
openapi: {
@ -143,7 +189,7 @@ export const templateRouter = router({
},
})
.input(ZDuplicateTemplateMutationSchema)
.output(z.unknown())
.output(ZDuplicateTemplateResponseSchema)
.mutation(async ({ input, ctx }) => {
const { teamId, templateId } = input;
@ -154,6 +200,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
deleteTemplate: authenticatedProcedure
.meta({
openapi: {
@ -164,7 +213,7 @@ export const templateRouter = router({
},
})
.input(ZDeleteTemplateMutationSchema)
.output(z.unknown())
.output(z.void())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId } = input;
@ -173,6 +222,9 @@ export const templateRouter = router({
await deleteTemplate({ userId, id: templateId, teamId });
}),
/**
* @public
*/
createDocumentFromTemplate: authenticatedProcedure
.meta({
openapi: {
@ -184,9 +236,9 @@ export const templateRouter = router({
},
})
.input(ZCreateDocumentFromTemplateMutationSchema)
.output(z.unknown())
.output(ZGetDocumentWithDetailsByIdResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, recipients } = input;
const { templateId, teamId, recipients, distributeDocument } = input;
const limits = await getServerLimits({ email: ctx.user.email, teamId });
@ -196,7 +248,7 @@ export const templateRouter = router({
const requestMetadata = extractNextApiRequestMetadata(ctx.req);
let document: Document = await createDocumentFromTemplate({
const document: Document = await createDocumentFromTemplate({
templateId,
teamId,
userId: ctx.user.id,
@ -204,8 +256,8 @@ export const templateRouter = router({
requestMetadata,
});
if (input.distributeDocument) {
document = await sendDocument({
if (distributeDocument) {
await sendDocument({
documentId: document.id,
userId: ctx.user.id,
teamId,
@ -217,9 +269,16 @@ export const templateRouter = router({
});
}
return document;
return getDocumentWithDetailsById({
documentId: document.id,
userId: ctx.user.id,
teamId,
});
}),
/**
* @public
*/
createDocumentFromDirectTemplate: maybeAuthenticatedProcedure
.meta({
openapi: {
@ -231,7 +290,7 @@ export const templateRouter = router({
},
})
.input(ZCreateDocumentFromDirectTemplateMutationSchema)
.output(z.unknown())
.output(ZCreateDocumentFromDirectTemplateResponseSchema)
.mutation(async ({ input, ctx }) => {
const {
directRecipientName,
@ -262,7 +321,9 @@ export const templateRouter = router({
});
}),
// Internal endpoint for now.
/**
* @private
*/
setSigningOrderForTemplate: authenticatedProcedure
.input(ZSetSigningOrderForTemplateMutationSchema)
.mutation(async ({ input, ctx }) => {
@ -278,6 +339,9 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
createTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@ -289,7 +353,7 @@ export const templateRouter = router({
},
})
.input(ZCreateTemplateDirectLinkMutationSchema)
.output(z.unknown())
.output(ZCreateTemplateDirectLinkResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, directRecipientId } = input;
@ -308,6 +372,9 @@ export const templateRouter = router({
return await createTemplateDirectLink({ userId, templateId, directRecipientId });
}),
/**
* @public
*/
deleteTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@ -319,7 +386,7 @@ export const templateRouter = router({
},
})
.input(ZDeleteTemplateDirectLinkMutationSchema)
.output(z.unknown())
.output(z.void())
.mutation(async ({ input, ctx }) => {
const { templateId } = input;
@ -328,6 +395,9 @@ export const templateRouter = router({
await deleteTemplateDirectLink({ userId, templateId });
}),
/**
* @public
*/
toggleTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@ -339,7 +409,7 @@ export const templateRouter = router({
},
})
.input(ZToggleTemplateDirectLinkMutationSchema)
.output(z.unknown())
.output(ZToggleTemplateDirectLinkResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, enabled } = input;
@ -348,6 +418,9 @@ export const templateRouter = router({
return await toggleTemplateDirectLink({ userId, templateId, enabled });
}),
/**
* @public
*/
moveTemplateToTeam: authenticatedProcedure
.meta({
openapi: {
@ -359,7 +432,7 @@ export const templateRouter = router({
},
})
.input(ZMoveTemplatesToTeamSchema)
.output(z.unknown())
.output(ZMoveTemplateToTeamResponseSchema)
.mutation(async ({ input, ctx }) => {
const { templateId, teamId } = input;
const userId = ctx.user.id;
@ -371,7 +444,9 @@ export const templateRouter = router({
});
}),
// Internal endpoint for now.
/**
* @internal
*/
updateTemplateTypedSignatureSettings: authenticatedProcedure
.input(ZUpdateTemplateTypedSignatureSettingsMutationSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -1,43 +1,73 @@
import { TRPCError, initTRPC } from '@trpc/server';
import SuperJSON from 'superjson';
import type { OpenApiMeta } from 'trpc-openapi';
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 { TrpcContext } from './context';
const t = initTRPC.context<TrpcContext>().create({
transformer: SuperJSON,
errorFormatter(opts) {
const { shape, error } = opts;
const t = initTRPC
.meta<OpenApiMeta>()
.context<TrpcContext>()
.create({
transformer: SuperJSON,
errorFormatter(opts) {
const { shape, error } = opts;
const originalError = error.cause;
const originalError = error.cause;
let data: Record<string, unknown> = shape.data;
let data: Record<string, unknown> = shape.data;
if (originalError instanceof AppError) {
data = {
...data,
appError: AppError.toJSON(originalError),
code: originalError.code,
httpStatus:
originalError.statusCode ??
genericErrorCodeToTrpcErrorCodeMap[originalError.code]?.status ??
500,
// Default unknown errors to 400, since if you're throwing an AppError it is expected
// that you already know what you're doing.
if (originalError instanceof AppError) {
data = {
...data,
appError: AppError.toJSON(originalError),
code: originalError.code,
httpStatus:
originalError.statusCode ??
genericErrorCodeToTrpcErrorCodeMap[originalError.code]?.status ??
400,
};
}
return {
...shape,
data,
};
}
return {
...shape,
data,
};
},
});
},
});
/**
* Middlewares
*/
export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
const authorizationHeader = ctx.req.headers.authorization;
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
if (authorizationHeader) {
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
const [token] = (authorizationHeader || '').split('Bearer ').filter((s) => s.length > 0);
if (!token) {
throw new Error('Token was not provided for authenticated middleware');
}
const apiToken = await getApiTokenByToken({ token });
return await next({
ctx: {
...ctx,
user: apiToken.user,
session: null,
source: 'api',
},
});
}
if (!ctx.session) {
throw new TRPCError({
code: 'UNAUTHORIZED',
@ -50,6 +80,7 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
...ctx,
user: ctx.user,
session: ctx.session,
source: 'app',
},
});
});