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:
Catalin Pit
2024-04-19 14:04:11 +03:00
committed by GitHub
parent fceb0eaac9
commit 4b90adde6b
3 changed files with 82 additions and 1 deletions

View File

@ -11,6 +11,7 @@ import {
ZDeleteDocumentMutationSchema,
ZDeleteFieldMutationSchema,
ZDeleteRecipientMutationSchema,
ZDownloadDocumentSuccessfulSchema,
ZGetDocumentsQuerySchema,
ZSendDocumentForSigningMutationSchema,
ZSuccessfulDocumentResponseSchema,
@ -51,6 +52,17 @@ export const ApiContractV1 = c.router(
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: {
method: 'POST',
path: '/api/v1/documents',

View File

@ -23,7 +23,10 @@ import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { getFile } from '@documenso/lib/universal/upload/get-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 { 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) => {
const { id: documentId } = args.params;

View File

@ -53,6 +53,10 @@ export const ZUploadDocumentSuccessfulSchema = z.object({
key: z.string(),
});
export const ZDownloadDocumentSuccessfulSchema = z.object({
downloadUrl: z.string(),
});
export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSuccessfulSchema>;
export const ZCreateDocumentMutationSchema = z.object({