mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
Merge branch 'main' into feat/bin-tab
This commit is contained in:
@ -24,8 +24,11 @@ export type CreateDocumentMetaOptions = {
|
||||
redirectUrl?: string;
|
||||
emailSettings?: TDocumentEmailSettings;
|
||||
signingOrder?: DocumentSigningOrder;
|
||||
allowDictateNextSigner?: boolean;
|
||||
distributionMethod?: DocumentDistributionMethod;
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
drawSignatureEnabled?: boolean;
|
||||
language?: SupportedLanguageCodes;
|
||||
requestMetadata: ApiRequestMetadata;
|
||||
};
|
||||
@ -41,9 +44,12 @@ export const upsertDocumentMeta = async ({
|
||||
password,
|
||||
redirectUrl,
|
||||
signingOrder,
|
||||
allowDictateNextSigner,
|
||||
emailSettings,
|
||||
distributionMethod,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
language,
|
||||
requestMetadata,
|
||||
}: CreateDocumentMetaOptions) => {
|
||||
@ -93,9 +99,12 @@ export const upsertDocumentMeta = async ({
|
||||
documentId,
|
||||
redirectUrl,
|
||||
signingOrder,
|
||||
allowDictateNextSigner,
|
||||
emailSettings,
|
||||
distributionMethod,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
language,
|
||||
},
|
||||
update: {
|
||||
@ -106,9 +115,12 @@ export const upsertDocumentMeta = async ({
|
||||
timezone,
|
||||
redirectUrl,
|
||||
signingOrder,
|
||||
allowDictateNextSigner,
|
||||
emailSettings,
|
||||
distributionMethod,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
language,
|
||||
},
|
||||
});
|
||||
|
||||
@ -7,7 +7,10 @@ import {
|
||||
WebhookTriggerEvents,
|
||||
} from '@prisma/client';
|
||||
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import {
|
||||
DOCUMENT_AUDIT_LOG_TYPE,
|
||||
RECIPIENT_DIFF_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';
|
||||
@ -30,6 +33,10 @@ export type CompleteDocumentWithTokenOptions = {
|
||||
userId?: number;
|
||||
authOptions?: TRecipientActionAuth;
|
||||
requestMetadata?: RequestMetadata;
|
||||
nextSigner?: {
|
||||
email: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptions) => {
|
||||
@ -57,6 +64,7 @@ export const completeDocumentWithToken = async ({
|
||||
token,
|
||||
documentId,
|
||||
requestMetadata,
|
||||
nextSigner,
|
||||
}: CompleteDocumentWithTokenOptions) => {
|
||||
const document = await getDocument({ token, documentId });
|
||||
|
||||
@ -146,7 +154,6 @@ export const completeDocumentWithToken = async ({
|
||||
recipientName: recipient.name,
|
||||
recipientId: recipient.id,
|
||||
recipientRole: recipient.role,
|
||||
// actionAuth: derivedRecipientActionAuth || undefined,
|
||||
},
|
||||
}),
|
||||
});
|
||||
@ -164,6 +171,9 @@ export const completeDocumentWithToken = async ({
|
||||
select: {
|
||||
id: true,
|
||||
signingOrder: true,
|
||||
name: true,
|
||||
email: true,
|
||||
role: true,
|
||||
},
|
||||
where: {
|
||||
documentId: document.id,
|
||||
@ -186,9 +196,49 @@ export const completeDocumentWithToken = async ({
|
||||
const [nextRecipient] = pendingRecipients;
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
if (nextSigner && document.documentMeta?.allowDictateNextSigner) {
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
|
||||
documentId: document.id,
|
||||
user: {
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
},
|
||||
requestMetadata,
|
||||
data: {
|
||||
recipientEmail: nextRecipient.email,
|
||||
recipientName: nextRecipient.name,
|
||||
recipientId: nextRecipient.id,
|
||||
recipientRole: nextRecipient.role,
|
||||
changes: [
|
||||
{
|
||||
type: RECIPIENT_DIFF_TYPE.NAME,
|
||||
from: nextRecipient.name,
|
||||
to: nextSigner.name,
|
||||
},
|
||||
{
|
||||
type: RECIPIENT_DIFF_TYPE.EMAIL,
|
||||
from: nextRecipient.email,
|
||||
to: nextSigner.email,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
await tx.recipient.update({
|
||||
where: { id: nextRecipient.id },
|
||||
data: { sendStatus: SendStatus.SENT },
|
||||
data: {
|
||||
sendStatus: SendStatus.SENT,
|
||||
...(nextSigner && document.documentMeta?.allowDictateNextSigner
|
||||
? {
|
||||
name: nextSigner.name,
|
||||
email: nextSigner.email,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
await jobs.triggerJob({
|
||||
|
||||
@ -158,6 +158,10 @@ export const createDocumentV2 = async ({
|
||||
language: meta?.language || team?.teamGlobalSettings?.documentLanguage,
|
||||
typedSignatureEnabled:
|
||||
meta?.typedSignatureEnabled ?? team?.teamGlobalSettings?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled:
|
||||
meta?.uploadSignatureEnabled ?? team?.teamGlobalSettings?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled:
|
||||
meta?.drawSignatureEnabled ?? team?.teamGlobalSettings?.drawSignatureEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -128,8 +128,10 @@ export const createDocument = async ({
|
||||
documentMeta: {
|
||||
create: {
|
||||
language: team?.teamGlobalSettings?.documentLanguage,
|
||||
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled,
|
||||
timezone: timezone,
|
||||
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled ?? true,
|
||||
uploadSignatureEnabled: team?.teamGlobalSettings?.uploadSignatureEnabled ?? true,
|
||||
drawSignatureEnabled: team?.teamGlobalSettings?.drawSignatureEnabled ?? true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
import { SignJWT } from 'jose';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { env } from '../../utils/env';
|
||||
import { getApiTokenByToken } from '../public-api/get-api-token-by-token';
|
||||
|
||||
export type CreateEmbeddingPresignTokenOptions = {
|
||||
apiToken: string;
|
||||
/**
|
||||
* Number of hours until the token expires
|
||||
* In development mode, can be set to 0 to create a token that expires immediately (for testing)
|
||||
*/
|
||||
expiresIn?: number;
|
||||
};
|
||||
|
||||
export const createEmbeddingPresignToken = async ({
|
||||
apiToken,
|
||||
expiresIn,
|
||||
}: CreateEmbeddingPresignTokenOptions) => {
|
||||
try {
|
||||
// Validate the API token
|
||||
const validatedToken = await getApiTokenByToken({ token: apiToken });
|
||||
|
||||
const now = DateTime.now();
|
||||
|
||||
// In development mode, allow setting expiresIn to 0 for testing
|
||||
// In production, enforce a minimum expiration time
|
||||
const isDevelopment = env('NODE_ENV') !== 'production';
|
||||
console.log('isDevelopment', isDevelopment);
|
||||
const minExpirationMinutes = isDevelopment ? 0 : 5;
|
||||
|
||||
// Ensure expiresIn is at least the minimum allowed value
|
||||
const effectiveExpiresIn =
|
||||
expiresIn !== undefined && expiresIn >= minExpirationMinutes ? expiresIn : 60; // Default to 1 hour if not specified or below minimum
|
||||
|
||||
const expiresAt = now.plus({ minutes: effectiveExpiresIn });
|
||||
|
||||
const secret = new TextEncoder().encode(validatedToken.token);
|
||||
|
||||
const token = await new SignJWT({
|
||||
aud: String(validatedToken.teamId ?? validatedToken.userId),
|
||||
sub: String(validatedToken.id),
|
||||
})
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt(now.toJSDate())
|
||||
.setExpirationTime(expiresAt.toJSDate())
|
||||
.sign(secret);
|
||||
|
||||
return {
|
||||
token,
|
||||
expiresAt: expiresAt.toJSDate(),
|
||||
expiresIn: Math.floor(expiresAt.diff(now).toMillis() / 1000),
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
throw error;
|
||||
}
|
||||
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
|
||||
message: 'Failed to create presign token',
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,115 @@
|
||||
import type { JWTPayload } from 'jose';
|
||||
import { decodeJwt, jwtVerify } from 'jose';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
export type VerifyEmbeddingPresignTokenOptions = {
|
||||
token: string;
|
||||
};
|
||||
|
||||
export const verifyEmbeddingPresignToken = async ({
|
||||
token,
|
||||
}: VerifyEmbeddingPresignTokenOptions) => {
|
||||
// First decode the JWT to get the claims without verification
|
||||
let decodedToken: JWTPayload;
|
||||
|
||||
try {
|
||||
decodedToken = decodeJwt<JWTPayload>(token);
|
||||
} catch (error) {
|
||||
console.error('Error decoding JWT token:', error);
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token format',
|
||||
});
|
||||
}
|
||||
|
||||
// Validate the required claims
|
||||
if (!decodedToken.sub || typeof decodedToken.sub !== 'string') {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token format: missing or invalid subject claim',
|
||||
});
|
||||
}
|
||||
|
||||
if (!decodedToken.aud || typeof decodedToken.aud !== 'string') {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token format: missing or invalid audience claim',
|
||||
});
|
||||
}
|
||||
|
||||
// Convert string IDs to numbers
|
||||
const tokenId = Number(decodedToken.sub);
|
||||
const audienceId = Number(decodedToken.aud);
|
||||
|
||||
if (Number.isNaN(tokenId) || !Number.isInteger(tokenId)) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid token ID format in subject claim',
|
||||
});
|
||||
}
|
||||
|
||||
if (Number.isNaN(audienceId) || !Number.isInteger(audienceId)) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid user ID format in audience claim',
|
||||
});
|
||||
}
|
||||
|
||||
// Get the API token to use as the verification secret
|
||||
const apiToken = await prisma.apiToken.findFirst({
|
||||
where: {
|
||||
id: tokenId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!apiToken) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token: API token not found',
|
||||
});
|
||||
}
|
||||
|
||||
// This should never happen but we need to narrow types
|
||||
if (!apiToken.userId) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token: API token does not have a user attached',
|
||||
});
|
||||
}
|
||||
|
||||
const userId = apiToken.userId;
|
||||
|
||||
if (audienceId !== apiToken.teamId && audienceId !== apiToken.userId) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token: API token does not match audience',
|
||||
});
|
||||
}
|
||||
|
||||
// Now verify the token with the actual secret
|
||||
const secret = new TextEncoder().encode(apiToken.token);
|
||||
|
||||
try {
|
||||
await jwtVerify(token, secret);
|
||||
} catch (error) {
|
||||
// Check if the token has expired
|
||||
if (error instanceof Error && error.name === 'JWTExpired') {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Presign token has expired',
|
||||
});
|
||||
}
|
||||
|
||||
// Handle invalid signature
|
||||
if (error instanceof Error && error.name === 'JWSInvalid') {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid presign token signature',
|
||||
});
|
||||
}
|
||||
|
||||
// Log and rethrow other errors
|
||||
console.error('Error verifying JWT token:', error);
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Failed to verify presign token',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...apiToken,
|
||||
userId,
|
||||
};
|
||||
};
|
||||
@ -201,7 +201,7 @@ export const signFieldWithToken = async ({
|
||||
throw new Error('Signature field must have a signature');
|
||||
}
|
||||
|
||||
if (isSignatureField && !documentMeta?.typedSignatureEnabled && typedSignature) {
|
||||
if (isSignatureField && documentMeta?.typedSignatureEnabled === false && typedSignature) {
|
||||
throw new Error('Typed signatures are not allowed. Please draw your signature');
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export const getNextPendingRecipient = async ({
|
||||
documentId,
|
||||
currentRecipientId,
|
||||
}: {
|
||||
documentId: number;
|
||||
currentRecipientId: number;
|
||||
}) => {
|
||||
const recipients = await prisma.recipient.findMany({
|
||||
where: {
|
||||
documentId,
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
signingOrder: {
|
||||
sort: 'asc',
|
||||
nulls: 'last',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'asc',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const currentIndex = recipients.findIndex((r) => r.id === currentRecipientId);
|
||||
|
||||
if (currentIndex === -1 || currentIndex === recipients.length - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...recipients[currentIndex + 1],
|
||||
token: '',
|
||||
};
|
||||
};
|
||||
@ -5,6 +5,7 @@ import { z } from 'zod';
|
||||
import { createTeamCustomer } from '@documenso/ee/server-only/stripe/create-team-customer';
|
||||
import { getTeamRelatedPrices } from '@documenso/ee/server-only/stripe/get-team-related-prices';
|
||||
import { mapStripeSubscriptionToPrismaUpsertAction } from '@documenso/ee/server-only/stripe/webhook/on-subscription-updated';
|
||||
import { isDocumentPlatform as isUserPlatformPlan } from '@documenso/ee/server-only/util/is-document-platform';
|
||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
|
||||
@ -60,6 +61,11 @@ export const createTeam = async ({
|
||||
},
|
||||
});
|
||||
|
||||
const isPlatformPlan = await isUserPlatformPlan({
|
||||
userId: user.id,
|
||||
teamId: null,
|
||||
});
|
||||
|
||||
let isPaymentRequired = IS_BILLING_ENABLED();
|
||||
let customerId: string | null = null;
|
||||
|
||||
@ -68,7 +74,25 @@ export const createTeam = async ({
|
||||
prices.map((price) => price.id),
|
||||
);
|
||||
|
||||
isPaymentRequired = !subscriptionsContainsActivePlan(user.subscriptions, teamRelatedPriceIds);
|
||||
const hasTeamRelatedSubscription = subscriptionsContainsActivePlan(
|
||||
user.subscriptions,
|
||||
teamRelatedPriceIds,
|
||||
);
|
||||
|
||||
if (isPlatformPlan) {
|
||||
// For platform users, check if they already have any teams
|
||||
const existingTeams = await prisma.team.findMany({
|
||||
where: {
|
||||
ownerUserId: userId,
|
||||
},
|
||||
});
|
||||
|
||||
// Payment is required if they already have any team
|
||||
isPaymentRequired = existingTeams.length > 0;
|
||||
} else {
|
||||
// For non-platform users, payment is required if they don't have a team-related subscription
|
||||
isPaymentRequired = !hasTeamRelatedSubscription;
|
||||
}
|
||||
|
||||
customerId = await createTeamCustomer({
|
||||
name: user.name ?? teamName,
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
import type { DocumentVisibility } from '@prisma/client';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { TeamGlobalSettingsSchema } from '@documenso/prisma/generated/zod/modelSchema/TeamGlobalSettingsSchema';
|
||||
|
||||
import type { SupportedLanguageCodes } from '../../constants/i18n';
|
||||
|
||||
export type UpdateTeamDocumentSettingsOptions = {
|
||||
userId: number;
|
||||
teamId: number;
|
||||
|
||||
settings: {
|
||||
documentVisibility: DocumentVisibility;
|
||||
documentLanguage: SupportedLanguageCodes;
|
||||
includeSenderDetails: boolean;
|
||||
typedSignatureEnabled: boolean;
|
||||
includeSigningCertificate: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export const ZUpdateTeamDocumentSettingsResponseSchema = TeamGlobalSettingsSchema;
|
||||
|
||||
export type TUpdateTeamDocumentSettingsResponse = z.infer<
|
||||
typeof ZUpdateTeamDocumentSettingsResponseSchema
|
||||
>;
|
||||
|
||||
export const updateTeamDocumentSettings = async ({
|
||||
userId,
|
||||
teamId,
|
||||
settings,
|
||||
}: UpdateTeamDocumentSettingsOptions): Promise<TUpdateTeamDocumentSettingsResponse> => {
|
||||
const {
|
||||
documentVisibility,
|
||||
documentLanguage,
|
||||
includeSenderDetails,
|
||||
includeSigningCertificate,
|
||||
typedSignatureEnabled,
|
||||
} = settings;
|
||||
|
||||
const member = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!member || member.role !== TeamMemberRole.ADMIN) {
|
||||
throw new Error('You do not have permission to update this team.');
|
||||
}
|
||||
|
||||
return await prisma.teamGlobalSettings.upsert({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
create: {
|
||||
teamId,
|
||||
documentVisibility,
|
||||
documentLanguage,
|
||||
includeSenderDetails,
|
||||
typedSignatureEnabled,
|
||||
includeSigningCertificate,
|
||||
},
|
||||
update: {
|
||||
documentVisibility,
|
||||
documentLanguage,
|
||||
includeSenderDetails,
|
||||
typedSignatureEnabled,
|
||||
includeSigningCertificate,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -324,6 +324,9 @@ export const createDocumentFromDirectTemplate = async ({
|
||||
language: metaLanguage,
|
||||
signingOrder: metaSigningOrder,
|
||||
distributionMethod: template.templateMeta?.distributionMethod,
|
||||
typedSignatureEnabled: template.templateMeta?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled: template.templateMeta?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled: template.templateMeta?.drawSignatureEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -96,6 +96,9 @@ export const createDocumentFromTemplateLegacy = async ({
|
||||
signingOrder: template.templateMeta?.signingOrder ?? undefined,
|
||||
language:
|
||||
template.templateMeta?.language || template.team?.teamGlobalSettings?.documentLanguage,
|
||||
typedSignatureEnabled: template.templateMeta?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled: template.templateMeta?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled: template.templateMeta?.drawSignatureEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -82,8 +82,11 @@ export type CreateDocumentFromTemplateOptions = {
|
||||
signingOrder?: DocumentSigningOrder;
|
||||
language?: SupportedLanguageCodes;
|
||||
distributionMethod?: DocumentDistributionMethod;
|
||||
typedSignatureEnabled?: boolean;
|
||||
allowDictateNextSigner?: boolean;
|
||||
emailSettings?: TDocumentEmailSettings;
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
drawSignatureEnabled?: boolean;
|
||||
};
|
||||
requestMetadata: ApiRequestMetadata;
|
||||
};
|
||||
@ -404,6 +407,14 @@ export const createDocumentFromTemplate = async ({
|
||||
template.team?.teamGlobalSettings?.documentLanguage,
|
||||
typedSignatureEnabled:
|
||||
override?.typedSignatureEnabled ?? template.templateMeta?.typedSignatureEnabled,
|
||||
uploadSignatureEnabled:
|
||||
override?.uploadSignatureEnabled ?? template.templateMeta?.uploadSignatureEnabled,
|
||||
drawSignatureEnabled:
|
||||
override?.drawSignatureEnabled ?? template.templateMeta?.drawSignatureEnabled,
|
||||
allowDictateNextSigner:
|
||||
override?.allowDictateNextSigner ??
|
||||
template.templateMeta?.allowDictateNextSigner ??
|
||||
false,
|
||||
},
|
||||
},
|
||||
recipients: {
|
||||
|
||||
@ -4,6 +4,8 @@ import { prisma } from '@documenso/prisma';
|
||||
import { TemplateSchema } from '@documenso/prisma/generated/zod/modelSchema//TemplateSchema';
|
||||
import type { TCreateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
export type CreateTemplateOptions = TCreateTemplateMutationSchema & {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
@ -19,8 +21,10 @@ export const createTemplate = async ({
|
||||
teamId,
|
||||
templateDocumentDataId,
|
||||
}: CreateTemplateOptions) => {
|
||||
let team = null;
|
||||
|
||||
if (teamId) {
|
||||
await prisma.team.findFirstOrThrow({
|
||||
team = await prisma.team.findFirst({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
@ -29,7 +33,14 @@ export const createTemplate = async ({
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!team) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
return await prisma.template.create({
|
||||
@ -38,6 +49,14 @@ export const createTemplate = async ({
|
||||
userId,
|
||||
templateDocumentDataId,
|
||||
teamId,
|
||||
templateMeta: {
|
||||
create: {
|
||||
language: team?.teamGlobalSettings?.documentLanguage,
|
||||
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled ?? true,
|
||||
uploadSignatureEnabled: team?.teamGlobalSettings?.uploadSignatureEnabled ?? true,
|
||||
drawSignatureEnabled: team?.teamGlobalSettings?.drawSignatureEnabled ?? true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user