mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 09:25:08 +10:00
feat: allow editing pending envelope titles (#2604)
This commit is contained in:
@@ -16,7 +16,7 @@ import { setDocumentRecipients } from '@documenso/lib/server-only/recipient/set-
|
||||
import { setTemplateRecipients } from '@documenso/lib/server-only/recipient/set-template-recipients';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { PRESIGNED_ENVELOPE_ITEM_ID_PREFIX } from '@documenso/lib/utils/embed-config';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { procedure } from '../trpc';
|
||||
@@ -191,10 +191,36 @@ export const updateEmbeddingEnvelopeRoute = procedure
|
||||
|
||||
// Should be safe to use stale envelope.recipients since only signed or sent
|
||||
// recipients affect the outcome.
|
||||
if (willEnvelopeItemsBeModified && !canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
if (willEnvelopeItemsBeModified) {
|
||||
const permissions = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
const hasFileChange = envelopeItemIdsToDelete.length > 0 || envelopeItemsToCreate.length > 0;
|
||||
|
||||
const hasOrderChange = envelopeItemsToUpdate.some((item) => {
|
||||
const existing = envelope.envelopeItems.find((e) => e.id === item.envelopeItemId);
|
||||
|
||||
return !existing || item.order !== existing.order;
|
||||
});
|
||||
|
||||
const hasTitleChange = envelopeItemsToUpdate.some((item) => item.title !== undefined);
|
||||
|
||||
if (hasFileChange && !permissions.canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item files are not editable',
|
||||
});
|
||||
}
|
||||
|
||||
if (hasOrderChange && !permissions.canOrderBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item order is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
if (hasTitleChange && !permissions.canTitleBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item title is not editable',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (envelopeItemIdsToDelete.length > 0) {
|
||||
@@ -252,7 +278,14 @@ export const updateEmbeddingEnvelopeRoute = procedure
|
||||
if (envelopeItemsToUpdate.length > 0) {
|
||||
await UNSAFE_updateEnvelopeItems({
|
||||
envelopeId: envelope.id,
|
||||
envelopeType: envelope.type,
|
||||
existingEnvelopeItems: envelope.envelopeItems,
|
||||
data: envelopeItemsToUpdate,
|
||||
user: {
|
||||
name: apiToken.user.name,
|
||||
email: apiToken.user.email,
|
||||
},
|
||||
apiRequestMetadata: ctx.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { UNSAFE_createEnvelopeItems } from '@documenso/lib/server-only/envelope-item/create-envelope-items';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
@@ -63,7 +63,9 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { UNSAFE_deleteEnvelopeItem } from '@documenso/lib/server-only/envelope-item/delete-envelope-item';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../schema';
|
||||
@@ -50,7 +50,9 @@ export const deleteEnvelopeItemRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { UNSAFE_replaceEnvelopeItemPdf } from '@documenso/lib/server-only/envelope-item/replace-envelope-item-pdf';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
@@ -65,7 +65,9 @@ export const replaceEnvelopeItemPdfRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { UNSAFE_updateEnvelopeItems } from '@documenso/lib/server-only/envelope-item/update-envelope-items';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
@@ -55,12 +55,40 @@ export const updateEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
// Note: This logic is duplicated in many places. If we plan to allow changing title/order
|
||||
// even after the envelope has been sent, make sure to update it everywhere including
|
||||
// embedding routes.
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const permissions = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
const hasOrderChange = data.some((item) => {
|
||||
if (item.order === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const existingItem = envelope.envelopeItems.find((e) => e.id === item.envelopeItemId);
|
||||
|
||||
return !existingItem || existingItem.order !== item.order;
|
||||
});
|
||||
|
||||
const hasTitleChange = data.some((item) => item.title !== undefined);
|
||||
|
||||
if (!hasTitleChange && !hasOrderChange) {
|
||||
return {
|
||||
data: envelope.envelopeItems.map((item) => ({
|
||||
id: item.id,
|
||||
order: item.order,
|
||||
title: item.title,
|
||||
envelopeId: item.envelopeId,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
if (hasTitleChange && !permissions.canTitleBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
message: 'Envelope item title is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
if (hasOrderChange && !permissions.canOrderBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item order is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,7 +105,14 @@ export const updateEnvelopeItemsRoute = authenticatedProcedure
|
||||
|
||||
const updatedEnvelopeItems = await UNSAFE_updateEnvelopeItems({
|
||||
envelopeId,
|
||||
envelopeType: envelope.type,
|
||||
existingEnvelopeItems: envelope.envelopeItems,
|
||||
data,
|
||||
user: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
apiRequestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -73,7 +73,7 @@ const t = initTRPC
|
||||
/**
|
||||
* Middlewares
|
||||
*/
|
||||
export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => {
|
||||
export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path, meta }) => {
|
||||
const infoToLog: TrpcApiLog = {
|
||||
path,
|
||||
auth: ctx.metadata.auth,
|
||||
@@ -84,8 +84,10 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path })
|
||||
|
||||
const authorizationHeader = ctx.req.headers.get('authorization');
|
||||
|
||||
const isApiV2 = Boolean(meta?.openapi?.path);
|
||||
|
||||
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
|
||||
if (authorizationHeader) {
|
||||
if (authorizationHeader && isApiV2) {
|
||||
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
|
||||
const [token] = (authorizationHeader || '').split('Bearer ').filter((s) => s.length > 0);
|
||||
|
||||
@@ -164,7 +166,7 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path })
|
||||
});
|
||||
});
|
||||
|
||||
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => {
|
||||
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, path, meta }) => {
|
||||
// Recreate the logger with a sub request ID to differentiate between batched requests.
|
||||
const trpcSessionLogger = ctx.logger.child({
|
||||
nonBatchedRequestId: alphaid(),
|
||||
@@ -180,8 +182,10 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, pat
|
||||
|
||||
const authorizationHeader = ctx.req.headers.get('authorization');
|
||||
|
||||
const isApiV2 = Boolean(meta?.openapi?.path);
|
||||
|
||||
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
|
||||
if (authorizationHeader) {
|
||||
if (authorizationHeader && isApiV2) {
|
||||
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
|
||||
const [token] = (authorizationHeader || '').split('Bearer ').filter((s) => s.length > 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user