Merge branch 'main' into feat/unlink-documents-deleted-org

This commit is contained in:
Lucas Smith
2025-11-25 11:45:42 +11:00
committed by GitHub
7 changed files with 99 additions and 73 deletions

View File

@ -23,7 +23,7 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
</Text>
)}
{branding.brandingCompanyDetails ? (
{branding.brandingEnabled && branding.brandingCompanyDetails && (
<Text className="my-8 text-sm text-slate-400">
{branding.brandingCompanyDetails.split('\n').map((line, idx) => {
return (
@ -34,7 +34,9 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
);
})}
</Text>
) : (
)}
{!branding.brandingEnabled && (
<Text className="my-8 text-sm text-slate-400">
Documenso, Inc.
<br />

View File

@ -22,53 +22,51 @@ export const run = async ({
const { webhookUrl: url, secret } = webhook;
await io.runTask('execute-webhook', async () => {
const payloadData = {
event,
payload: data,
createdAt: new Date().toISOString(),
webhookEndpoint: url,
};
const payloadData = {
event,
payload: data,
createdAt: new Date().toISOString(),
webhookEndpoint: url,
};
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(payloadData),
headers: {
'Content-Type': 'application/json',
'X-Documenso-Secret': secret ?? '',
},
});
const body = await response.text();
let responseBody: Prisma.InputJsonValue | Prisma.JsonNullValueInput = Prisma.JsonNull;
try {
responseBody = JSON.parse(body);
} catch (err) {
responseBody = body;
}
await prisma.webhookCall.create({
data: {
url,
event,
status: response.ok ? WebhookCallStatus.SUCCESS : WebhookCallStatus.FAILED,
requestBody: payloadData as Prisma.InputJsonValue,
responseCode: response.status,
responseBody,
responseHeaders: Object.fromEntries(response.headers.entries()),
webhookId: webhook.id,
},
});
if (!response.ok) {
throw new Error(`Webhook execution failed with status ${response.status}`);
}
return {
success: response.ok,
status: response.status,
};
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(payloadData),
headers: {
'Content-Type': 'application/json',
'X-Documenso-Secret': secret ?? '',
},
});
const body = await response.text();
let responseBody: Prisma.InputJsonValue | Prisma.JsonNullValueInput = Prisma.JsonNull;
try {
responseBody = JSON.parse(body);
} catch (err) {
responseBody = body;
}
await prisma.webhookCall.create({
data: {
url,
event,
status: response.ok ? WebhookCallStatus.SUCCESS : WebhookCallStatus.FAILED,
requestBody: payloadData as Prisma.InputJsonValue,
responseCode: response.status,
responseBody,
responseHeaders: Object.fromEntries(response.headers.entries()),
webhookId: webhook.id,
},
});
if (!response.ok) {
throw new Error(`Webhook execution failed with status ${response.status}`);
}
return {
success: response.ok,
status: response.status,
};
};

View File

@ -13,6 +13,7 @@ export type HandlerTriggerWebhooksResponse =
error: string;
};
// Todo: [Webhooks] delete after deployment.
export const handlerTriggerWebhooks = async (req: Request) => {
const signature = req.headers.get('x-webhook-signature');

View File

@ -1,7 +1,6 @@
import type { WebhookTriggerEvents } from '@prisma/client';
import { NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../../constants/app';
import { sign } from '../../crypto/sign';
import { jobs } from '../../../jobs/client';
import { getAllWebhooksByEventTrigger } from '../get-all-webhooks-by-event-trigger';
export type TriggerWebhookOptions = {
@ -13,35 +12,26 @@ export type TriggerWebhookOptions = {
export const triggerWebhook = async ({ event, data, userId, teamId }: TriggerWebhookOptions) => {
try {
const body = {
event,
data,
userId,
teamId,
};
const registeredWebhooks = await getAllWebhooksByEventTrigger({ event, userId, teamId });
if (registeredWebhooks.length === 0) {
return;
}
const signature = sign(body);
await Promise.race([
fetch(`${NEXT_PRIVATE_INTERNAL_WEBAPP_URL()}/api/webhook/trigger`, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-webhook-signature': signature,
},
body: JSON.stringify(body),
await Promise.allSettled(
registeredWebhooks.map(async (webhook) => {
await jobs.triggerJob({
name: 'internal.execute-webhook',
payload: {
event,
webhookId: webhook.id,
data,
},
});
}),
new Promise((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), 500);
}),
]).catch(() => null);
);
} catch (err) {
console.error(err);
throw new Error(`Failed to trigger webhook`);
}
};

View File

@ -457,8 +457,10 @@ export const templateRouter = router({
recipients,
distributeDocument,
customDocumentDataId,
prefillFields,
folderId,
prefillFields,
override,
attachments,
} = input;
ctx.logger.info({
@ -495,6 +497,8 @@ export const templateRouter = router({
requestMetadata: ctx.metadata,
folderId,
prefillFields,
override,
attachments,
});
if (distributeDocument) {

View File

@ -133,12 +133,42 @@ export const ZCreateDocumentFromTemplateRequestSchema = z.object({
'The ID of the folder to create the document in. If not provided, the document will be created in the root folder.',
)
.optional(),
prefillFields: z
.array(ZFieldMetaPrefillFieldsSchema)
.describe(
'The fields to prefill on the document before sending it out. Useful when you want to create a document from an existing template and pre-fill the fields with specific values.',
)
.optional(),
override: z
.object({
title: z.string().min(1).max(255).optional(),
subject: ZDocumentMetaSubjectSchema.optional(),
message: ZDocumentMetaMessageSchema.optional(),
timezone: ZDocumentMetaTimezoneSchema.optional(),
dateFormat: ZDocumentMetaDateFormatSchema.optional(),
redirectUrl: ZDocumentMetaRedirectUrlSchema.optional(),
distributionMethod: ZDocumentMetaDistributionMethodSchema.optional(),
emailSettings: ZDocumentEmailSettingsSchema.optional(),
language: ZDocumentMetaLanguageSchema.optional(),
typedSignatureEnabled: ZDocumentMetaTypedSignatureEnabledSchema.optional(),
uploadSignatureEnabled: ZDocumentMetaUploadSignatureEnabledSchema.optional(),
drawSignatureEnabled: ZDocumentMetaDrawSignatureEnabledSchema.optional(),
allowDictateNextSigner: z.boolean().optional(),
})
.describe('Override values from the template for the created document.')
.optional(),
attachments: z
.array(
z.object({
label: z.string().min(1, 'Label is required'),
data: z.string().url('Must be a valid URL'),
type: ZEnvelopeAttachmentTypeSchema.optional().default('link'),
}),
)
.optional(),
});
export const ZCreateDocumentFromTemplateResponseSchema = ZDocumentSchema;