This commit is contained in:
Mythie
2025-01-02 15:33:37 +11:00
committed by David Nguyen
parent 9183f668d3
commit f7a98180d7
413 changed files with 29538 additions and 1606 deletions

View File

@ -1,4 +1,4 @@
import type { DocumentData } from '@documenso/prisma/client';
import type { DocumentData } from '@prisma/client';
import { getFile } from '../universal/upload/get-file';
import { downloadFile } from './download-file';

View File

@ -1,8 +1,9 @@
import { useCallback, useEffect, useState } from 'react';
import type { Field } from '@prisma/client';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import type { Field } from '@documenso/prisma/client';
export const useFieldPageCoords = (field: Field) => {
const [coords, setCoords] = useState({

View File

@ -1,9 +1,9 @@
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { useLocation, useNavigate, useSearchParams } from 'react-router';
export const useUpdateSearchParams = () => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const navigate = useNavigate();
const { pathname } = useLocation();
const [searchParams] = useSearchParams();
return (params: Record<string, string | number | boolean | null | undefined>) => {
const nextSearchParams = new URLSearchParams(searchParams?.toString() ?? '');
@ -16,6 +16,6 @@ export const useUpdateSearchParams = () => {
}
});
router.push(`${pathname}?${nextSearchParams.toString()}`);
void navigate(`${pathname}?${nextSearchParams.toString()}`);
};
};

View File

