zapier webhooks

This commit is contained in:
Catalin Pit
2024-02-23 15:02:53 +02:00
parent 91375a17c2
commit 99a26065a8
13 changed files with 232 additions and 3 deletions

View File

@ -0,0 +1,3 @@
import { testCredentialsHandler } from '@documenso/lib/server-only/public-api/test-credentials';
export default testCredentialsHandler;

View File

@ -0,0 +1,3 @@
import { listDocumentsHandler } from '@documenso/lib/server-only/webhooks/zapier/list-documents';
export default listDocumentsHandler;

View File

@ -0,0 +1,3 @@
import { signedDocumentHandler } from '@documenso/lib/server-only/webhooks/zapier/signed-document';
export default signedDocumentHandler;

View File

@ -0,0 +1,3 @@
import { subscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/subscribe';
export default subscribeHandler;

View File

@ -0,0 +1,3 @@
import { unsubscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/unsubscribe';
export default unsubscribeHandler;

View File

@ -0,0 +1,31 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { getUserByApiToken } from './get-user-by-token';
export const testCredentialsHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { authorization } = req.headers;
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
const [token] = (authorization || '').split('Bearer ').filter((s) => s.length > 0);
if (!token) {
return res.status(500).json({
body: {
message: 'API token was not provided',
},
});
}
const user = await getUserByApiToken({ token });
return res.status(200).json({
username: user.name,
email: user.email,
});
} catch (err) {
return res.status(500).json({
message: 'Internal Server Error',
});
}
};

View File

@ -5,7 +5,7 @@ export type GetAllWebhooksOptions = {
eventTrigger: WebhookTriggerEvents;
};
export const getAllWebhooks = async ({ eventTrigger }: GetAllWebhooksOptions) => {
export const getAllWebhooksByEventTrigger = async ({ eventTrigger }: GetAllWebhooksOptions) => {
return prisma.webhook.findMany({
where: {
eventTriggers: {

View File

@ -0,0 +1,45 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getWebhooksByUserId } from '../get-webhooks-by-user-id';
import { validateApiToken } from './validateApiToken';
export const listDocumentsHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { authorization } = req.headers;
const user = await validateApiToken({ authorization });
const documents = await findDocuments({ userId: user.id });
const allWebhooks = await getWebhooksByUserId(user.id);
if (documents.data.length > 0 && allWebhooks.length > 0) {
const testWebhook = {
event: allWebhooks[0].eventTriggers.toString(),
createdAt: allWebhooks[0].createdAt,
webhookEndpoint: allWebhooks[0].webhookUrl,
payload: {
id: documents.data[0].id,
userId: documents.data[0].userId,
title: documents.data[0].title,
status: documents.data[0].status,
documentDataId: documents.data[0].documentDataId,
createdAt: documents.data[0].createdAt,
updatedAt: documents.data[0].updatedAt,
completedAt: documents.data[0].completedAt,
deletedAt: documents.data[0].deletedAt,
teamId: documents.data[0].teamId,
},
};
return res.status(200).json([testWebhook]);
}
return res.status(200).json([]);
} catch (err) {
console.error(err);
return res.status(500).json({
message: 'Internal Server Error',
});
}
};

View File

@ -0,0 +1,67 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
import { getWebhooksByUserId } from '../get-webhooks-by-user-id';
import { validateApiToken } from './validateApiToken';
export const signedDocumentHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { authorization } = req.headers;
const user = await validateApiToken({ authorization });
const documents = await findDocuments({ userId: user.id });
const allWebhooks = await getWebhooksByUserId(user.id);
const recipients = await getRecipientsForDocument({
documentId: documents.data[0].id,
userId: user.id,
});
if (documents.data.length > 0 && allWebhooks.length > 0 && recipients.length > 0) {
const testWebhook = {
event: allWebhooks[0].eventTriggers.toString(),
createdAt: allWebhooks[0].createdAt,
webhookEndpoint: allWebhooks[0].webhookUrl,
payload: {
id: documents.data[0].id,
userId: documents.data[0].userId,
title: documents.data[0].title,
status: documents.data[0].status,
documentDataId: documents.data[0].documentDataId,
createdAt: documents.data[0].createdAt,
updatedAt: documents.data[0].updatedAt,
completedAt: documents.data[0].completedAt,
deletedAt: documents.data[0].deletedAt,
teamId: documents.data[0].teamId,
Recipient: [
{
id: recipients[0].id,
documentId: recipients[0].documentId,
templateId: recipients[0].templateId,
email: recipients[0].email,
name: recipients[0].name,
token: recipients[0].token,
expired: recipients[0].expired,
signedAt: recipients[0].signedAt,
role: recipients[0].role,
readStatus: recipients[0].readStatus,
signingStatus: recipients[0].signingStatus,
sendStatus: recipients[0].sendStatus,
},
],
},
};
return res.status(200).json([testWebhook]);
}
return res.status(200).json([]);
} catch (err) {
console.error(err);
return res.status(500).json({
message: 'Internal Server Error',
});
}
};

View File

@ -0,0 +1,29 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '@documenso/prisma';
import { validateApiToken } from './validateApiToken';
export const subscribeHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { authorization } = req.headers;
const { webhookUrl, eventTrigger } = req.body;
const user = await validateApiToken({ authorization });
const createdWebhook = await prisma.webhook.create({
data: {
webhookUrl,
eventTriggers: [eventTrigger],
secret: null,
enabled: true,
userId: user.id,
},
});
return res.status(200).json(createdWebhook);
} catch (err) {
return res.status(500).json({
message: 'Internal Server Error',
});
}
};

View File

@ -0,0 +1,26 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { prisma } from '@documenso/prisma';
import { validateApiToken } from './validateApiToken';
export const unsubscribeHandler = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { authorization } = req.headers;
const { webhookId } = req.body;
const user = await validateApiToken({ authorization });
const deletedWebhook = await prisma.webhook.delete({
where: {
id: webhookId,
userId: user.id,
},
});
return res.status(200).json(deletedWebhook);
} catch (err) {
return res.status(500).json({
message: 'Internal Server Error',
});
}
};

View File

@ -0,0 +1,16 @@
import { getUserByApiToken } from '../../public-api/get-user-by-token';
type ValidateApiTokenOptions = {
authorization: string | undefined;
};
export const validateApiToken = async ({ authorization }: ValidateApiTokenOptions) => {
try {
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
const [token] = (authorization || '').split('Bearer ').filter((s) => s.length > 0);
return await getUserByApiToken({ token });
} catch (err) {
throw new Error(`Failed to validate API token`);
}
};

View File

@ -1,6 +1,6 @@
import type { Document, WebhookTriggerEvents } from '@documenso/prisma/client';
import { getAllWebhooks } from '../server-only/webhooks/get-all-webhooks';
import { getAllWebhooksByEventTrigger } from '../server-only/webhooks/get-all-webhooks-by-event-trigger';
import { postWebhookPayload } from './post-webhook-payload';
export type TriggerWebhookOptions = {
@ -10,7 +10,7 @@ export type TriggerWebhookOptions = {
export const triggerWebhook = async ({ eventTrigger, documentData }: TriggerWebhookOptions) => {
try {
const allWebhooks = await getAllWebhooks({ eventTrigger });
const allWebhooks = await getAllWebhooksByEventTrigger({ eventTrigger });
const webhookPromises = allWebhooks.map((webhook) => {
const { webhookUrl, secret } = webhook;