feat: download completed docs via api (#1078)
## Description Allow users to download a completed document via API. ## Testing Performed Tested the code locally by trying to download both draft and completed docs. Works as expected. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Implemented functionality to download signed documents directly from the app. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
ZDeleteDocumentMutationSchema,
|
ZDeleteDocumentMutationSchema,
|
||||||
ZDeleteFieldMutationSchema,
|
ZDeleteFieldMutationSchema,
|
||||||
ZDeleteRecipientMutationSchema,
|
ZDeleteRecipientMutationSchema,
|
||||||
|
ZDownloadDocumentSuccessfulSchema,
|
||||||
ZGetDocumentsQuerySchema,
|
ZGetDocumentsQuerySchema,
|
||||||
ZSendDocumentForSigningMutationSchema,
|
ZSendDocumentForSigningMutationSchema,
|
||||||
ZSuccessfulDocumentResponseSchema,
|
ZSuccessfulDocumentResponseSchema,
|
||||||
@@ -51,6 +52,17 @@ export const ApiContractV1 = c.router(
|
|||||||
summary: 'Get a single document',
|
summary: 'Get a single document',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
downloadSignedDocument: {
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/v1/documents/:id/download',
|
||||||
|
responses: {
|
||||||
|
200: ZDownloadDocumentSuccessfulSchema,
|
||||||
|
401: ZUnsuccessfulResponseSchema,
|
||||||
|
404: ZUnsuccessfulResponseSchema,
|
||||||
|
},
|
||||||
|
summary: 'Download a signed document when the storage transport is S3',
|
||||||
|
},
|
||||||
|
|
||||||
createDocument: {
|
createDocument: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
path: '/api/v1/documents',
|
path: '/api/v1/documents',
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/
|
|||||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||||
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
||||||
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
import {
|
||||||
|
getPresignGetUrl,
|
||||||
|
getPresignPostUrl,
|
||||||
|
} from '@documenso/lib/universal/upload/server-actions';
|
||||||
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { ApiContractV1 } from './contract';
|
import { ApiContractV1 } from './contract';
|
||||||
@@ -83,6 +86,68 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
downloadSignedDocument: authenticatedMiddleware(async (args, user, team) => {
|
||||||
|
const { id: documentId } = args.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
message: 'Please make sure the storage transport is set to S3.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = await getDocumentById({
|
||||||
|
id: Number(documentId),
|
||||||
|
userId: user.id,
|
||||||
|
teamId: team?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!document || !document.documentDataId) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: {
|
||||||
|
message: 'Document not found',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DocumentDataType.S3_PATH !== document.documentData.type) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
message: 'Invalid document data type',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.status !== DocumentStatus.COMPLETED) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
message: 'Document is not completed yet.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { url } = await getPresignGetUrl(document.documentData.data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 200,
|
||||||
|
body: { downloadUrl: url },
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: {
|
||||||
|
message: 'Error downloading the document. Please try again.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
deleteDocument: authenticatedMiddleware(async (args, user, team) => {
|
deleteDocument: authenticatedMiddleware(async (args, user, team) => {
|
||||||
const { id: documentId } = args.params;
|
const { id: documentId } = args.params;
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ export const ZUploadDocumentSuccessfulSchema = z.object({
|
|||||||
key: z.string(),
|
key: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const ZDownloadDocumentSuccessfulSchema = z.object({
|
||||||
|
downloadUrl: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSuccessfulSchema>;
|
export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSuccessfulSchema>;
|
||||||
|
|
||||||
export const ZCreateDocumentMutationSchema = z.object({
|
export const ZCreateDocumentMutationSchema = z.object({
|
||||||
|
|||||||
Reference in New Issue
Block a user