@ -1,17 +1,11 @@
import 'server-only';
import { cookies, headers } from 'next/headers';
import type { I18n, Messages } from '@lingui/core';
import { setupI18n } from '@lingui/core';
import { setI18n } from '@lingui/react/server';
import {
APP_I18N_OPTIONS,
SUPPORTED_LANGUAGE_CODES,
isValidLanguageCode,
} from '../../constants/i18n';
import { extractLocaleData } from '../../utils/i18n';
import { remember } from '../../utils/remember';
type SupportedLanguages = (typeof SUPPORTED_LANGUAGE_CODES)[number];
@ -21,7 +15,8 @@ export async function loadCatalog(lang: SupportedLanguages): Promise<{
}> {
const extension = process.env.NODE_ENV === 'development' ? 'po' : 'js';
const { messages } = await import(`../../translations/${lang}/web.${extension}`);
// const { messages } = await import(`../../translations/${lang}/web.${extension}`);
const messages = {};
return {
[lang]: messages,
@ -71,29 +66,3 @@ export const getI18nInstance = async (lang?: SupportedLanguages | (string & {}))
return instances[lang] ?? instances[APP_I18N_OPTIONS.sourceLang];
};
/**
* This needs to be run in all layouts and page server components that require i18n.
*
* https://lingui.dev/tutorials/react-rsc#pages-layouts-and-lingui
*/
export const setupI18nSSR = async () => {
const { lang, locales } = extractLocaleData({
cookies: cookies(),
headers: headers(),
});
// Get and set a ready-made i18n instance for the given language.
const i18n = await getI18nInstance(lang);
// Reactivate the i18n instance with the locale for date and number formatting.
i18n.activate(lang, locales);
setI18n(i18n);
return {
lang,
locales,
i18n,
};
};

View File

@ -1,11 +1,11 @@
import type { Recipient } from '@documenso/prisma/client';
import type { Recipient } from '@prisma/client';
import {
DocumentDistributionMethod,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
} from '@prisma/client';
export enum RecipientStatusType {
COMPLETED = 'completed',

View File

@ -3,7 +3,8 @@ import { env } from 'next-runtime-env';
export const APP_DOCUMENT_UPLOAD_SIZE_LIMIT =
Number(process.env.NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT) || 50;
export const NEXT_PUBLIC_WEBAPP_URL = () => env('NEXT_PUBLIC_WEBAPP_URL');
// Todo: env('NEXT_PUBLIC_WEBAPP_URL')
export const NEXT_PUBLIC_WEBAPP_URL = () => 'http://localhost:3000';
export const NEXT_PUBLIC_MARKETING_URL = () => env('NEXT_PUBLIC_MARKETING_URL');
export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL =
process.env.NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? NEXT_PUBLIC_WEBAPP_URL();

View File

@ -1,15 +1,16 @@
import { IdentityProvider, UserSecurityAuditLogType } from '@documenso/prisma/client';
// Todo: Reimport
export const SALT_ROUNDS = 12;
export const IDENTITY_PROVIDER_NAME: { [key in IdentityProvider]: string } = {
[IdentityProvider.DOCUMENSO]: 'Documenso',
[IdentityProvider.GOOGLE]: 'Google',
[IdentityProvider.OIDC]: 'OIDC',
export const IDENTITY_PROVIDER_NAME: Record<string, string> = {
DOCUMENSO: 'Documenso',
GOOGLE: 'Google',
OIDC: 'OIDC',
};
export const IS_GOOGLE_SSO_ENABLED = Boolean(
process.env.NEXT_PRIVATE_GOOGLE_CLIENT_ID && process.env.NEXT_PRIVATE_GOOGLE_CLIENT_SECRET,
import.meta.env.NEXT_PRIVATE_GOOGLE_CLIENT_ID &&
import.meta.env.NEXT_PRIVATE_GOOGLE_CLIENT_SECRET,
);
export const IS_OIDC_SSO_ENABLED = Boolean(
@ -20,21 +21,21 @@ export const IS_OIDC_SSO_ENABLED = Boolean(
export const OIDC_PROVIDER_LABEL = process.env.NEXT_PRIVATE_OIDC_PROVIDER_LABEL;
export const USER_SECURITY_AUDIT_LOG_MAP: { [key in UserSecurityAuditLogType]: string } = {
[UserSecurityAuditLogType.ACCOUNT_SSO_LINK]: 'Linked account to SSO',
[UserSecurityAuditLogType.ACCOUNT_PROFILE_UPDATE]: 'Profile updated',
[UserSecurityAuditLogType.AUTH_2FA_DISABLE]: '2FA Disabled',
[UserSecurityAuditLogType.AUTH_2FA_ENABLE]: '2FA Enabled',
[UserSecurityAuditLogType.PASSKEY_CREATED]: 'Passkey created',
[UserSecurityAuditLogType.PASSKEY_DELETED]: 'Passkey deleted',
[UserSecurityAuditLogType.PASSKEY_UPDATED]: 'Passkey updated',
[UserSecurityAuditLogType.PASSWORD_RESET]: 'Password reset',
[UserSecurityAuditLogType.PASSWORD_UPDATE]: 'Password updated',
[UserSecurityAuditLogType.SIGN_OUT]: 'Signed Out',
[UserSecurityAuditLogType.SIGN_IN]: 'Signed In',
[UserSecurityAuditLogType.SIGN_IN_FAIL]: 'Sign in attempt failed',
[UserSecurityAuditLogType.SIGN_IN_PASSKEY_FAIL]: 'Passkey sign in failed',
[UserSecurityAuditLogType.SIGN_IN_2FA_FAIL]: 'Sign in 2FA attempt failed',
export const USER_SECURITY_AUDIT_LOG_MAP: Record<string, string> = {
ACCOUNT_SSO_LINK: 'Linked account to SSO',
ACCOUNT_PROFILE_UPDATE: 'Profile updated',
AUTH_2FA_DISABLE: '2FA Disabled',
AUTH_2FA_ENABLE: '2FA Enabled',
PASSKEY_CREATED: 'Passkey created',
PASSKEY_DELETED: 'Passkey deleted',
PASSKEY_UPDATED: 'Passkey updated',
PASSWORD_RESET: 'Password reset',
PASSWORD_UPDATE: 'Password updated',
SIGN_OUT: 'Signed Out',
SIGN_IN: 'Signed In',
SIGN_IN_FAIL: 'Sign in attempt failed',
SIGN_IN_PASSKEY_FAIL: 'Passkey sign in failed',
SIGN_IN_2FA_FAIL: 'Sign in 2FA attempt failed',
};
/**

View File

@ -1,6 +1,6 @@
export const DOCUMENSO_ENCRYPTION_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_KEY;
export const DOCUMENSO_ENCRYPTION_KEY = '12345678912345678912345678912457';
export const DOCUMENSO_ENCRYPTION_SECONDARY_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY;
export const DOCUMENSO_ENCRYPTION_SECONDARY_KEY = '12345678912345678912345678912458';
if (typeof window === 'undefined') {
if (!DOCUMENSO_ENCRYPTION_KEY || !DOCUMENSO_ENCRYPTION_SECONDARY_KEY) {

View File

@ -1,7 +1,6 @@
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/macro';
import { DocumentDistributionMethod, DocumentStatus } from '@documenso/prisma/client';
import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client';
export const DOCUMENT_STATUS: {
[status in DocumentStatus]: { description: MessageDescriptor };

View File

@ -1,7 +1,6 @@
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/macro';
import { RecipientRole } from '@documenso/prisma/client';
import { RecipientRole } from '@prisma/client';
export const RECIPIENT_ROLES_DESCRIPTION = {
[RecipientRole.APPROVER]: {

View File

@ -1,7 +1,6 @@
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/macro';
import { TeamMemberRole } from '@documenso/prisma/client';
import { TeamMemberRole } from '@prisma/client';
export const TEAM_URL_ROOT_REGEX = new RegExp('^/t/[^/]+$');
export const TEAM_URL_REGEX = new RegExp('^/t/[^/]+');

View File

@ -1,5 +1,6 @@
import { JobClient } from './client/client';
import { SEND_CONFIRMATION_EMAIL_JOB_DEFINITION } from './definitions/emails/send-confirmation-email';
import { SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION } from './definitions/emails/send-password-reset-success-email';
import { SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-recipient-signed-email';
import { SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION } from './definitions/emails/send-rejection-emails';
import { SEND_SIGNING_EMAIL_JOB_DEFINITION } from './definitions/emails/send-signing-email';
@ -19,6 +20,7 @@ export const jobsClient = new JobClient([
SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION,
SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION,
SEAL_DOCUMENT_JOB_DEFINITION,
SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION,
SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION,
SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION,
] as const);

View File

@ -1,5 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { Context as HonoContext } from 'hono';
import type { JobDefinition, SimpleTriggerJobOptions } from './_internal/job';
export abstract class BaseJobProvider {
@ -16,4 +18,8 @@ export abstract class BaseJobProvider {
public getApiHandler(): (req: NextApiRequest, res: NextApiResponse) => Promise<Response | void> {
throw new Error('Not implemented');
}
public getHonoApiHandler(): (req: HonoContext) => Promise<Response | void> {
throw new Error('Not implemented');
}
}

View File

@ -27,4 +27,8 @@ export class JobClient<T extends ReadonlyArray<JobDefinition> = []> {
public getApiHandler() {
return this._provider.getApiHandler();
}
public getHonoApiHandler() {
return this._provider.getHonoApiHandler();
}
}

View File

@ -1,8 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextRequest } from 'next/server';
import type { Context as HonoContext } from 'hono';
import type { Context, Handler, InngestFunction } from 'inngest';
import { Inngest as InngestClient } from 'inngest';
import { serve as createHonoPagesRoute } from 'inngest/hono';
import type { Logger } from 'inngest/middleware/logger';
import { serve as createPagesRoute } from 'inngest/next';
import { json } from 'micro';
@ -94,6 +96,18 @@ export class InngestJobProvider extends BaseJobProvider {
};
}
// Todo: Do we need to handle the above?
public getHonoApiHandler() {
return async (context: HonoContext) => {
const handler = createHonoPagesRoute({
client: this._client,
functions: this._functions,
});
return await handler(context);
};
}
private convertInngestIoToJobRunIo(ctx: Context.Any & { logger: Logger }) {
const { step } = ctx;

View File

@ -1,10 +1,11 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { sha256 } from '@noble/hashes/sha256';
import { BackgroundJobStatus, Prisma } from '@prisma/client';
import type { Context as HonoContext } from 'hono';
import { json } from 'micro';
import { prisma } from '@documenso/prisma';
import { BackgroundJobStatus, Prisma } from '@documenso/prisma/client';
import { NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
import { sign } from '../../server-only/crypto/sign';
@ -213,6 +214,152 @@ export class LocalJobProvider extends BaseJobProvider {
};
}
public getHonoApiHandler(): (context: HonoContext) => Promise<Response | void> {
return async (context: HonoContext) => {
const req = context.req;
if (req.method !== 'POST') {
context.text('Method not allowed', 405);
return;
}
const jobId = req.header('x-job-id');
const signature = req.header('x-job-signature');
const isRetry = req.header('x-job-retry') !== undefined;
const options = await req
.json()
.then(async (data) => ZSimpleTriggerJobOptionsSchema.parseAsync(data))
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
.then((data) => data as SimpleTriggerJobOptions)
.catch(() => null);
if (!options) {
context.text('Bad request', 400);
return;
}
const definition = this._jobDefinitions[options.name];
if (
typeof jobId !== 'string' ||
typeof signature !== 'string' ||
typeof options !== 'object'
) {
context.text('Bad request', 400);
return;
}
if (!definition) {
context.text('Job not found', 404);
return;
}
if (definition && !definition.enabled) {
console.log('Attempted to trigger a disabled job', options.name);
context.text('Job not found', 404);
return;
}
if (!signature || !verify(options, signature)) {
context.text('Unauthorized', 401);
return;
}
if (definition.trigger.schema) {
const result = definition.trigger.schema.safeParse(options.payload);
if (!result.success) {
context.text('Bad request', 400);
return;
}
}
console.log(`[JOBS]: Triggering job ${options.name} with payload`, options.payload);
let backgroundJob = await prisma.backgroundJob
.update({
where: {
id: jobId,
status: BackgroundJobStatus.PENDING,
},
data: {
status: BackgroundJobStatus.PROCESSING,
retried: {
increment: isRetry ? 1 : 0,
},
lastRetriedAt: isRetry ? new Date() : undefined,
},
})
.catch(() => null);
if (!backgroundJob) {
context.text('Job not found', 404);
return;
}
try {
await definition.handler({
payload: options.payload,
io: this.createJobRunIO(jobId),
});
backgroundJob = await prisma.backgroundJob.update({
where: {
id: jobId,
status: BackgroundJobStatus.PROCESSING,
},
data: {
status: BackgroundJobStatus.COMPLETED,
completedAt: new Date(),
},
});
} catch (error) {
console.log(`[JOBS]: Job ${options.name} failed`, error);
const taskHasExceededRetries = error instanceof BackgroundTaskExceededRetriesError;
const jobHasExceededRetries =
backgroundJob.retried >= backgroundJob.maxRetries &&
!(error instanceof BackgroundTaskFailedError);
if (taskHasExceededRetries || jobHasExceededRetries) {
backgroundJob = await prisma.backgroundJob.update({
where: {
id: jobId,
status: BackgroundJobStatus.PROCESSING,
},
data: {
status: BackgroundJobStatus.FAILED,
completedAt: new Date(),
},
});
context.text('Task exceeded retries', 500);
return;
}
backgroundJob = await prisma.backgroundJob.update({
where: {
id: jobId,
status: BackgroundJobStatus.PROCESSING,
},
data: {
status: BackgroundJobStatus.PENDING,
},
});
await this.submitJobToEndpoint({
jobId,
jobDefinitionId: backgroundJob.jobId,
data: options,
});
}
context.text('OK', 200);
};
}
private async submitJobToEndpoint(options: {
jobId: string;
jobDefinitionId: string;

View File

@ -58,6 +58,11 @@ export class TriggerJobProvider extends BaseJobProvider {
return handler;
}
// Hono v2 is being deprecated so not sure if we will be required.
// public getHonoApiHandler(): (req: HonoContext) => Promise<Response | void> {
// throw new Error('Not implemented');
// }
private convertTriggerIoToJobRunIo(io: IO) {
return {
wait: io.wait,

View File

@ -0,0 +1,12 @@
import { sendResetPassword } from '../../../server-only/auth/send-reset-password';
import type { TSendPasswordResetSuccessEmailJobDefinition } from './send-password-reset-success-email';
export const run = async ({
payload,
}: {
payload: TSendPasswordResetSuccessEmailJobDefinition;
}) => {
await sendResetPassword({
userId: payload.userId,
});
};

View File

@ -0,0 +1,31 @@
import { z } from 'zod';
import type { JobDefinition } from '../../client/_internal/job';
const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID = 'send.password.reset.success.email';
const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
userId: z.number(),
});
export type TSendPasswordResetSuccessEmailJobDefinition = z.infer<
typeof SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA
>;
export const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION = {
id: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
name: 'Send Password Reset Email',
version: '1.0.0',
trigger: {
name: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
schema: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload }) => {
const handler = await import('./send-password-reset-success-email.handler');
await handler.run({ payload });
},
} as const satisfies JobDefinition<
typeof SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
TSendPasswordResetSuccessEmailJobDefinition
>;

View File

@ -0,0 +1,117 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { mailer } from '@documenso/email/mailer';
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import type { JobRunIO } from '../../client/_internal/job';
import type { TSendRecipientSignedEmailJobDefinition } from './send-recipient-signed-email';
export const run = async ({
payload,
io,
}: {
payload: TSendRecipientSignedEmailJobDefinition;
io: JobRunIO;
}) => {
const { documentId, recipientId } = payload;
const document = await prisma.document.findFirst({
where: {
id: documentId,
recipients: {
some: {
id: recipientId,
},
},
},
include: {
recipients: {
where: {
id: recipientId,
},
},
user: true,
documentMeta: true,
team: {
include: {
teamGlobalSettings: true,
},
},
},
});
if (!document) {
throw new Error('Document not found');
}
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
).recipientSigned;
if (!isRecipientSignedEmailEnabled) {
return;
}
const [recipient] = document.recipients;
const { email: recipientEmail, name: recipientName } = recipient;
const { user: owner } = document;
const recipientReference = recipientName || recipientEmail;
// Don't send notification if the owner is the one who signed
if (owner.email === recipientEmail) {
return;
}
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const i18n = await getI18nInstance(document.documentMeta?.language);
const template = createElement(DocumentRecipientSignedEmailTemplate, {
documentName: document.title,
recipientName,
recipientEmail,
assetBaseUrl,
});
await io.runTask('send-recipient-signed-email', async () => {
const branding = document.team?.teamGlobalSettings
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
: undefined;
const [html, text] = await Promise.all([
renderEmailWithI18N(template, { lang: document.documentMeta?.language, branding }),
renderEmailWithI18N(template, {
lang: document.documentMeta?.language,
branding,
plainText: true,
}),
]);
await mailer.sendMail({
to: {
name: owner.name ?? '',
address: owner.email,
},
from: {
name: FROM_NAME,
address: FROM_ADDRESS,
},
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
html,
text,
});
});
};

View File

@ -1,18 +1,5 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { z } from 'zod';
import { mailer } from '@documenso/email/mailer';
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import { type JobDefinition } from '../../client/_internal/job';
const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID = 'send.recipient.signed.email';
@ -22,6 +9,10 @@ const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
recipientId: z.number(),
});
export type TSendRecipientSignedEmailJobDefinition = z.infer<
typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA
>;
export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
id: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,
name: 'Send Recipient Signed Email',
@ -31,98 +22,9 @@ export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
schema: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload, io }) => {
const { documentId, recipientId } = payload;
const handler = await import('./send-recipient-signed-email.handler');
const document = await prisma.document.findFirst({
where: {
id: documentId,
recipients: {
some: {
id: recipientId,
},
},
},
include: {
recipients: {
where: {
id: recipientId,
},
},
user: true,
documentMeta: true,
team: {
include: {
teamGlobalSettings: true,
},
},
},
});
if (!document) {
throw new Error('Document not found');
}
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
).recipientSigned;
if (!isRecipientSignedEmailEnabled) {
return;
}
const [recipient] = document.recipients;
const { email: recipientEmail, name: recipientName } = recipient;
const { user: owner } = document;
const recipientReference = recipientName || recipientEmail;
// Don't send notification if the owner is the one who signed
if (owner.email === recipientEmail) {
return;
}
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const i18n = await getI18nInstance(document.documentMeta?.language);
const template = createElement(DocumentRecipientSignedEmailTemplate, {
documentName: document.title,
recipientName,
recipientEmail,
assetBaseUrl,
});
await io.runTask('send-recipient-signed-email', async () => {
const branding = document.team?.teamGlobalSettings
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
: undefined;
const [html, text] = await Promise.all([
renderEmailWithI18N(template, { lang: document.documentMeta?.language, branding }),
renderEmailWithI18N(template, {
lang: document.documentMeta?.language,
branding,
plainText: true,
}),
]);
await mailer.sendMail({
to: {
name: owner.name ?? '',
address: owner.email,
},
from: {
name: FROM_NAME,
address: FROM_ADDRESS,
},
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
html,
text,
});
});
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,

View File

@ -1,14 +1,14 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { SendStatus, SigningStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentRejectedEmail from '@documenso/email/templates/document-rejected';
import DocumentRejectionConfirmedEmail from '@documenso/email/templates/document-rejection-confirmed';
import { prisma } from '@documenso/prisma';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';

View File

@ -1,18 +1,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { DocumentSource, DocumentStatus, RecipientRole, SendStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentInviteEmailTemplate from '@documenso/email/templates/document-invite';
import { prisma } from '@documenso/prisma';
import {
DocumentSource,
DocumentStatus,
RecipientRole,
SendStatus,
} from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import {

View File

@ -1,7 +1,6 @@
import { DocumentVisibility } from '@prisma/client';
import { z } from 'zod';
import { DocumentVisibility } from '@documenso/prisma/client';
import type { JobDefinition } from '../../client/_internal/job';
const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_ID = 'send.team-deleted.email';

View File

@ -1,13 +1,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { TeamMemberRole } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';

View File

@ -1,13 +1,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { TeamMemberRole } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';

View File

@ -1,14 +1,9 @@
import { DocumentStatus, RecipientRole, SigningStatus, WebhookTriggerEvents } from '@prisma/client';
import { nanoid } from 'nanoid';
import path from 'node:path';
import { PDFDocument } from 'pdf-lib';
import { prisma } from '@documenso/prisma';
import {
DocumentStatus,
RecipientRole,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import { sendCompletedEmail } from '../../../server-only/document/send-completed-email';

View File

@ -2,6 +2,7 @@
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import { compare } from '@node-rs/bcrypt';
import { Prisma } from '@prisma/client';
import { IdentityProvider, UserSecurityAuditLogType } from '@prisma/client';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { DateTime } from 'luxon';
import type { AuthOptions, Session, User } from 'next-auth';
@ -12,7 +13,6 @@ import GoogleProvider from 'next-auth/providers/google';
import { env } from 'next-runtime-env';
import { prisma } from '@documenso/prisma';
import { IdentityProvider, UserSecurityAuditLogType } from '@documenso/prisma/client';
import { formatSecureCookieName, useSecureCookies } from '../constants/auth';
import { AppError, AppErrorCode } from '../errors/app-error';

View File

@ -1,3 +1,4 @@
import { Role, User } from '@documenso/prisma/client';
import type { User } from '@prisma/client';
import { Role } from '@prisma/client';
export const isAdmin = (user: User) => user.roles.includes(Role.ADMIN);
export const isAdmin = (user: Pick<User, 'roles'>) => user.roles.includes(Role.ADMIN);

View File

@ -63,4 +63,4 @@
"@types/luxon": "^3.3.1",
"@types/pg": "^8.11.4"
}
}
}

View File

@ -1,13 +1,14 @@
import type { User } from '@prisma/client';
import { UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { User } from '@documenso/prisma/client';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { validateTwoFactorAuthentication } from './validate-2fa';
type DisableTwoFactorAuthenticationOptions = {
user: User;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret'>;
totpCode?: string;
backupCode?: string;
requestMetadata?: RequestMetadata;

View File

@ -1,5 +1,6 @@
import { type User, UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { type User, UserSecurityAuditLogType } from '@documenso/prisma/client';
import { AppError } from '../../errors/app-error';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
@ -7,7 +8,7 @@ import { getBackupCodes } from './get-backup-code';
import { verifyTwoFactorAuthenticationToken } from './verify-2fa-token';
type EnableTwoFactorAuthenticationOptions = {
user: User;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret'>;
code: string;
requestMetadata?: RequestMetadata;
};

View File

@ -1,12 +1,11 @@
import type { User } from '@prisma/client';
import { z } from 'zod';
import { User } from '@documenso/prisma/client';
import { DOCUMENSO_ENCRYPTION_KEY } from '../../constants/crypto';
import { symmetricDecrypt } from '../../universal/crypto';
interface GetBackupCodesOptions {
user: User;
user: Pick<User, 'id' | 'twoFactorEnabled' | 'twoFactorBackupCodes'>;
}
const ZBackupCodeSchema = z.array(z.string());

View File

@ -1,4 +1,4 @@
import type { User } from '@documenso/prisma/client';
import type { User } from '@prisma/client';
import { DOCUMENSO_ENCRYPTION_KEY } from '../../constants/crypto';

View File

@ -1,16 +1,16 @@
import { type User } from '@prisma/client';
import { base32 } from '@scure/base';
import crypto from 'crypto';
import { createTOTPKeyURI } from 'oslo/otp';
import { ErrorCode } from '@documenso/lib/next-auth/error-codes';
import { prisma } from '@documenso/prisma';
import { type User } from '@documenso/prisma/client';
import { DOCUMENSO_ENCRYPTION_KEY } from '../../constants/crypto';
import { symmetricEncrypt } from '../../universal/crypto';
type SetupTwoFactorAuthenticationOptions = {
user: User;
user: Pick<User, 'id' | 'email'>;
};
const ISSUER = 'Documenso';

View File

@ -1,4 +1,4 @@
import type { User } from '@documenso/prisma/client';
import type { User } from '@prisma/client';
import { ErrorCode } from '../../next-auth/error-codes';
import { verifyTwoFactorAuthenticationToken } from './verify-2fa-token';
@ -7,7 +7,7 @@ import { verifyBackupCode } from './verify-backup-code';
type ValidateTwoFactorAuthenticationOptions = {
totpCode?: string;
backupCode?: string;
user: User;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret'>;
};
export const validateTwoFactorAuthentication = async ({

View File

@ -1,8 +1,7 @@
import type { User } from '@prisma/client';
import { base32 } from '@scure/base';
import { generateHOTP } from 'oslo/otp';
import type { User } from '@documenso/prisma/client';
import { DOCUMENSO_ENCRYPTION_KEY } from '../../constants/crypto';
import { symmetricDecrypt } from '../../universal/crypto';

View File

@ -1,9 +1,9 @@
import { User } from '@documenso/prisma/client';
import type { User } from '@prisma/client';
import { getBackupCodes } from './get-backup-code';
type VerifyBackupCodeParams = {
user: User;
user: Pick<User, 'id' | 'email' | 'twoFactorEnabled' | 'twoFactorBackupCodes'>;
backupCode: string;
};

View File

@ -1,11 +1,14 @@
import type { User } from '@documenso/prisma/client';
import type { User } from '@prisma/client';
import { AppError } from '../../errors/app-error';
import { getBackupCodes } from './get-backup-code';
import { validateTwoFactorAuthentication } from './validate-2fa';
type ViewBackupCodesOptions = {
user: User;
user: Pick<
User,
'id' | 'email' | 'twoFactorEnabled' | 'twoFactorSecret' | 'twoFactorBackupCodes'
>;
token: string;
};

View File

@ -1,5 +1,6 @@
import type { Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';

View File

@ -1,5 +1,6 @@
import { ReadStatus, SendStatus, SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { ReadStatus, SendStatus, SigningStatus } from '@documenso/prisma/client';
export const getRecipientsStats = async () => {
const results = await prisma.recipient.groupBy({

View File

@ -1,5 +1,6 @@
import { DocumentStatus, Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, Prisma } from '@documenso/prisma/client';
export type SigningVolume = {
id: number;

View File

@ -1,7 +1,7 @@
import { DocumentStatus, SubscriptionStatus } from '@prisma/client';
import { DateTime } from 'luxon';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, SubscriptionStatus } from '@documenso/prisma/client';
export const getUsersCount = async () => {
return await prisma.user.count();

View File

@ -1,5 +1,6 @@
import { SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { SigningStatus } from '@documenso/prisma/client';
export type UpdateRecipientOptions = {
id: number;

View File

@ -1,5 +1,6 @@
import type { Role } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { Role } from '@documenso/prisma/client';
export type UpdateUserOptions = {
id: number;

View File

@ -1,9 +1,9 @@
import type { Passkey } from '@prisma/client';
import { generateAuthenticationOptions } from '@simplewebauthn/server';
import type { AuthenticatorTransportFuture } from '@simplewebauthn/types';
import { DateTime } from 'luxon';
import { prisma } from '@documenso/prisma';
import type { Passkey } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { getAuthenticatorOptions } from '../../utils/authenticator';

View File

@ -1,8 +1,8 @@
import { UserSecurityAuditLogType } from '@prisma/client';
import { verifyRegistrationResponse } from '@simplewebauthn/server';
import type { RegistrationResponseJSON } from '@simplewebauthn/types';
import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import { MAXIMUM_PASSKEYS } from '../../constants/auth';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,5 +1,6 @@
import { UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';

View File

@ -1,6 +1,7 @@
import type { Passkey } from '@prisma/client';
import { Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { Passkey } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';

View File

@ -6,7 +6,7 @@ import { mailer } from '@documenso/email/mailer';
import { ConfirmEmailTemplate } from '@documenso/email/templates/confirm-email';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';

View File

@ -6,7 +6,7 @@ import { mailer } from '@documenso/email/mailer';
import { ForgotPasswordTemplate } from '@documenso/email/templates/forgot-password';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';

View File

@ -1,5 +1,6 @@
import { UserSecurityAuditLogType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';

View File

@ -1,7 +1,8 @@
'use server';
import type { DocumentDataType } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { DocumentDataType } from '@documenso/prisma/client';
export type CreateDocumentDataOptions = {
type: DocumentDataType;

View File

@ -1,5 +1,7 @@
'use server';
import type { DocumentDistributionMethod, DocumentSigningOrder } from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import {
@ -7,7 +9,6 @@ import {
diffDocumentMetaChanges,
} from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { DocumentDistributionMethod, DocumentSigningOrder } from '@documenso/prisma/client';
import type { SupportedLanguageCodes } from '../../constants/i18n';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,8 +1,3 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import {
DocumentSigningOrder,
DocumentStatus,
@ -10,7 +5,13 @@ import {
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
} from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { jobs } from '../../jobs/client';
import type { TRecipientActionAuth } from '../../types/document-auth';

View File

@ -1,3 +1,13 @@
import type { DocumentVisibility, TemplateMeta } from '@prisma/client';
import {
DocumentSource,
RecipientRole,
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@prisma/client';
import { TeamMemberRole } from '@prisma/client';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { normalizePdf as makeNormalizedPdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
@ -6,15 +16,6 @@ import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-reques
import { nanoid } from '@documenso/lib/universal/id';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { DocumentVisibility, TemplateMeta } from '@documenso/prisma/client';
import {
DocumentSource,
RecipientRole,
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import type { TCreateDocumentV2Request } from '@documenso/trpc/server/document-router/schema';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';

View File

@ -1,14 +1,15 @@
'use server';
import { DocumentSource, WebhookTriggerEvents } from '@prisma/client';
import type { Team, TeamGlobalSettings } from '@prisma/client';
import { TeamMemberRole } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { normalizePdf as makeNormalizedPdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { DocumentSource, WebhookTriggerEvents } from '@documenso/prisma/client';
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import {
ZWebhookDocumentSchema,

View File

@ -3,10 +3,6 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { mailer } from '@documenso/email/mailer';
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
import { prisma } from '@documenso/prisma';
import type {
Document,
DocumentMeta,
@ -14,10 +10,14 @@ import type {
Team,
TeamGlobalSettings,
User,
} from '@documenso/prisma/client';
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
} from '@prisma/client';
import { DocumentStatus, SendStatus } from '@prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { mailer } from '@documenso/email/mailer';
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,5 +1,6 @@
import { DocumentSource, type Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { DocumentSource, type Prisma } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { getDocumentWhereInput } from './get-document-by-id';

View File

@ -1,5 +1,6 @@
import type { DocumentAuditLog, Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { DocumentAuditLog, Prisma } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';

View File

@ -1,16 +1,9 @@
import type { Document, DocumentSource, Prisma, Team, TeamEmail, User } from '@prisma/client';
import { RecipientRole, SigningStatus, TeamMemberRole } from '@prisma/client';
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import type {
Document,
DocumentSource,
Prisma,
Team,
TeamEmail,
User,
} from '@documenso/prisma/client';
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { DocumentVisibility } from '../../types/document-visibility';

View File

@ -1,8 +1,8 @@
import type { Prisma } from '@prisma/client';
import { TeamMemberRole } from '@prisma/client';
import { match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { DocumentVisibility } from '../../types/document-visibility';

View File

@ -1,12 +1,12 @@
import { TeamMemberRole } from '@prisma/client';
import type { Prisma, User } from '@prisma/client';
import { SigningStatus } from '@prisma/client';
import { DocumentVisibility } from '@prisma/client';
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import type { Prisma, User } from '@documenso/prisma/client';
import { SigningStatus } from '@documenso/prisma/client';
import { DocumentVisibility } from '@documenso/prisma/client';
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';

View File

@ -1,8 +1,8 @@
import type { Document, Recipient } from '@prisma/client';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import type { Document, Recipient } from '@documenso/prisma/client';
import { verifyTwoFactorAuthenticationToken } from '../2fa/verify-2fa-token';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,8 +1,8 @@
import { SigningStatus } from '@prisma/client';
import { WebhookTriggerEvents } from '@prisma/client';
import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';

View File

@ -1,6 +1,8 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client';
import type { Prisma } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
@ -14,10 +16,8 @@ import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-reques
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import type { Prisma } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';

View File

@ -1,3 +1,4 @@
import { DocumentStatus, RecipientRole, SigningStatus, WebhookTriggerEvents } from '@prisma/client';
import { nanoid } from 'nanoid';
import path from 'node:path';
import { PDFDocument } from 'pdf-lib';
@ -6,12 +7,6 @@ import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-po
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import {
DocumentStatus,
RecipientRole,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import {

View File

@ -1,10 +1,10 @@
import { DocumentStatus } from '@prisma/client';
import type { Document, Recipient, User } from '@prisma/client';
import { DocumentVisibility, TeamMemberRole } from '@prisma/client';
import { match } from 'ts-pattern';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
import { DocumentStatus } from '@documenso/prisma/client';
import type { Document, Recipient, User } from '@documenso/prisma/client';
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
export type SearchDocumentsWithKeywordOptions = {
query: string;

View File

@ -1,13 +1,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { DocumentSource } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed';
import { prisma } from '@documenso/prisma';
import { DocumentSource } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';

View File

@ -6,7 +6,7 @@ import { mailer } from '@documenso/email/mailer';
import { DocumentSuperDeleteEmailTemplate } from '@documenso/email/templates/document-super-delete';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';

View File

@ -1,8 +1,3 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import {
DocumentSigningOrder,
DocumentStatus,
@ -10,7 +5,13 @@ import {
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
} from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { jobs } from '../../jobs/client';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';

View File

@ -6,7 +6,7 @@ import { mailer } from '@documenso/email/mailer';
import { DocumentPendingEmailTemplate } from '@documenso/email/templates/document-pending';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';

View File

@ -3,13 +3,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { DocumentStatus, SendStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,3 +1,5 @@
import { DocumentVisibility } from '@prisma/client';
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
@ -6,8 +8,6 @@ import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-reques
import type { CreateDocumentAuditLogDataResponse } from '@documenso/lib/utils/document-audit-logs';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { DocumentVisibility } from '@documenso/prisma/client';
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';

View File

@ -1,5 +1,5 @@
import type { Document, Field, Recipient } from '@documenso/prisma/client';
import { FieldType } from '@documenso/prisma/client';
import type { Document, Field, Recipient } from '@prisma/client';
import { FieldType } from '@prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TRecipientActionAuth } from '../../types/document-auth';

View File

@ -1,9 +1,10 @@
import { ReadStatus, SendStatus } from '@prisma/client';
import { WebhookTriggerEvents } from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { ReadStatus, SendStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
import {

View File

@ -1,7 +1,7 @@
import type { FieldType, Team } from '@prisma/client';
import { match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import type { FieldType, Team } from '@documenso/prisma/client';
import {
ZCheckboxFieldMeta,

View File

@ -1,6 +1,7 @@
import type { FieldType } from '@prisma/client';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { prisma } from '@documenso/prisma';
import type { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';

View File

@ -1,5 +1,6 @@
import type { Team } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { Team } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';

View File

@ -1,5 +1,6 @@
import { SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { SigningStatus } from '@documenso/prisma/client';
export type GetCompletedFieldsForDocumentOptions = {
documentId: number;

View File

@ -1,5 +1,6 @@
import { SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { SigningStatus } from '@documenso/prisma/client';
export type GetCompletedFieldsForTokenOptions = {
token: string;

View File

@ -1,10 +1,11 @@
'use server';
import { DocumentStatus, SigningStatus } from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
export type RemovedSignedFieldWithTokenOptions = {
token: string;

View File

@ -1,3 +1,5 @@
import type { Field } from '@prisma/client';
import { FieldType } from '@prisma/client';
import { isDeepEqual } from 'remeda';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
@ -21,8 +23,6 @@ import {
diffFieldChanges,
} from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { Field } from '@documenso/prisma/client';
import { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';

View File

@ -1,3 +1,5 @@
import { FieldType } from '@prisma/client';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
@ -13,7 +15,6 @@ import {
ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import { prisma } from '@documenso/prisma';
import { FieldType } from '@documenso/prisma/client';
export type SetFieldsForTemplateOptions = {
userId: number;

View File

@ -1,5 +1,6 @@
'use server';
import { DocumentStatus, FieldType, SigningStatus } from '@prisma/client';
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
@ -10,7 +11,6 @@ import { validateRadioField } from '@documenso/lib/advanced-fields-validation/va
import { validateTextField } from '@documenso/lib/advanced-fields-validation/validate-text';
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, FieldType, SigningStatus } from '@documenso/prisma/client';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../../constants/date-formats';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '../../constants/time-zones';

View File

@ -1,3 +1,5 @@
import type { FieldType } from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@ -6,7 +8,6 @@ import {
diffFieldChanges,
} from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';

View File

@ -1,6 +1,7 @@
import type { FieldType, Team } from '@prisma/client';
import { type TFieldMetaSchema as FieldMeta } from '@documenso/lib/types/field-meta';
import { prisma } from '@documenso/prisma';
import type { FieldType, Team } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata';

View File

@ -1,6 +1,7 @@
import type { FieldType } from '@prisma/client';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { prisma } from '@documenso/prisma';
import type { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';

View File

@ -1,5 +1,6 @@
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
import fontkit from '@pdf-lib/fontkit';
import { FieldType } from '@prisma/client';
import type { PDFDocument } from 'pdf-lib';
import { RotationTypes, degrees, radiansToDegrees } from 'pdf-lib';
import { P, match } from 'ts-pattern';
@ -11,7 +12,6 @@ import {
MIN_STANDARD_FONT_SIZE,
} from '@documenso/lib/constants/pdf';
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
import { FieldType } from '@documenso/prisma/client';
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';

View File

@ -1,12 +1,13 @@
import { getCommunityPlanPriceIds } from '@documenso/ee/server-only/stripe/get-community-plan-prices';
import { prisma } from '@documenso/prisma';
import type { Template, TemplateDirectLink } from '@documenso/prisma/client';
import type { Template, TemplateDirectLink } from '@prisma/client';
import {
SubscriptionStatus,
type TeamProfile,
TemplateType,
type UserProfile,
} from '@documenso/prisma/client';
} from '@prisma/client';
import { getCommunityPlanPriceIds } from '@documenso/ee/server-only/stripe/get-community-plan-prices';
import { prisma } from '@documenso/prisma';
import { IS_BILLING_ENABLED } from '../../constants/app';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,8 +1,8 @@
import { TeamMemberRole } from '@prisma/client';
import type { Duration } from 'luxon';
import { DateTime } from 'luxon';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
// temporary choice for testing only
import * as timeConstants from '../../constants/time';

View File

@ -1,5 +1,6 @@
import { TeamMemberRole } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
export type DeleteTokenByIdOptions = {
id: number;

View File

@ -1,6 +1,7 @@
import { TeamMemberRole } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
export type GetUserTokensOptions = {
userId: number;

View File

@ -1,3 +1,6 @@
import { RecipientRole } from '@prisma/client';
import { SendStatus, SigningStatus } from '@prisma/client';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { TRecipientAccessAuthTypes } from '@documenso/lib/types/document-auth';
@ -7,8 +10,6 @@ import { nanoid } from '@documenso/lib/universal/id';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { createRecipientAuthOptions } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma';
import { RecipientRole } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,11 +1,12 @@
import { RecipientRole } from '@prisma/client';
import { SendStatus, SigningStatus } from '@prisma/client';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import type { TRecipientAccessAuthTypes } from '@documenso/lib/types/document-auth';
import { type TRecipientActionAuthTypes } from '@documenso/lib/types/document-auth';
import { nanoid } from '@documenso/lib/universal/id';
import { createRecipientAuthOptions } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma';
import { RecipientRole } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,15 +1,15 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { SendStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import RecipientRemovedFromDocumentTemplate from '@documenso/email/templates/recipient-removed-from-document';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { prisma } from '@documenso/prisma';
import { SendStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,6 +1,7 @@
import type { Team } from '@prisma/client';
import { SendStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import type { Team } from '@documenso/prisma/client';
import { SendStatus } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';

View File

@ -1,5 +1,6 @@
import { DocumentSigningOrder, SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { DocumentSigningOrder, SigningStatus } from '@documenso/prisma/client';
export type GetIsRecipientTurnOptions = {
token: string;

View File

@ -1,6 +1,9 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import type { Recipient } from '@prisma/client';
import { RecipientRole } from '@prisma/client';
import { SendStatus, SigningStatus } from '@prisma/client';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { mailer } from '@documenso/email/mailer';
@ -19,11 +22,8 @@ import {
} from '@documenso/lib/utils/document-audit-logs';
import { createRecipientAuthOptions } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { RecipientRole } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@ -1,11 +1,12 @@
import type { Recipient } from '@prisma/client';
import { RecipientRole } from '@prisma/client';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import {
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
DIRECT_TEMPLATE_RECIPIENT_NAME,
} from '@documenso/lib/constants/direct-templates';
import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { RecipientRole } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import {

Some files were not shown because too many files have changed in this diff Show More