chore: merge main, resolve biome formatting conflicts

Reconcile toolbar-filters changes with main's Kysely-based query rewrite
and biome reformatting. Notable:

- find-documents.ts: rebase on main's Kysely impl while preserving PR
  semantics (status[] / source[] filters via OR-of-predicates,
  period 'all' bypass).
- get-stats.ts: skip period filter when 'all'.
- find-templates.ts: preserve PR templateType array + query filter on
  top of main's where/include structure.
- find-documents-internal: drop preloaded team plumbing (main resolves
  team internally) and switch appMetaTags to msg`...`.
- Restore packages/lib/translations/ from HEAD (autogenerated).
This commit is contained in:
ephraimduncan
2026-05-12 12:12:37 +00:00
1936 changed files with 92316 additions and 47005 deletions
@@ -1,7 +1,6 @@
import { OrganisationType } from '@prisma/client';
import { createOrganisation } from '@documenso/lib/server-only/organisation/create-organisation';
import { internalClaims } from '@documenso/lib/types/subscription';
import { OrganisationType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
@@ -38,19 +38,19 @@ export const createStripeCustomerRoute = adminProcedure
throw new AppError(AppErrorCode.NOT_FOUND);
}
await prisma.$transaction(async (tx) => {
const stripeCustomer = await createCustomer({
name: organisation.name,
email: organisation.owner.email,
});
// Create Stripe customer outside a transaction to avoid holding a
// connection open during the external API call.
const stripeCustomer = await createCustomer({
name: organisation.name,
email: organisation.owner.email,
});
await tx.organisation.update({
where: {
id: organisationId,
},
data: {
customerId: stripeCustomer.id,
},
});
await prisma.organisation.update({
where: {
id: organisationId,
},
data: {
customerId: stripeCustomer.id,
},
});
});
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZClaimFlagsSchema } from '@documenso/lib/types/subscription';
import { z } from 'zod';
export const ZCreateSubscriptionClaimRequestSchema = z.object({
name: z.string().min(1),
@@ -2,10 +2,7 @@ import { adminSuperDeleteDocument } from '@documenso/lib/server-only/admin/admin
import { sendDeleteEmail } from '@documenso/lib/server-only/document/send-delete-email';
import { adminProcedure } from '../trpc';
import {
ZDeleteDocumentRequestSchema,
ZDeleteDocumentResponseSchema,
} from './delete-document.types';
import { ZDeleteDocumentRequestSchema, ZDeleteDocumentResponseSchema } from './delete-document.types';
export const deleteDocumentRoute = adminProcedure
.input(ZDeleteDocumentRequestSchema)
@@ -0,0 +1,123 @@
import { syncMemberCountWithStripeSeatPlan } from '@documenso/ee/server-only/stripe/update-subscription-item-quantity';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma';
import { OrganisationMemberInviteStatus } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
ZDeleteAdminOrganisationMemberRequestSchema,
ZDeleteAdminOrganisationMemberResponseSchema,
} from './delete-organisation-member.types';
export const deleteAdminOrganisationMemberRoute = adminProcedure
.input(ZDeleteAdminOrganisationMemberRequestSchema)
.output(ZDeleteAdminOrganisationMemberResponseSchema)
.mutation(async ({ ctx, input }) => {
const { organisationId, organisationMemberId } = input;
ctx.logger.info({
input: {
organisationId,
organisationMemberId,
},
});
const organisation = await prisma.organisation.findUnique({
where: {
id: organisationId,
},
include: {
subscription: true,
organisationClaim: true,
teams: {
select: {
id: true,
},
},
members: {
select: {
id: true,
userId: true,
},
},
invites: {
where: {
status: OrganisationMemberInviteStatus.PENDING,
},
select: {
id: true,
},
},
},
});
if (!organisation) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Organisation not found',
});
}
const memberToDelete = organisation.members.find((member) => member.id === organisationMemberId);
if (!memberToDelete) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Member not found in this organisation',
});
}
if (memberToDelete.userId === organisation.ownerUserId) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Cannot remove the organisation owner. Transfer ownership first.',
});
}
const newMemberCount = organisation.members.length + organisation.invites.length - 1;
// Removing a member is a reducing operation, so we don't gate it on the
// subscription being present. Sync Stripe only when one exists.
if (organisation.subscription) {
await syncMemberCountWithStripeSeatPlan(
organisation.subscription,
organisation.organisationClaim,
newMemberCount,
);
}
const teamIds = organisation.teams.map((team) => team.id);
await prisma.$transaction(async (tx) => {
// Removing an OrganisationMember cascades the user out of every team in
// the org via OrganisationGroupMember, but their authored Envelope rows
// still reference them. Reassign those to the org owner so they remain
// reachable after the member loses access (mirrors delete-user.ts).
if (teamIds.length > 0) {
await tx.envelope.updateMany({
where: {
userId: memberToDelete.userId,
teamId: {
in: teamIds,
},
},
data: {
userId: organisation.ownerUserId,
},
});
}
await tx.organisationMember.delete({
where: {
id: organisationMemberId,
organisationId,
},
});
});
await jobs.triggerJob({
name: 'send.organisation-member-left.email',
payload: {
organisationId,
memberUserId: memberToDelete.userId,
},
});
});
@@ -0,0 +1,11 @@
import { z } from 'zod';
export const ZDeleteAdminOrganisationMemberRequestSchema = z.object({
organisationId: z.string().min(1),
organisationMemberId: z.string().min(1),
});
export const ZDeleteAdminOrganisationMemberResponseSchema = z.void();
export type TDeleteAdminOrganisationMemberRequest = z.infer<typeof ZDeleteAdminOrganisationMemberRequestSchema>;
export type TDeleteAdminOrganisationMemberResponse = z.infer<typeof ZDeleteAdminOrganisationMemberResponseSchema>;
@@ -0,0 +1,101 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
import { OrganisationGroupType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import { ZDeleteAdminTeamMemberRequestSchema, ZDeleteAdminTeamMemberResponseSchema } from './delete-team-member.types';
export const deleteAdminTeamMemberRoute = adminProcedure
.input(ZDeleteAdminTeamMemberRequestSchema)
.output(ZDeleteAdminTeamMemberResponseSchema)
.mutation(async ({ ctx, input }) => {
const { teamId, memberId } = input;
ctx.logger.info({
input: {
teamId,
memberId,
},
});
const team = await prisma.team.findUnique({
where: {
id: teamId,
},
include: {
organisation: {
select: {
ownerUserId: true,
},
},
teamGroups: {
where: {
organisationGroup: {
type: OrganisationGroupType.INTERNAL_TEAM,
organisationGroupMembers: {
some: {
organisationMember: {
id: memberId,
},
},
},
},
},
},
},
});
if (!team) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Team not found',
});
}
const teamGroupToRemoveMemberFrom = team.teamGroups[0];
if (!teamGroupToRemoveMemberFrom) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Member is not directly assigned to this team. Inherited members cannot be removed here.',
});
}
const member = await prisma.organisationMember.findUnique({
where: {
id: memberId,
},
select: {
userId: true,
},
});
if (!member) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Member not found',
});
}
await prisma.$transaction(async (tx) => {
// Removing a user from a single team drops their INTERNAL_TEAM
// OrganisationGroupMember link, but Envelope rows they authored in this
// team still point at their userId. Reassign to the org owner so those
// envelopes remain reachable after the member loses team access.
await tx.envelope.updateMany({
where: {
userId: member.userId,
teamId,
},
data: {
userId: team.organisation.ownerUserId,
},
});
await tx.organisationGroupMember.delete({
where: {
organisationMemberId_groupId: {
organisationMemberId: memberId,
groupId: teamGroupToRemoveMemberFrom.organisationGroupId,
},
},
});
});
});
@@ -0,0 +1,11 @@
import { z } from 'zod';
export const ZDeleteAdminTeamMemberRequestSchema = z.object({
teamId: z.number().min(1),
memberId: z.string().min(1),
});
export const ZDeleteAdminTeamMemberResponseSchema = z.void();
export type TDeleteAdminTeamMemberRequest = z.infer<typeof ZDeleteAdminTeamMemberRequestSchema>;
export type TDeleteAdminTeamMemberResponse = z.infer<typeof ZDeleteAdminTeamMemberResponseSchema>;
@@ -0,0 +1,81 @@
import { PDF_SIZE_A4_72PPI } from '@documenso/lib/constants/pdf';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { generateAuditLogPdf } from '@documenso/lib/server-only/pdf/generate-audit-log-pdf';
import { unsafeBuildEnvelopeIdQuery } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
ZAdminDownloadDocumentAuditLogsRequestSchema,
ZAdminDownloadDocumentAuditLogsResponseSchema,
} from './download-document-audit-logs.types';
export const downloadDocumentAuditLogsRoute = adminProcedure
.input(ZAdminDownloadDocumentAuditLogsRequestSchema)
.output(ZAdminDownloadDocumentAuditLogsResponseSchema)
.mutation(async ({ input, ctx }) => {
const { envelopeId } = input;
ctx.logger.info({
input: {
envelopeId,
},
});
const envelope = await prisma.envelope.findFirst({
where: unsafeBuildEnvelopeIdQuery(
{
type: 'envelopeId',
id: envelopeId,
},
EnvelopeType.DOCUMENT,
),
include: {
documentMeta: true,
envelopeItems: true,
user: {
select: {
id: true,
name: true,
email: true,
},
},
recipients: {
orderBy: {
id: 'asc',
},
},
fields: true,
},
});
if (!envelope) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Envelope not found',
});
}
const auditLogPdf = await generateAuditLogPdf({
envelope,
recipients: envelope.recipients,
fields: envelope.fields,
language: envelope.documentMeta.language,
envelopeOwner: {
email: envelope.user.email,
name: envelope.user.name || '',
},
envelopeItems: envelope.envelopeItems.map((item) => item.title),
pageWidth: PDF_SIZE_A4_72PPI.width,
pageHeight: PDF_SIZE_A4_72PPI.height,
});
const result = await auditLogPdf.save();
const base64 = Buffer.from(result).toString('base64');
return {
data: base64,
envelopeTitle: envelope.title,
};
});
@@ -0,0 +1,13 @@
import { z } from 'zod';
export const ZAdminDownloadDocumentAuditLogsRequestSchema = z.object({
envelopeId: z.string(),
});
export const ZAdminDownloadDocumentAuditLogsResponseSchema = z.object({
data: z.string(),
envelopeTitle: z.string(),
});
export type TAdminDownloadDocumentAuditLogsRequest = z.infer<typeof ZAdminDownloadDocumentAuditLogsRequestSchema>;
export type TAdminDownloadDocumentAuditLogsResponse = z.infer<typeof ZAdminDownloadDocumentAuditLogsResponseSchema>;
@@ -1,7 +1,6 @@
import { Prisma } from '@prisma/client';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { prisma } from '@documenso/prisma';
import { Prisma } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
@@ -120,14 +119,16 @@ export const findAdminOrganisations = async ({
};
}
const orderBy: Prisma.OrganisationOrderByWithRelationInput[] = query
? [{ subscription: { status: 'asc' } }, { name: 'asc' }]
: [{ createdAt: 'desc' }];
const [data, count] = await Promise.all([
prisma.organisation.findMany({
where: whereClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
orderBy: {
createdAt: 'desc',
},
orderBy,
select: {
id: true,
createdAt: true,
@@ -1,9 +1,8 @@
import { z } from 'zod';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import OrganisationSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationSchema';
import SubscriptionSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionSchema';
import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
import { z } from 'zod';
export const ZFindAdminOrganisationsRequestSchema = ZFindSearchParamsSchema.extend({
ownerUserId: z.number().optional(),
@@ -1,10 +1,9 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { parseDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { unsafeBuildEnvelopeIdQuery } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
@@ -16,13 +15,7 @@ export const findDocumentAuditLogsRoute = adminProcedure
.input(ZFindDocumentAuditLogsRequestSchema)
.output(ZFindDocumentAuditLogsResponseSchema)
.query(async ({ input }) => {
const {
envelopeId,
page = 1,
perPage = 50,
orderByColumn = 'createdAt',
orderByDirection = 'desc',
} = input;
const { envelopeId, page = 1, perPage = 50, orderByColumn = 'createdAt', orderByDirection = 'desc' } = input;
const envelope = await prisma.envelope.findFirst({
where: unsafeBuildEnvelopeIdQuery(
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { ZDocumentAuditLogSchema } from '@documenso/lib/types/document-audit-logs';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { z } from 'zod';
export const ZFindDocumentAuditLogsRequestSchema = ZFindSearchParamsSchema.extend({
envelopeId: z.string(),
@@ -1,18 +1,11 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import {
mapSecondaryIdToDocumentId,
unsafeBuildEnvelopeIdQuery,
} from '@documenso/lib/utils/envelope';
import { mapSecondaryIdToDocumentId, unsafeBuildEnvelopeIdQuery } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
ZFindDocumentJobsRequestSchema,
ZFindDocumentJobsResponseSchema,
} from './find-document-jobs.types';
import { ZFindDocumentJobsRequestSchema, ZFindDocumentJobsResponseSchema } from './find-document-jobs.types';
export const findDocumentJobsRoute = adminProcedure
.input(ZFindDocumentJobsRequestSchema)
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import BackgroundJobSchema from '@documenso/prisma/generated/zod/modelSchema/BackgroundJobSchema';
import { z } from 'zod';
export const ZFindDocumentJobsRequestSchema = ZFindSearchParamsSchema.extend({
envelopeId: z.string(),
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { z } from 'zod';
export const ZFindDocumentsRequestSchema = ZFindSearchParamsSchema.extend({
perPage: z.number().optional().default(20),
@@ -0,0 +1,92 @@
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { prisma } from '@documenso/prisma';
import { Prisma } from '@prisma/client';
import { adminProcedure } from '../trpc';
import { ZFindEmailDomainsRequestSchema, ZFindEmailDomainsResponseSchema } from './find-email-domains.types';
export const findEmailDomainsRoute = adminProcedure
.input(ZFindEmailDomainsRequestSchema)
.output(ZFindEmailDomainsResponseSchema)
.query(async ({ input }) => {
const { query, page, perPage, status } = input;
return await findEmailDomains({ query, page, perPage, status });
});
type FindEmailDomainsOptions = {
query?: string;
page?: number;
perPage?: number;
status?: 'PENDING' | 'ACTIVE';
};
const findEmailDomains = async ({ query, page = 1, perPage = 20, status }: FindEmailDomainsOptions) => {
const whereClause: Prisma.EmailDomainWhereInput = {};
if (query) {
whereClause.OR = [
{
domain: {
contains: query,
mode: Prisma.QueryMode.insensitive,
},
},
{
organisation: {
name: {
contains: query,
mode: Prisma.QueryMode.insensitive,
},
},
},
];
}
if (status) {
whereClause.status = status;
}
const [data, count] = await Promise.all([
prisma.emailDomain.findMany({
where: whereClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
orderBy: {
createdAt: 'desc',
},
select: {
id: true,
domain: true,
status: true,
selector: true,
createdAt: true,
updatedAt: true,
lastVerifiedAt: true,
organisation: {
select: {
id: true,
name: true,
url: true,
},
},
_count: {
select: {
emails: true,
},
},
},
}),
prisma.emailDomain.count({
where: whereClause,
}),
]);
return {
data,
count,
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
};
@@ -0,0 +1,35 @@
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import EmailDomainStatusSchema from '@documenso/prisma/generated/zod/inputTypeSchemas/EmailDomainStatusSchema';
import EmailDomainSchema from '@documenso/prisma/generated/zod/modelSchema/EmailDomainSchema';
import OrganisationSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationSchema';
import { z } from 'zod';
export const ZFindEmailDomainsRequestSchema = ZFindSearchParamsSchema.extend({
status: EmailDomainStatusSchema.optional(),
});
export const ZFindEmailDomainsResponseSchema = ZFindResultResponse.extend({
data: EmailDomainSchema.pick({
id: true,
domain: true,
status: true,
selector: true,
createdAt: true,
updatedAt: true,
lastVerifiedAt: true,
})
.extend({
organisation: OrganisationSchema.pick({
id: true,
name: true,
url: true,
}),
_count: z.object({
emails: z.number(),
}),
})
.array(),
});
export type TFindEmailDomainsRequest = z.infer<typeof ZFindEmailDomainsRequestSchema>;
export type TFindEmailDomainsResponse = z.infer<typeof ZFindEmailDomainsResponseSchema>;
@@ -1,9 +1,8 @@
import type { Prisma } from '@prisma/client';
import type { z } from 'zod';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { prisma } from '@documenso/prisma';
import type SubscriptionClaimSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionClaimSchema';
import type { Prisma } from '@prisma/client';
import type { z } from 'zod';
import { adminProcedure } from '../trpc';
import {
@@ -26,11 +25,7 @@ type FindSubscriptionClaimsOptions = {
perPage?: number;
};
export const findSubscriptionClaims = async ({
query,
page = 1,
perPage = 50,
}: FindSubscriptionClaimsOptions) => {
export const findSubscriptionClaims = async ({ query, page = 1, perPage = 50 }: FindSubscriptionClaimsOptions) => {
let whereClause: Prisma.SubscriptionClaimWhereInput = {};
if (query) {
@@ -1,7 +1,6 @@
import type { z } from 'zod';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import SubscriptionClaimSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionClaimSchema';
import type { z } from 'zod';
export const ZFindSubscriptionClaimsRequestSchema = ZFindSearchParamsSchema.extend({});
@@ -0,0 +1,16 @@
import { adminFindUnsealedDocuments } from '@documenso/lib/server-only/admin/admin-find-unsealed-documents';
import { adminProcedure } from '../trpc';
import {
ZFindUnsealedDocumentsRequestSchema,
ZFindUnsealedDocumentsResponseSchema,
} from './find-unsealed-documents.types';
export const findUnsealedDocumentsRoute = adminProcedure
.input(ZFindUnsealedDocumentsRequestSchema)
.output(ZFindUnsealedDocumentsResponseSchema)
.query(async ({ input }) => {
const { page, perPage } = input;
return await adminFindUnsealedDocuments({ page, perPage });
});
@@ -0,0 +1,30 @@
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { z } from 'zod';
export const ZFindUnsealedDocumentsRequestSchema = ZFindSearchParamsSchema.pick({
page: true,
perPage: true,
}).extend({
perPage: z.number().optional().default(20),
});
export const ZAdminUnsealedDocumentSchema = z.object({
id: z.string(),
secondaryId: z.string(),
title: z.string(),
status: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
userId: z.number(),
teamId: z.number(),
ownerName: z.string().nullable(),
ownerEmail: z.string(),
lastSignedAt: z.date().nullable(),
});
export const ZFindUnsealedDocumentsResponseSchema = ZFindResultResponse.extend({
data: ZAdminUnsealedDocumentSchema.array(),
});
export type TFindUnsealedDocumentsRequest = z.infer<typeof ZFindUnsealedDocumentsRequestSchema>;
export type TFindUnsealedDocumentsResponse = z.infer<typeof ZFindUnsealedDocumentsResponseSchema>;
@@ -1,8 +1,7 @@
import { Prisma } from '@prisma/client';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { getHighestTeamRoleInGroup } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
import { Prisma } from '@prisma/client';
import { adminProcedure } from '../trpc';
import { ZFindUserTeamsRequestSchema, ZFindUserTeamsResponseSchema } from './find-user-teams.types';
@@ -1,9 +1,8 @@
import { z } from 'zod';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { TeamMemberRoleSchema } from '@documenso/prisma/generated/zod/inputTypeSchemas/TeamMemberRoleSchema';
import OrganisationSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationSchema';
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
import { z } from 'zod';
export const ZFindUserTeamsRequestSchema = ZFindSearchParamsSchema.extend({
userId: z.number(),
@@ -1,5 +1,3 @@
import { z } from 'zod';
import { ZOrganisationSchema } from '@documenso/lib/types/organisation';
import OrganisationClaimSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationClaimSchema';
import OrganisationGlobalSettingsSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationGlobalSettingsSchema';
@@ -9,6 +7,7 @@ import OrganisationMemberSchema from '@documenso/prisma/generated/zod/modelSchem
import SubscriptionSchema from '@documenso/prisma/generated/zod/modelSchema/SubscriptionSchema';
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
import { z } from 'zod';
export const ZGetAdminOrganisationRequestSchema = z.object({
organisationId: z.string(),
@@ -0,0 +1,130 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getHighestOrganisationRoleInGroup } from '@documenso/lib/utils/organisations';
import { getHighestTeamRoleInGroup } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
import { OrganisationMemberInviteStatus } from '@prisma/client';
import { adminProcedure } from '../trpc';
import { ZGetAdminTeamRequestSchema, ZGetAdminTeamResponseSchema } from './get-admin-team.types';
export const getAdminTeamRoute = adminProcedure
.input(ZGetAdminTeamRequestSchema)
.output(ZGetAdminTeamResponseSchema)
.query(async ({ input, ctx }) => {
const { teamId } = input;
ctx.logger.info({
input: {
teamId,
},
});
const team = await prisma.team.findUnique({
where: {
id: teamId,
},
include: {
organisation: {
select: {
id: true,
name: true,
url: true,
ownerUserId: true,
},
},
teamEmail: true,
teamGlobalSettings: true,
},
});
if (!team) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Team not found',
});
}
const [teamMembers, pendingInvites] = await Promise.all([
prisma.organisationMember.findMany({
where: {
organisationId: team.organisationId,
organisationGroupMembers: {
some: {
group: {
teamGroups: {
some: {
teamId,
},
},
},
},
},
},
orderBy: {
createdAt: 'desc',
},
select: {
id: true,
userId: true,
createdAt: true,
organisationGroupMembers: {
include: {
group: {
include: {
teamGroups: {
where: {
teamId,
},
},
},
},
},
},
user: {
select: {
id: true,
email: true,
name: true,
},
},
},
}),
// Invites are organisation-scoped in the schema (no team relation), so this is intentionally
// all pending invites for the team's parent organisation.
prisma.organisationMemberInvite.findMany({
where: {
organisationId: team.organisationId,
status: OrganisationMemberInviteStatus.PENDING,
},
orderBy: {
createdAt: 'desc',
},
select: {
id: true,
email: true,
createdAt: true,
organisationRole: true,
status: true,
},
}),
]);
const mappedTeamMembers = teamMembers.map((teamMember) => {
const groups = teamMember.organisationGroupMembers.map(({ group }) => group);
return {
id: teamMember.id,
userId: teamMember.userId,
createdAt: teamMember.createdAt,
user: teamMember.user,
teamRole: getHighestTeamRoleInGroup(groups.flatMap((group) => group.teamGroups)),
organisationRole: getHighestOrganisationRoleInGroup(groups),
};
});
return {
...team,
memberCount: mappedTeamMembers.length,
teamMembers: mappedTeamMembers,
pendingInvites,
};
});
@@ -0,0 +1,51 @@
import { OrganisationMemberRoleSchema } from '@documenso/prisma/generated/zod/inputTypeSchemas/OrganisationMemberRoleSchema';
import { TeamMemberRoleSchema } from '@documenso/prisma/generated/zod/inputTypeSchemas/TeamMemberRoleSchema';
import OrganisationMemberInviteSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationMemberInviteSchema';
import OrganisationMemberSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationMemberSchema';
import OrganisationSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationSchema';
import TeamEmailSchema from '@documenso/prisma/generated/zod/modelSchema/TeamEmailSchema';
import TeamGlobalSettingsSchema from '@documenso/prisma/generated/zod/modelSchema/TeamGlobalSettingsSchema';
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
import { z } from 'zod';
export const ZGetAdminTeamRequestSchema = z.object({
teamId: z.number().min(1),
});
export const ZGetAdminTeamResponseSchema = TeamSchema.extend({
organisation: OrganisationSchema.pick({
id: true,
name: true,
url: true,
ownerUserId: true,
}),
teamEmail: TeamEmailSchema.nullable(),
teamGlobalSettings: TeamGlobalSettingsSchema.nullable(),
memberCount: z.number(),
teamMembers: OrganisationMemberSchema.pick({
id: true,
userId: true,
createdAt: true,
})
.extend({
user: UserSchema.pick({
id: true,
email: true,
name: true,
}),
teamRole: TeamMemberRoleSchema,
organisationRole: OrganisationMemberRoleSchema,
})
.array(),
pendingInvites: OrganisationMemberInviteSchema.pick({
id: true,
email: true,
createdAt: true,
organisationRole: true,
status: true,
}).array(),
});
export type TGetAdminTeamRequest = z.infer<typeof ZGetAdminTeamRequestSchema>;
export type TGetAdminTeamResponse = z.infer<typeof ZGetAdminTeamResponseSchema>;
@@ -0,0 +1,39 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
import { adminProcedure } from '../trpc';
import { ZGetEmailDomainRequestSchema, ZGetEmailDomainResponseSchema } from './get-email-domain.types';
export const getEmailDomainRoute = adminProcedure
.input(ZGetEmailDomainRequestSchema)
.output(ZGetEmailDomainResponseSchema)
.query(async ({ input }) => {
const { emailDomainId } = input;
const emailDomain = await prisma.emailDomain.findUnique({
where: {
id: emailDomainId,
},
omit: {
privateKey: true,
},
include: {
organisation: {
select: {
id: true,
name: true,
url: true,
},
},
emails: true,
},
});
if (!emailDomain) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Email domain not found',
});
}
return emailDomain;
});
@@ -0,0 +1,29 @@
import { ZOrganisationEmailLiteSchema } from '@documenso/lib/types/organisation-email';
import EmailDomainSchema from '@documenso/prisma/generated/zod/modelSchema/EmailDomainSchema';
import OrganisationSchema from '@documenso/prisma/generated/zod/modelSchema/OrganisationSchema';
import { z } from 'zod';
export const ZGetEmailDomainRequestSchema = z.object({
emailDomainId: z.string(),
});
export const ZGetEmailDomainResponseSchema = EmailDomainSchema.pick({
id: true,
domain: true,
status: true,
selector: true,
publicKey: true,
createdAt: true,
updatedAt: true,
lastVerifiedAt: true,
}).extend({
organisation: OrganisationSchema.pick({
id: true,
name: true,
url: true,
}),
emails: ZOrganisationEmailLiteSchema.array(),
});
export type TGetEmailDomainRequest = z.infer<typeof ZGetEmailDomainRequestSchema>;
export type TGetEmailDomainResponse = z.infer<typeof ZGetEmailDomainResponseSchema>;
@@ -1,6 +1,5 @@
import { z } from 'zod';
import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
import { z } from 'zod';
export const ZGetUserRequestSchema = z.object({
id: z.number().min(1),
@@ -1,9 +1,8 @@
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { generateDatabaseId } from '@documenso/lib/universal/id';
import { getHighestOrganisationRoleInGroup } from '@documenso/lib/utils/organisations';
import { prisma } from '@documenso/prisma';
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
@@ -78,13 +77,9 @@ export const promoteMemberToOwnerRoute = adminProcedure
);
// Find the current and target organisation groups
const currentMemberGroup = organisation.groups.find(
(group) => group.organisationRole === currentOrganisationRole,
);
const currentMemberGroup = organisation.groups.find((group) => group.organisationRole === currentOrganisationRole);
const adminGroup = organisation.groups.find(
(group) => group.organisationRole === OrganisationMemberRole.ADMIN,
);
const adminGroup = organisation.groups.find((group) => group.organisationRole === OrganisationMemberRole.ADMIN);
if (!currentMemberGroup) {
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
@@ -0,0 +1,22 @@
import { reregisterEmailDomain } from '@documenso/ee/server-only/lib/reregister-email-domain';
import { adminProcedure } from '../trpc';
import {
ZReregisterEmailDomainRequestSchema,
ZReregisterEmailDomainResponseSchema,
} from './reregister-email-domain.types';
export const reregisterEmailDomainRoute = adminProcedure
.input(ZReregisterEmailDomainRequestSchema)
.output(ZReregisterEmailDomainResponseSchema)
.mutation(async ({ input, ctx }) => {
const { emailDomainId } = input;
ctx.logger.info({
input: {
emailDomainId,
},
});
await reregisterEmailDomain({ emailDomainId });
});
@@ -0,0 +1,10 @@
import { z } from 'zod';
export const ZReregisterEmailDomainRequestSchema = z.object({
emailDomainId: z.string(),
});
export const ZReregisterEmailDomainResponseSchema = z.void();
export type TReregisterEmailDomainRequest = z.infer<typeof ZReregisterEmailDomainRequestSchema>;
export type TReregisterEmailDomainResponse = z.infer<typeof ZReregisterEmailDomainResponseSchema>;
@@ -1,15 +1,11 @@
import { EnvelopeType } from '@prisma/client';
import { jobs } from '@documenso/lib/jobs/client';
import { unsafeGetEntireEnvelope } from '@documenso/lib/server-only/admin/get-entire-document';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { EnvelopeType } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
ZResealDocumentRequestSchema,
ZResealDocumentResponseSchema,
} from './reseal-document.types';
import { ZResealDocumentRequestSchema, ZResealDocumentResponseSchema } from './reseal-document.types';
export const resealDocumentRoute = adminProcedure
.input(ZResealDocumentRequestSchema)
@@ -2,10 +2,7 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
import { adminProcedure } from '../trpc';
import {
ZResetTwoFactorRequestSchema,
ZResetTwoFactorResponseSchema,
} from './reset-two-factor-authentication.types';
import { ZResetTwoFactorRequestSchema, ZResetTwoFactorResponseSchema } from './reset-two-factor-authentication.types';
export const resetTwoFactorRoute = adminProcedure
.input(ZResetTwoFactorRequestSchema)
@@ -3,22 +3,31 @@ import { createAdminOrganisationRoute } from './create-admin-organisation';
import { createStripeCustomerRoute } from './create-stripe-customer';
import { createSubscriptionClaimRoute } from './create-subscription-claim';
import { deleteDocumentRoute } from './delete-document';
import { deleteAdminOrganisationMemberRoute } from './delete-organisation-member';
import { deleteSubscriptionClaimRoute } from './delete-subscription-claim';
import { deleteAdminTeamMemberRoute } from './delete-team-member';
import { deleteUserRoute } from './delete-user';
import { disableUserRoute } from './disable-user';
import { downloadDocumentAuditLogsRoute } from './download-document-audit-logs';
import { enableUserRoute } from './enable-user';
import { findAdminOrganisationsRoute } from './find-admin-organisations';
import { findDocumentAuditLogsRoute } from './find-document-audit-logs';
import { findDocumentJobsRoute } from './find-document-jobs';
import { findDocumentsRoute } from './find-documents';
import { findEmailDomainsRoute } from './find-email-domains';
import { findSubscriptionClaimsRoute } from './find-subscription-claims';
import { findUnsealedDocumentsRoute } from './find-unsealed-documents';
import { findUserTeamsRoute } from './find-user-teams';
import { getAdminOrganisationRoute } from './get-admin-organisation';
import { getAdminTeamRoute } from './get-admin-team';
import { getEmailDomainRoute } from './get-email-domain';
import { getUserRoute } from './get-user';
import { promoteMemberToOwnerRoute } from './promote-member-to-owner';
import { reregisterEmailDomainRoute } from './reregister-email-domain';
import { resealDocumentRoute } from './reseal-document';
import { resetTwoFactorRoute } from './reset-two-factor-authentication';
import { resyncLicenseRoute } from './resync-license';
import { swapOrganisationSubscriptionRoute } from './swap-organisation-subscription';
import { updateAdminOrganisationRoute } from './update-admin-organisation';
import { updateOrganisationMemberRoleRoute } from './update-organisation-member-role';
import { updateRecipientRoute } from './update-recipient';
@@ -32,10 +41,12 @@ export const adminRouter = router({
get: getAdminOrganisationRoute,
create: createAdminOrganisationRoute,
update: updateAdminOrganisationRoute,
swapSubscription: swapOrganisationSubscriptionRoute,
},
organisationMember: {
promoteToOwner: promoteMemberToOwnerRoute,
updateRole: updateOrganisationMemberRoleRoute,
delete: deleteAdminOrganisationMemberRoute,
},
claims: {
find: findSubscriptionClaimsRoute,
@@ -60,13 +71,26 @@ export const adminRouter = router({
},
document: {
find: findDocumentsRoute,
findUnsealed: findUnsealedDocumentsRoute,
delete: deleteDocumentRoute,
reseal: resealDocumentRoute,
findJobs: findDocumentJobsRoute,
findAuditLogs: findDocumentAuditLogsRoute,
downloadAuditLogs: downloadDocumentAuditLogsRoute,
},
recipient: {
update: updateRecipientRoute,
},
emailDomain: {
find: findEmailDomainsRoute,
get: getEmailDomainRoute,
reregister: reregisterEmailDomainRoute,
},
team: {
get: getAdminTeamRoute,
},
teamMember: {
delete: deleteAdminTeamMemberRoute,
},
updateSiteSetting: updateSiteSettingRoute,
});
@@ -0,0 +1,139 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createOrganisationClaimUpsertData } from '@documenso/lib/server-only/organisation/create-organisation';
import { INTERNAL_CLAIM_ID, internalClaims } from '@documenso/lib/types/subscription';
import { prisma } from '@documenso/prisma';
import { SubscriptionStatus } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
ZSwapOrganisationSubscriptionRequestSchema,
ZSwapOrganisationSubscriptionResponseSchema,
} from './swap-organisation-subscription.types';
export const swapOrganisationSubscriptionRoute = adminProcedure
.input(ZSwapOrganisationSubscriptionRequestSchema)
.output(ZSwapOrganisationSubscriptionResponseSchema)
.mutation(async ({ input, ctx }) => {
const { sourceOrganisationId, targetOrganisationId } = input;
ctx.logger.info({
input: {
sourceOrganisationId,
targetOrganisationId,
},
});
if (sourceOrganisationId === targetOrganisationId) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Source and target organisations must be different',
});
}
const sourceOrg = await prisma.organisation.findUnique({
where: { id: sourceOrganisationId },
include: {
subscription: true,
organisationClaim: true,
},
});
if (!sourceOrg) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Source organisation not found',
});
}
if (
!sourceOrg.subscription ||
(sourceOrg.subscription.status !== SubscriptionStatus.ACTIVE &&
sourceOrg.subscription.status !== SubscriptionStatus.PAST_DUE)
) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Source organisation does not have an active subscription',
});
}
const targetOrg = await prisma.organisation.findUnique({
where: { id: targetOrganisationId },
include: {
subscription: true,
organisationClaim: true,
},
});
if (!targetOrg) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Target organisation not found',
});
}
if (sourceOrg.ownerUserId !== targetOrg.ownerUserId) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Both organisations must be owned by the same user',
});
}
if (
targetOrg.subscription &&
(targetOrg.subscription.status === SubscriptionStatus.ACTIVE ||
targetOrg.subscription.status === SubscriptionStatus.PAST_DUE)
) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Target organisation already has an active subscription',
});
}
const customerId = sourceOrg.customerId ?? sourceOrg.subscription.customerId;
await prisma.$transaction(async (tx) => {
// Delete stale INACTIVE subscription on target if present.
if (targetOrg.subscription) {
await tx.subscription.delete({
where: { id: targetOrg.subscription.id },
});
}
// Clear customerId on source org to avoid unique constraint violation.
await tx.organisation.update({
where: { id: sourceOrganisationId },
data: { customerId: null },
});
// Set customerId on target org.
await tx.organisation.update({
where: { id: targetOrganisationId },
data: { customerId },
});
// Move the subscription record to the target org.
await tx.subscription.update({
where: { id: sourceOrg.subscription!.id },
data: { organisationId: targetOrganisationId },
});
// Copy source org's claim entitlements to target org's claim.
if (sourceOrg.organisationClaim && targetOrg.organisationClaim) {
await tx.organisationClaim.update({
where: { id: targetOrg.organisationClaim.id },
data: {
originalSubscriptionClaimId: sourceOrg.organisationClaim.originalSubscriptionClaimId,
teamCount: sourceOrg.organisationClaim.teamCount,
memberCount: sourceOrg.organisationClaim.memberCount,
envelopeItemCount: sourceOrg.organisationClaim.envelopeItemCount,
flags: sourceOrg.organisationClaim.flags,
},
});
}
// Reset source org's claim to FREE.
if (sourceOrg.organisationClaim) {
await tx.organisationClaim.update({
where: { id: sourceOrg.organisationClaim.id },
data: {
originalSubscriptionClaimId: INTERNAL_CLAIM_ID.FREE,
...createOrganisationClaimUpsertData(internalClaims[INTERNAL_CLAIM_ID.FREE]),
},
});
}
});
});
@@ -0,0 +1,11 @@
import { z } from 'zod';
export const ZSwapOrganisationSubscriptionRequestSchema = z.object({
sourceOrganisationId: z.string(),
targetOrganisationId: z.string(),
});
export const ZSwapOrganisationSubscriptionResponseSchema = z.void();
export type TSwapOrganisationSubscriptionRequest = z.infer<typeof ZSwapOrganisationSubscriptionRequestSchema>;
export type TSwapOrganisationSubscriptionResponse = z.infer<typeof ZSwapOrganisationSubscriptionResponseSchema>;
@@ -1,9 +1,8 @@
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { generateDatabaseId } from '@documenso/lib/universal/id';
import { getHighestOrganisationRoleInGroup } from '@documenso/lib/utils/organisations';
import { prisma } from '@documenso/prisma';
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { adminProcedure } from '../trpc';
import {
@@ -88,9 +87,7 @@ export const updateOrganisationMemberRoleRoute = adminProcedure
(group) => group.organisationRole === currentOrganisationRole,
);
const adminGroup = organisation.groups.find(
(group) => group.organisationRole === OrganisationMemberRole.ADMIN,
);
const adminGroup = organisation.groups.find((group) => group.organisationRole === OrganisationMemberRole.ADMIN);
if (!currentMemberGroup) {
ctx.logger.error({
@@ -165,13 +162,9 @@ export const updateOrganisationMemberRoleRoute = adminProcedure
});
}
const currentMemberGroup = organisation.groups.find(
(group) => group.organisationRole === currentOrganisationRole,
);
const currentMemberGroup = organisation.groups.find((group) => group.organisationRole === currentOrganisationRole);
const newMemberGroup = organisation.groups.find(
(group) => group.organisationRole === targetRole,
);
const newMemberGroup = organisation.groups.find((group) => group.organisationRole === targetRole);
if (!currentMemberGroup) {
ctx.logger.error({
@@ -22,9 +22,5 @@ export const ZUpdateOrganisationMemberRoleRequestSchema = z.object({
export const ZUpdateOrganisationMemberRoleResponseSchema = z.void();
export type TUpdateOrganisationMemberRoleRequest = z.infer<
typeof ZUpdateOrganisationMemberRoleRequestSchema
>;
export type TUpdateOrganisationMemberRoleResponse = z.infer<
typeof ZUpdateOrganisationMemberRoleResponseSchema
>;
export type TUpdateOrganisationMemberRoleRequest = z.infer<typeof ZUpdateOrganisationMemberRoleRequestSchema>;
export type TUpdateOrganisationMemberRoleResponse = z.infer<typeof ZUpdateOrganisationMemberRoleResponseSchema>;
@@ -1,16 +1,13 @@
import { updateRecipient } from '@documenso/lib/server-only/admin/update-recipient';
import { adminProcedure } from '../trpc';
import {
ZUpdateRecipientRequestSchema,
ZUpdateRecipientResponseSchema,
} from './update-recipient.types';
import { ZUpdateRecipientRequestSchema, ZUpdateRecipientResponseSchema } from './update-recipient.types';
export const updateRecipientRoute = adminProcedure
.input(ZUpdateRecipientRequestSchema)
.output(ZUpdateRecipientResponseSchema)
.mutation(async ({ input, ctx }) => {
const { id, name, email } = input;
const { id, name, email, role } = input;
ctx.logger.info({
input: {
@@ -18,5 +15,5 @@ export const updateRecipientRoute = adminProcedure
},
});
await updateRecipient({ id, name, email });
await updateRecipient({ id, name, email, role });
});
@@ -1,9 +1,11 @@
import { zEmail } from '@documenso/lib/utils/zod';
import { z } from 'zod';
export const ZUpdateRecipientRequestSchema = z.object({
id: z.number().min(1),
name: z.string().optional(),
email: z.string().email().optional(),
email: zEmail().optional(),
role: z.enum(['CC', 'SIGNER', 'VIEWER', 'APPROVER', 'ASSISTANT']).optional(),
});
export const ZUpdateRecipientResponseSchema = z.void();
@@ -1,10 +1,7 @@
import { upsertSiteSetting } from '@documenso/lib/server-only/site-settings/upsert-site-setting';
import { adminProcedure } from '../trpc';
import {
ZUpdateSiteSettingRequestSchema,
ZUpdateSiteSettingResponseSchema,
} from './update-site-setting.types';
import { ZUpdateSiteSettingRequestSchema, ZUpdateSiteSettingResponseSchema } from './update-site-setting.types';
export const updateSiteSettingRoute = adminProcedure
.input(ZUpdateSiteSettingRequestSchema)
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
import { z } from 'zod';
export const ZUpdateSiteSettingRequestSchema = ZSiteSettingSchema;
@@ -29,30 +29,25 @@ export const updateSubscriptionClaimRoute = adminProcedure
const newlyEnabledFlags = getNewTruthyFlags(existingClaim.flags, data.flags);
await prisma.$transaction(async (tx) => {
await tx.subscriptionClaim.update({
where: {
id,
},
data,
});
if (Object.keys(newlyEnabledFlags).length > 0) {
await jobsClient.triggerJob({
name: 'internal.backport-subscription-claims',
payload: {
subscriptionClaimId: id,
flags: newlyEnabledFlags,
},
});
}
await prisma.subscriptionClaim.update({
where: {
id,
},
data,
});
if (Object.keys(newlyEnabledFlags).length > 0) {
await jobsClient.triggerJob({
name: 'internal.backport-subscription-claims',
payload: {
subscriptionClaimId: id,
flags: newlyEnabledFlags,
},
});
}
});
function getNewTruthyFlags(
a: Partial<TClaimFlags>,
b: Partial<TClaimFlags>,
): Record<keyof TClaimFlags, true> {
function getNewTruthyFlags(a: Partial<TClaimFlags>, b: Partial<TClaimFlags>): Record<keyof TClaimFlags, true> {
const flags: { [key in keyof TClaimFlags]?: true } = {};
for (const key in b) {
@@ -1,10 +1,11 @@
import { zEmail } from '@documenso/lib/utils/zod';
import { Role } from '@prisma/client';
import { z } from 'zod';
export const ZUpdateUserRequestSchema = z.object({
id: z.number().min(1),
name: z.string().nullish(),
email: z.string().email().optional(),
email: zEmail().optional(),
roles: z.array(z.nativeEnum(Role)).optional(),
});
@@ -1,10 +1,7 @@
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { authenticatedProcedure } from '../trpc';
import {
ZCreateApiTokenRequestSchema,
ZCreateApiTokenResponseSchema,
} from './create-api-token.types';
import { ZCreateApiTokenRequestSchema, ZCreateApiTokenResponseSchema } from './create-api-token.types';
export const createApiTokenRoute = authenticatedProcedure
.input(ZCreateApiTokenRequestSchema)
@@ -1,10 +1,7 @@
import { deleteTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id';
import { authenticatedProcedure } from '../trpc';
import {
ZDeleteApiTokenRequestSchema,
ZDeleteApiTokenResponseSchema,
} from './delete-api-token.types';
import { ZDeleteApiTokenRequestSchema, ZDeleteApiTokenResponseSchema } from './delete-api-token.types';
export const deleteApiTokenRoute = authenticatedProcedure
.input(ZDeleteApiTokenRequestSchema)
@@ -1,6 +1,5 @@
import { z } from 'zod';
import ApiTokenSchema from '@documenso/prisma/generated/zod/modelSchema/ApiTokenSchema';
import { z } from 'zod';
export const ZGetApiTokensRequestSchema = z.void();
@@ -4,9 +4,5 @@ export const ZCreatePasskeyRegistrationOptionsRequestSchema = z.void();
export const ZCreatePasskeyRegistrationOptionsResponseSchema = z.any(); // PublicKeyCredentialCreationOptions type
export type TCreatePasskeyRegistrationOptionsRequest = z.infer<
typeof ZCreatePasskeyRegistrationOptionsRequestSchema
>;
export type TCreatePasskeyRegistrationOptionsResponse = z.infer<
typeof ZCreatePasskeyRegistrationOptionsResponseSchema
>;
export type TCreatePasskeyRegistrationOptionsRequest = z.infer<typeof ZCreatePasskeyRegistrationOptionsRequestSchema>;
export type TCreatePasskeyRegistrationOptionsResponse = z.infer<typeof ZCreatePasskeyRegistrationOptionsResponseSchema>;
@@ -7,9 +7,5 @@ export const ZCreatePasskeySigninOptionsResponseSchema = z.object({
sessionId: z.string(),
});
export type TCreatePasskeySigninOptionsRequest = z.infer<
typeof ZCreatePasskeySigninOptionsRequestSchema
>;
export type TCreatePasskeySigninOptionsResponse = z.infer<
typeof ZCreatePasskeySigninOptionsResponseSchema
>;
export type TCreatePasskeySigninOptionsRequest = z.infer<typeof ZCreatePasskeySigninOptionsRequestSchema>;
export type TCreatePasskeySigninOptionsResponse = z.infer<typeof ZCreatePasskeySigninOptionsResponseSchema>;
@@ -1,6 +1,5 @@
import type { RegistrationResponseJSON } from '@simplewebauthn/server';
import { createPasskey } from '@documenso/lib/server-only/auth/create-passkey';
import type { RegistrationResponseJSON } from '@simplewebauthn/server';
import { authenticatedProcedure } from '../trpc';
import { ZCreatePasskeyRequestSchema, ZCreatePasskeyResponseSchema } from './create-passkey.types';
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZRegistrationResponseJSONSchema } from '@documenso/lib/types/webauthn';
import { z } from 'zod';
export const ZCreatePasskeyRequestSchema = z.object({
passkeyName: z.string().trim().min(1),
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import PasskeySchema from '@documenso/prisma/generated/zod/modelSchema/PasskeySchema';
import { z } from 'zod';
export const ZFindPasskeysRequestSchema = ZFindSearchParamsSchema.extend({
orderBy: z
+1 -4
View File
@@ -1,9 +1,6 @@
import { z } from 'zod';
export const ZCurrentPasswordSchema = z
.string()
.min(6, { message: 'Must be at least 6 characters in length' })
.max(72);
export const ZCurrentPasswordSchema = z.string().min(6, { message: 'Must be at least 6 characters in length' }).max(72);
export const ZPasswordSchema = z
.string()
+5 -9
View File
@@ -1,8 +1,3 @@
import type { Session } from '@prisma/client';
import type { Context } from 'hono';
import type { Logger } from 'pino';
import { z } from 'zod';
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import type { RootApiLog } from '@documenso/lib/types/api-logs';
@@ -11,16 +6,17 @@ import { alphaid } from '@documenso/lib/universal/id';
import { logger } from '@documenso/lib/utils/logger';
// This is a bit nasty. Todo: Extract
import type { HonoEnv } from '@documenso/remix/server/router';
import type { Session } from '@prisma/client';
import type { Context } from 'hono';
import type { Logger } from 'pino';
import { z } from 'zod';
type CreateTrpcContextOptions = {
c: Context<HonoEnv>;
requestSource: 'app' | 'apiV1' | 'apiV2';
};
export const createTrpcContext = async ({
c,
requestSource,
}: CreateTrpcContextOptions): Promise<TrpcContext> => {
export const createTrpcContext = async ({ c, requestSource }: CreateTrpcContextOptions): Promise<TrpcContext> => {
const { session, user } = await getOptionalSession(c);
const req = c.req.raw;
@@ -1,12 +1,13 @@
import { EnvelopeType } from '@prisma/client';
import { TRPCError } from '@trpc/server';
import { DateTime } from 'luxon';
import { TWO_FACTOR_EMAIL_EXPIRATION_MINUTES } from '@documenso/lib/server-only/2fa/email/constants';
import { send2FATokenEmail } from '@documenso/lib/server-only/2fa/email/send-2fa-token-email';
import { assertRateLimit } from '@documenso/lib/server-only/rate-limit/rate-limit-middleware';
import { request2FAEmailRateLimit } from '@documenso/lib/server-only/rate-limit/rate-limits';
import { DocumentAuth } from '@documenso/lib/types/document-auth';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { TRPCError } from '@trpc/server';
import { DateTime } from 'luxon';
import { procedure } from '../trpc';
import {
@@ -21,6 +22,13 @@ export const accessAuthRequest2FAEmailRoute = procedure
try {
const { token } = input;
const rateLimitResult = await request2FAEmailRateLimit.check({
ip: ctx.metadata.requestMetadata.ipAddress ?? 'unknown',
identifier: token,
});
assertRateLimit(rateLimitResult);
const user = ctx.user;
// Get document and recipient by token
@@ -9,9 +9,5 @@ export const ZAccessAuthRequest2FAEmailResponseSchema = z.object({
expiresAt: z.date(),
});
export type TAccessAuthRequest2FAEmailRequest = z.infer<
typeof ZAccessAuthRequest2FAEmailRequestSchema
>;
export type TAccessAuthRequest2FAEmailResponse = z.infer<
typeof ZAccessAuthRequest2FAEmailResponseSchema
>;
export type TAccessAuthRequest2FAEmailRequest = z.infer<typeof ZAccessAuthRequest2FAEmailRequestSchema>;
export type TAccessAuthRequest2FAEmailResponse = z.infer<typeof ZAccessAuthRequest2FAEmailResponseSchema>;
@@ -1,13 +1,10 @@
import { EnvelopeType } from '@prisma/client';
import { createAttachment } from '@documenso/lib/server-only/envelope-attachment/create-attachment';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { createAttachment } from '@documenso/lib/server-only/envelope-attachment/create-attachment';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../../trpc';
import {
ZCreateAttachmentRequestSchema,
ZCreateAttachmentResponseSchema,
} from './create-attachment.types';
import { ZCreateAttachmentRequestSchema, ZCreateAttachmentResponseSchema } from './create-attachment.types';
export const createAttachmentRoute = authenticatedProcedure
.meta({
@@ -2,10 +2,7 @@ import { deleteAttachment } from '@documenso/lib/server-only/envelope-attachment
import { ZGenericSuccessResponse } from '../../schema';
import { authenticatedProcedure } from '../../trpc';
import {
ZDeleteAttachmentRequestSchema,
ZDeleteAttachmentResponseSchema,
} from './delete-attachment.types';
import { ZDeleteAttachmentRequestSchema, ZDeleteAttachmentResponseSchema } from './delete-attachment.types';
export const deleteAttachmentRoute = authenticatedProcedure
.meta({
@@ -1,13 +1,10 @@
import { EnvelopeType } from '@prisma/client';
import { findAttachmentsByEnvelopeId } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-envelope-id';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { findAttachmentsByEnvelopeId } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-envelope-id';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../../trpc';
import {
ZFindAttachmentsRequestSchema,
ZFindAttachmentsResponseSchema,
} from './find-attachments.types';
import { ZFindAttachmentsRequestSchema, ZFindAttachmentsResponseSchema } from './find-attachments.types';
export const findAttachmentsRoute = authenticatedProcedure
.meta({
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
import { z } from 'zod';
export const ZFindAttachmentsRequestSchema = z.object({
documentId: z.number(),
@@ -2,10 +2,7 @@ import { updateAttachment } from '@documenso/lib/server-only/envelope-attachment
import { ZGenericSuccessResponse } from '../../schema';
import { authenticatedProcedure } from '../../trpc';
import {
ZUpdateAttachmentRequestSchema,
ZUpdateAttachmentResponseSchema,
} from './update-attachment.types';
import { ZUpdateAttachmentRequestSchema, ZUpdateAttachmentResponseSchema } from './update-attachment.types';
export const updateAttachmentRoute = authenticatedProcedure
.meta({
@@ -1,5 +1,3 @@
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
@@ -7,12 +5,13 @@ import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envel
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
createDocumentTemporaryMeta,
ZCreateDocumentTemporaryRequestSchema,
ZCreateDocumentTemporaryResponseSchema,
createDocumentTemporaryMeta,
} from './create-document-temporary.types';
/**
@@ -1,10 +1,5 @@
import { z } from 'zod';
import { ZDocumentSchema } from '@documenso/lib/types/document';
import {
ZDocumentAccessAuthTypesSchema,
ZDocumentActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import { ZDocumentAccessAuthTypesSchema, ZDocumentActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
@@ -16,14 +11,11 @@ import {
ZFieldWidthSchema,
} from '@documenso/lib/types/field';
import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
import { z } from 'zod';
import { ZCreateRecipientSchema } from '../recipient-router/schema';
import type { TrpcRouteMeta } from '../trpc';
import {
ZDocumentExternalIdSchema,
ZDocumentTitleSchema,
ZDocumentVisibilitySchema,
} from './schema';
import { ZDocumentExternalIdSchema, ZDocumentTitleSchema, ZDocumentVisibilitySchema } from './schema';
/**
* Temporariy endpoint for V2 Beta until we allow passthrough documents on create.
@@ -88,12 +80,8 @@ export const ZCreateDocumentTemporaryResponseSchema = z.object({
document: ZDocumentSchema,
uploadUrl: z
.string()
.describe(
'The URL to upload the document PDF to. Use a PUT request with the file via form-data',
),
.describe('The URL to upload the document PDF to. Use a PUT request with the file via form-data'),
});
export type TCreateDocumentTemporaryRequest = z.infer<typeof ZCreateDocumentTemporaryRequestSchema>;
export type TCreateDocumentTemporaryResponse = z.infer<
typeof ZCreateDocumentTemporaryResponseSchema
>;
export type TCreateDocumentTemporaryResponse = z.infer<typeof ZCreateDocumentTemporaryResponseSchema>;
@@ -1,17 +1,16 @@
import { EnvelopeType } from '@prisma/client';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { insertFormValuesInPdf } from '@documenso/lib/server-only/pdf/insert-form-values-in-pdf';
import { putNormalizedPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
createDocumentMeta,
ZCreateDocumentRequestSchema,
ZCreateDocumentResponseSchema,
createDocumentMeta,
} from './create-document.types';
export const createDocumentRoute = authenticatedProcedure
@@ -1,10 +1,4 @@
import { z } from 'zod';
import { zfd } from 'zod-form-data';
import {
ZDocumentAccessAuthTypesSchema,
ZDocumentActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import { ZDocumentAccessAuthTypesSchema, ZDocumentActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
import { ZDocumentVisibilitySchema } from '@documenso/lib/types/document-visibility';
@@ -17,8 +11,10 @@ import {
ZFieldWidthSchema,
} from '@documenso/lib/types/field';
import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
import { z } from 'zod';
import { zfd } from 'zod-form-data';
import { zodFormData } from '../../utils/zod-form-data';
import { zfdFile, zodFormData } from '../../utils/zod-form-data';
import { ZCreateRecipientSchema } from '../recipient-router/schema';
import type { TrpcRouteMeta } from '../trpc';
import { ZDocumentExternalIdSchema, ZDocumentTitleSchema } from './schema';
@@ -79,7 +75,7 @@ export const ZCreateDocumentPayloadSchema = z.object({
export const ZCreateDocumentRequestSchema = zodFormData({
payload: zfd.json(ZCreateDocumentPayloadSchema),
file: zfd.file(),
file: zfdFile(),
});
export const ZCreateDocumentResponseSchema = z.object({
@@ -3,9 +3,9 @@ import { deleteDocument } from '@documenso/lib/server-only/document/delete-docum
import { ZGenericSuccessResponse } from '../schema';
import { authenticatedProcedure } from '../trpc';
import {
deleteDocumentMeta,
ZDeleteDocumentRequestSchema,
ZDeleteDocumentResponseSchema,
deleteDocumentMeta,
} from './delete-document.types';
export const deleteDocumentRoute = authenticatedProcedure
@@ -1,12 +1,12 @@
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { mapEnvelopeToDocumentLite } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
import {
distributeDocumentMeta,
ZDistributeDocumentRequestSchema,
ZDistributeDocumentResponseSchema,
distributeDocumentMeta,
} from './distribute-document.types';
export const distributeDocumentRoute = authenticatedProcedure
@@ -1,5 +1,3 @@
import { z } from 'zod';
import { ZDocumentLiteSchema } from '@documenso/lib/types/document';
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import {
@@ -11,6 +9,8 @@ import {
ZDocumentMetaSubjectSchema,
ZDocumentMetaTimezoneSchema,
} from '@documenso/lib/types/document-meta';
import { zEmail } from '@documenso/lib/utils/zod';
import { z } from 'zod';
import type { TrpcRouteMeta } from '../trpc';
@@ -36,7 +36,7 @@ export const ZDistributeDocumentRequestSchema = z.object({
redirectUrl: ZDocumentMetaRedirectUrlSchema.optional(),
language: ZDocumentMetaLanguageSchema.optional(),
emailId: z.string().nullish(),
emailReplyTo: z.string().email().nullish(),
emailReplyTo: zEmail().nullish(),
emailSettings: ZDocumentEmailSettingsSchema.optional(),
})
.optional(),
@@ -1,9 +1,8 @@
import { EnvelopeType } from '@prisma/client';
import { PDF_SIZE_A4_72PPI } from '@documenso/lib/constants/pdf';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { generateAuditLogPdf } from '@documenso/lib/server-only/pdf/generate-audit-log-pdf';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
@@ -9,9 +9,5 @@ export const ZDownloadDocumentAuditLogsResponseSchema = z.object({
envelopeTitle: z.string(),
});
export type TDownloadDocumentAuditLogsRequest = z.infer<
typeof ZDownloadDocumentAuditLogsRequestSchema
>;
export type TDownloadDocumentAuditLogsResponse = z.infer<
typeof ZDownloadDocumentAuditLogsResponseSchema
>;
export type TDownloadDocumentAuditLogsRequest = z.infer<typeof ZDownloadDocumentAuditLogsRequestSchema>;
export type TDownloadDocumentAuditLogsResponse = z.infer<typeof ZDownloadDocumentAuditLogsResponseSchema>;
@@ -1,16 +1,15 @@
import type { DocumentData } from '@prisma/client';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getPresignGetUrl } from '@documenso/lib/universal/upload/server-actions';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import type { DocumentData } from '@prisma/client';
import { DocumentDataType, EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
downloadDocumentMeta,
ZDownloadDocumentRequestSchema,
ZDownloadDocumentResponseSchema,
downloadDocumentMeta,
} from './download-document-beta.types';
export const downloadDocumentBetaRoute = authenticatedProcedure
@@ -49,8 +48,7 @@ export const downloadDocumentBetaRoute = authenticatedProcedure
if (envelope.envelopeItems.length !== 1 || !documentData) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message:
'This endpoint only supports documents with a single item. Use envelopes API instead.',
message: 'This endpoint only supports documents with a single item. Use envelopes API instead.',
});
}
@@ -67,8 +65,7 @@ export const downloadDocumentBetaRoute = authenticatedProcedure
}
try {
const data =
version === 'original' ? documentData.initialData || documentData.data : documentData.data;
const data = version === 'original' ? documentData.initialData || documentData.data : documentData.data;
const { url } = await getPresignGetUrl(data);
@@ -1,11 +1,10 @@
import { EnvelopeType } from '@prisma/client';
import { PDF_SIZE_A4_72PPI } from '@documenso/lib/constants/pdf';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { generateCertificatePdf } from '@documenso/lib/server-only/pdf/generate-certificate-pdf';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
@@ -9,9 +9,5 @@ export const ZDownloadDocumentCertificateResponseSchema = z.object({
envelopeTitle: z.string(),
});
export type TDownloadDocumentCertificateRequest = z.infer<
typeof ZDownloadDocumentCertificateRequestSchema
>;
export type TDownloadDocumentCertificateResponse = z.infer<
typeof ZDownloadDocumentCertificateResponseSchema
>;
export type TDownloadDocumentCertificateRequest = z.infer<typeof ZDownloadDocumentCertificateRequestSchema>;
export type TDownloadDocumentCertificateResponse = z.infer<typeof ZDownloadDocumentCertificateResponseSchema>;
@@ -1,8 +1,8 @@
import { authenticatedProcedure } from '../trpc';
import {
downloadDocumentMeta,
ZDownloadDocumentRequestSchema,
ZDownloadDocumentResponseSchema,
downloadDocumentMeta,
} from './download-document.types';
export const downloadDocumentRoute = authenticatedProcedure
@@ -2,9 +2,9 @@ import { duplicateEnvelope } from '@documenso/lib/server-only/envelope/duplicate
import { authenticatedProcedure } from '../trpc';
import {
duplicateDocumentMeta,
ZDuplicateDocumentRequestSchema,
ZDuplicateDocumentResponseSchema,
duplicateDocumentMeta,
} from './duplicate-document.types';
export const duplicateDocumentRoute = authenticatedProcedure
@@ -12,15 +12,7 @@ export const findDocumentAuditLogsRoute = authenticatedProcedure
.query(async ({ input, ctx }) => {
const { teamId } = ctx;
const {
page,
perPage,
documentId,
cursor,
filterForRecentActivity,
orderByColumn,
orderByDirection,
} = input;
const { page, perPage, documentId, cursor, filterForRecentActivity, orderByColumn, orderByDirection } = input;
ctx.logger.info({
input: {
@@ -1,7 +1,6 @@
import { z } from 'zod';
import { ZDocumentAuditLogSchema } from '@documenso/lib/types/document-audit-logs';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { z } from 'zod';
export const ZFindDocumentAuditLogsRequestSchema = ZFindSearchParamsSchema.extend({
documentId: z.number().min(1),
@@ -1,7 +1,5 @@
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
@@ -30,34 +28,18 @@ export const findDocumentsInternalRoute = authenticatedProcedure
folderId,
} = input;
const getStatOptions: GetStatsInput = {
user,
period,
search: query,
folderId,
};
let team: Awaited<ReturnType<typeof getTeamById>> | undefined;
if (teamId) {
team = await getTeamById({ userId: user.id, teamId });
getStatOptions.team = {
teamId: team.id,
teamEmail: team.teamEmail?.email,
senderIds,
currentTeamMemberRole: team.currentTeamRole,
currentUserEmail: user.email,
userId: user.id,
};
}
const [stats, documents] = await Promise.all([
getStats(getStatOptions),
getStats({
userId: user.id,
teamId,
period,
search: query,
folderId,
senderIds,
}),
findDocuments({
userId: user.id,
teamId,
team,
query,
templateId,
page,
@@ -1,10 +1,9 @@
import { DocumentSource } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse } from '@documenso/lib/types/search-params';
import { parseCommaSeparatedValues } from '@documenso/lib/utils/params';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { DocumentSource } from '@prisma/client';
import { z } from 'zod';
import { ZFindDocumentsRequestSchema } from './find-documents.types';
@@ -12,10 +11,7 @@ export const ZFindDocumentsInternalRequestSchema = ZFindDocumentsRequestSchema.e
period: z.enum(['all', '7d', '14d', '30d']).optional(),
senderIds: z.array(z.number()).optional(),
source: z.preprocess(parseCommaSeparatedValues, z.array(z.nativeEnum(DocumentSource)).optional()),
status: z.preprocess(
parseCommaSeparatedValues,
z.array(z.nativeEnum(ExtendedDocumentStatus)).optional(),
),
status: z.preprocess(parseCommaSeparatedValues, z.array(z.nativeEnum(ExtendedDocumentStatus)).optional()),
folderId: z.string().optional(),
});
@@ -2,11 +2,7 @@ import { findDocuments } from '@documenso/lib/server-only/document/find-document
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { authenticatedProcedure } from '../trpc';
import {
ZFindDocumentsMeta,
ZFindDocumentsRequestSchema,
ZFindDocumentsResponseSchema,
} from './find-documents.types';
import { ZFindDocumentsMeta, ZFindDocumentsRequestSchema, ZFindDocumentsResponseSchema } from './find-documents.types';
export const findDocumentsRoute = authenticatedProcedure
.meta(ZFindDocumentsMeta)
@@ -15,17 +11,7 @@ export const findDocumentsRoute = authenticatedProcedure
.query(async ({ input, ctx }) => {
const { user, teamId } = ctx;
const {
query,
templateId,
page,
perPage,
orderByDirection,
orderByColumn,
source,
status,
folderId,
} = input;
const { query, templateId, page, perPage, orderByDirection, orderByColumn, source, status, folderId } = input;
const documents = await findDocuments({
userId: user.id,
@@ -38,6 +24,7 @@ export const findDocumentsRoute = authenticatedProcedure
perPage,
folderId,
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
useWindowedCount: false,
});
return {
@@ -1,8 +1,7 @@
import { DocumentSource, DocumentStatus } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { DocumentSource, DocumentStatus } from '@prisma/client';
import { z } from 'zod';
import type { TrpcRouteMeta } from '../trpc';
@@ -17,18 +16,9 @@ export const ZFindDocumentsMeta: TrpcRouteMeta = {
};
export const ZFindDocumentsRequestSchema = ZFindSearchParamsSchema.extend({
templateId: z
.number()
.describe('Filter documents by the template ID used to create it.')
.optional(),
source: z
.nativeEnum(DocumentSource)
.describe('Filter documents by how it was created.')
.optional(),
status: z
.nativeEnum(DocumentStatus)
.describe('Filter documents by the current status')
.optional(),
templateId: z.number().describe('Filter documents by the template ID used to create it.').optional(),
source: z.nativeEnum(DocumentSource).describe('Filter documents by how it was created.').optional(),
status: z.nativeEnum(DocumentStatus).describe('Filter documents by the current status').optional(),
folderId: z.string().describe('Filter documents by folder ID').optional(),
orderByColumn: z.enum(['createdAt']).optional(),
orderByDirection: z.enum(['asc', 'desc']).describe('').default('desc'),
@@ -1,10 +1,9 @@
import type { Envelope, Prisma } from '@prisma/client';
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { maskRecipientTokensForDocument } from '@documenso/lib/utils/mask-recipient-tokens-for-document';
import { prisma } from '@documenso/prisma';
import type { Envelope, Prisma } from '@prisma/client';
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import { ZFindInboxRequestSchema, ZFindInboxResponseSchema } from './find-inbox.types';
@@ -1,8 +1,8 @@
// import type { OpenApiMeta } from 'trpc-to-openapi';
import type { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import type { z } from 'zod';
export const ZFindInboxRequestSchema = ZFindSearchParamsSchema;
@@ -1,13 +1,9 @@
import { EnvelopeType } from '@prisma/client';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
ZGetDocumentByTokenRequestSchema,
ZGetDocumentByTokenResponseSchema,
} from './get-document-by-token.types';
import { ZGetDocumentByTokenRequestSchema, ZGetDocumentByTokenResponseSchema } from './get-document-by-token.types';
export const getDocumentByTokenRoute = authenticatedProcedure
.input(ZGetDocumentByTokenRequestSchema)
@@ -1,6 +1,5 @@
import { z } from 'zod';
import DocumentDataSchema from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema';
import { z } from 'zod';
export const ZGetDocumentByTokenRequestSchema = z.object({
token: z.string().min(1),
@@ -1,11 +1,7 @@
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { authenticatedProcedure } from '../trpc';
import {
ZGetDocumentRequestSchema,
ZGetDocumentResponseSchema,
getDocumentMeta,
} from './get-document.types';
import { getDocumentMeta, ZGetDocumentRequestSchema, ZGetDocumentResponseSchema } from './get-document.types';
export const getDocumentRoute = authenticatedProcedure
.meta(getDocumentMeta)
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZDocumentSchema } from '@documenso/lib/types/document';
import { z } from 'zod';
import type { TrpcRouteMeta } from '../trpc';
@@ -1,14 +1,13 @@
import { EnvelopeType } from '@prisma/client';
import { getMultipleEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelopes-by-ids';
import { mapEnvelopesToDocumentMany } from '@documenso/lib/utils/document';
import { prisma } from '@documenso/prisma';
import { EnvelopeType } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import {
getDocumentsByIdsMeta,
ZGetDocumentsByIdsRequestSchema,
ZGetDocumentsByIdsResponseSchema,
getDocumentsByIdsMeta,
} from './get-documents-by-ids.types';
export const getDocumentsByIdsRoute = authenticatedProcedure
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { ZDocumentManySchema } from '@documenso/lib/types/document';
import { z } from 'zod';
import type { TrpcRouteMeta } from '../trpc';
@@ -1,6 +1,5 @@
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, EnvelopeType, RecipientRole } from '@prisma/client';
import { authenticatedProcedure } from '../trpc';
import { ZGetInboxCountRequestSchema, ZGetInboxCountResponseSchema } from './get-inbox-count.types';
@@ -3,9 +3,9 @@ import { resendDocument } from '@documenso/lib/server-only/document/resend-docum
import { ZGenericSuccessResponse } from '../schema';
import { authenticatedProcedure } from '../trpc';
import {
redistributeDocumentMeta,
ZRedistributeDocumentRequestSchema,
ZRedistributeDocumentResponseSchema,
redistributeDocumentMeta,
} from './redistribute-document.types';
export const redistributeDocumentRoute = authenticatedProcedure
@@ -16,10 +16,7 @@ export const redistributeDocumentMeta: TrpcRouteMeta = {
export const ZRedistributeDocumentRequestSchema = z.object({
documentId: z.number(),
recipients: z
.array(z.number())
.min(1)
.describe('The IDs of the recipients to redistribute the document to.'),
recipients: z.array(z.number()).min(1).describe('The IDs of the recipients to redistribute the document to.'),
});
export const ZRedistributeDocumentResponseSchema = ZSuccessResponseSchema;
@@ -1,19 +1,15 @@
import { DocumentVisibility } from '@prisma/client';
import { z } from 'zod';
export const DOCUMENT_TITLE_MAX_LENGTH = 255;
export const ZDocumentTitleSchema = z
.string()
.trim()
.min(1)
.max(255)
.max(DOCUMENT_TITLE_MAX_LENGTH)
.describe('The title of the document.');
export const ZDocumentExternalIdSchema = z
.string()
.trim()
.max(255)
.describe('The external ID of the document.');
export const ZDocumentExternalIdSchema = z.string().trim().max(255).describe('The external ID of the document.');
export const ZDocumentVisibilitySchema = z
.nativeEnum(DocumentVisibility)
.describe('The visibility of the document.');
export const ZDocumentVisibilitySchema = z.nativeEnum(DocumentVisibility).describe('The visibility of the document.');
@@ -1,10 +1,7 @@
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
import { authenticatedProcedure } from '../trpc';
import {
ZSearchDocumentRequestSchema,
ZSearchDocumentResponseSchema,
} from './search-document.types';
import { ZSearchDocumentRequestSchema, ZSearchDocumentResponseSchema } from './search-document.types';
export const searchDocumentRoute = authenticatedProcedure
.input(ZSearchDocumentRequestSchema)
@@ -1,7 +1,7 @@
import { z } from 'zod';
export const ZSearchDocumentRequestSchema = z.object({
query: z.string(),
query: z.string().trim().min(1).max(1024),
});
export const ZSearchDocumentResponseSchema = z
@@ -1,6 +1,5 @@
import { z } from 'zod';
import DocumentShareLinkSchema from '@documenso/prisma/generated/zod/modelSchema/DocumentShareLinkSchema';
import { z } from 'zod';
export const ZShareDocumentRequestSchema = z.object({
documentId: z.number(),
@@ -3,16 +3,16 @@ import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { authenticatedProcedure } from '../trpc';
import {
updateDocumentMeta,
ZUpdateDocumentRequestSchema,
ZUpdateDocumentResponseSchema,
} from './update-document.types';
import { updateDocumentMeta as updateDocumentTrpcMeta } from './update-document.types';
/**
* Public route.
*/
export const updateDocumentRoute = authenticatedProcedure
.meta(updateDocumentTrpcMeta)
.meta(updateDocumentMeta)
.input(ZUpdateDocumentRequestSchema)
.output(ZUpdateDocumentResponseSchema)
.mutation(async ({ input, ctx }) => {

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