Compare commits

..

3 Commits

Author SHA1 Message Date
Ephraim Atta-Duncan 4f822414cf fix: use khebab case 2024-12-11 15:55:35 +00:00
Ephraim Atta-Duncan 50a7b59371 fix: all variables with camel case not working 2024-12-11 15:51:34 +00:00
Ephraim Atta-Duncan fd40873210 fix: add unit to hsl colors in css conversion 2024-12-11 14:02:26 +00:00
92 changed files with 689 additions and 1716 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@documenso/marketing",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"private": true,
"license": "AGPL-3.0",
"scripts": {
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@documenso/web",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"private": true,
"license": "AGPL-3.0",
"scripts": {
@@ -56,7 +56,6 @@
"recharts": "^2.7.2",
"remeda": "^2.17.3",
"sharp": "0.32.6",
"trpc-openapi": "^1.2.0",
"ts-pattern": "^5.0.5",
"ua-parser-js": "^1.0.37",
"uqr": "^0.1.2",
@@ -40,7 +40,7 @@ export const AdminDocumentResults = () => {
const { data: findDocumentsData, isLoading: isFindDocumentsLoading } =
trpc.admin.findDocuments.useQuery(
{
query: debouncedTerm,
term: debouncedTerm,
page: page || 1,
perPage: perPage || 20,
},
@@ -134,7 +134,7 @@ export const LeaderboardTable = ({
startTransition(() => {
updateSearchParams({
sortBy: column,
sortOrder: sortBy === column && sortOrder === 'asc' ? 'desc' : 'asc',
sortOrder: sortOrder === 'asc' ? 'desc' : 'asc',
});
});
};
@@ -37,8 +37,10 @@ export const DocumentPageViewRecentActivity = ({
{
documentId,
filterForRecentActivity: true,
orderByColumn: 'createdAt',
orderByDirection: 'asc',
orderBy: {
column: 'createdAt',
direction: 'asc',
},
perPage: 10,
},
{
@@ -12,8 +12,8 @@ import {
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
SKIP_QUERY_BATCH_META,
} from '@documenso/lib/constants/trpc';
import type { TGetDocumentWithDetailsByIdResponse } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { DocumentDistributionMethod, DocumentStatus } from '@documenso/prisma/client';
import type { DocumentWithDetails } from '@documenso/prisma/types/document';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Card, CardContent } from '@documenso/ui/primitives/card';
@@ -35,7 +35,7 @@ import { useOptionalCurrentTeam } from '~/providers/team';
export type EditDocumentFormProps = {
className?: string;
initialDocument: TGetDocumentWithDetailsByIdResponse;
initialDocument: DocumentWithDetails;
documentRootPath: string;
isDocumentEnterprise: boolean;
};
@@ -103,7 +103,7 @@ export const EditDocumentForm = ({
const { mutateAsync: addFields } = trpc.field.addFields.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: ({ fields: newFields }) => {
onSuccess: (newFields) => {
utils.document.getDocumentWithDetailsById.setData(
{
documentId: initialDocument.id,
@@ -134,7 +134,7 @@ export const EditDocumentForm = ({
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: ({ recipients: newRecipients }) => {
onSuccess: (newRecipients) => {
utils.document.getDocumentWithDetailsById.setData(
{
documentId: initialDocument.id,
@@ -11,7 +11,7 @@ import type { DateTimeFormatOptions } from 'luxon';
import { UAParser } from 'ua-parser-js';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { formatDocumentAuditLogAction } from '@documenso/lib/utils/document-audit-logs';
import { trpc } from '@documenso/trpc/react';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
@@ -35,7 +35,9 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } =
trpc.document.findDocumentAuditLogs.useQuery(
@@ -9,8 +9,8 @@ import { DateTime } from 'luxon';
import { useSession } from 'next-auth/react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { TFindDocumentsResponse } from '@documenso/lib/server-only/document/find-documents';
import type { Team } from '@documenso/prisma/client';
import type { FindResultSet } from '@documenso/lib/types/find-result-set';
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
@@ -24,7 +24,13 @@ import { DataTableActionDropdown } from './data-table-action-dropdown';
import { DataTableTitle } from './data-table-title';
export type DocumentsDataTableProps = {
results: TFindDocumentsResponse;
results: FindResultSet<
Document & {
Recipient: Recipient[];
User: Pick<User, 'id' | 'name' | 'email'>;
team: Pick<Team, 'id' | 'url'> | null;
}
>;
showSenderColumn?: boolean;
team?: Pick<Team, 'id' | 'url'> & { teamEmail?: string };
};
@@ -83,7 +83,7 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
perPage,
period,
senderIds,
query: search,
search,
});
const getTabHref = (value: typeof status) => {
@@ -75,7 +75,7 @@ export const PublicProfilePageView = ({ user, team, profile }: PublicProfilePage
const enabledPrivateDirectTemplates = useMemo(
() =>
(data?.data ?? []).filter(
(data?.templates ?? []).filter(
(template): template is DirectTemplate =>
template.directLink?.enabled === true && template.type !== TemplateType.PUBLIC,
),
@@ -52,7 +52,7 @@ export const PublicTemplatesDataTable = () => {
);
const { directTemplates, publicDirectTemplates, privateDirectTemplates } = useMemo(() => {
const directTemplates = (data?.data ?? []).filter(
const directTemplates = (data?.templates ?? []).filter(
(template): template is DirectTemplate => template.directLink?.enabled === true,
);
@@ -12,7 +12,7 @@ import { UAParser } from 'ua-parser-js';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { USER_SECURITY_AUDIT_LOG_MAP } from '@documenso/lib/constants/auth';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { trpc } from '@documenso/trpc/react';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
@@ -33,7 +33,9 @@ export const UserSecurityActivityDataTable = () => {
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } =
trpc.profile.findUserSecurityAuditLogs.useQuery(
@@ -9,7 +9,7 @@ import { useLingui } from '@lingui/react';
import { DateTime } from 'luxon';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { trpc } from '@documenso/trpc/react';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
@@ -27,7 +27,9 @@ export const UserPasskeysDataTable = () => {
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.auth.findPasskeys.useQuery(
{
@@ -12,7 +12,7 @@ import { DateTime } from 'luxon';
import { z } from 'zod';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import type { Team } from '@documenso/prisma/client';
import { DocumentSource, DocumentStatus as DocumentStatusEnum } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
@@ -40,7 +40,7 @@ const DOCUMENT_SOURCE_LABELS: { [key in DocumentSource]: MessageDescriptor } = {
TEMPLATE_DIRECT_LINK: msg`Direct link`,
};
const ZDocumentSearchParamsSchema = ZUrlSearchParamsSchema.extend({
const ZTemplateSearchParamsSchema = ZBaseTableSearchParamsSchema.extend({
source: z
.nativeEnum(DocumentSource)
.optional()
@@ -49,6 +49,10 @@ const ZDocumentSearchParamsSchema = ZUrlSearchParamsSchema.extend({
.nativeEnum(DocumentStatusEnum)
.optional()
.catch(() => undefined),
search: z.coerce
.string()
.optional()
.catch(() => undefined),
});
type TemplatePageViewDocumentsTableProps = {
@@ -65,7 +69,7 @@ export const TemplatePageViewDocumentsTable = ({
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZDocumentSearchParamsSchema.parse(
const parsedSearchParams = ZTemplateSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
@@ -76,7 +80,7 @@ export const TemplatePageViewDocumentsTable = ({
teamId: team?.id,
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,
query: parsedSearchParams.query,
search: parsedSearchParams.search,
source: parsedSearchParams.source,
status: parsedSearchParams.status,
},
@@ -26,8 +26,10 @@ export const TemplatePageViewRecentActivity = ({
const { data, isLoading, isLoadingError, refetch } = trpc.document.findDocuments.useQuery({
templateId,
teamId,
orderByColumn: 'createdAt',
orderByDirection: 'asc',
orderBy: {
column: 'createdAt',
direction: 'asc',
},
perPage: 5,
});
@@ -4,10 +4,8 @@ import { useRouter } from 'next/navigation';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { match } from 'ts-pattern';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { trpc } from '@documenso/trpc/react';
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import { Button } from '@documenso/ui/primitives/button';
@@ -53,20 +51,10 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
});
onOpenChange(false);
},
onError: (err) => {
const error = AppError.parseError(err);
const errorMessage = match(error.code)
.with(
AppErrorCode.NOT_FOUND,
() => msg`Template not found or already associated with a team.`,
)
.with(AppErrorCode.UNAUTHORIZED, () => msg`You are not a member of this team.`)
.otherwise(() => msg`An error occurred while moving the template.`);
onError: (error) => {
toast({
title: _(msg`Error`),
description: _(errorMessage),
description: error.message || _(msg`An error occurred while moving the template.`),
variant: 'destructive',
duration: 7500,
});
@@ -29,7 +29,7 @@ export const TemplatesPageView = async ({ searchParams = {}, team }: TemplatesPa
const documentRootPath = formatDocumentsPath(team?.url);
const templateRootPath = formatTemplatesPath(team?.url);
const { data: templates, totalPages } = await findTemplates({
const { templates, totalPages } = await findTemplates({
userId: user.id,
teamId: team?.id,
page: page,
@@ -11,7 +11,7 @@ import { useLingui } from '@lingui/react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { NEXT_PUBLIC_WEBAPP_URL, WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
@@ -30,11 +30,13 @@ export const CurrentUserTeamsDataTable = () => {
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeams.useQuery(
{
query: parsedSearchParams.query,
term: parsedSearchParams.query,
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,
},
@@ -9,7 +9,7 @@ import { useLingui } from '@lingui/react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { trpc } from '@documenso/trpc/react';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
@@ -27,13 +27,15 @@ export const PendingUserTeamsDataTable = () => {
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const [checkoutPendingTeamId, setCheckoutPendingTeamId] = useState<number | null>(null);
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeamsPending.useQuery(
{
query: parsedSearchParams.query,
term: parsedSearchParams.query,
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,
},
@@ -10,7 +10,7 @@ import { History, MoreHorizontal, Trash2 } from 'lucide-react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { trpc } from '@documenso/trpc/react';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
@@ -38,13 +38,15 @@ export const TeamMemberInvitesDataTable = ({ teamId }: TeamMemberInvitesDataTabl
const { _, i18n } = useLingui();
const { toast } = useToast();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } =
trpc.team.findTeamMemberInvites.useQuery(
{
teamId,
query: parsedSearchParams.query,
term: parsedSearchParams.query,
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,
},
@@ -10,7 +10,7 @@ import { Edit, MoreHorizontal, Trash2 } from 'lucide-react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams';
import { ZUrlSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import { isTeamRoleWithinUserHierarchy } from '@documenso/lib/utils/teams';
import type { TeamMemberRole } from '@documenso/prisma/client';
@@ -50,12 +50,14 @@ export const TeamMembersDataTable = ({
const searchParams = useSearchParams();
const updateSearchParams = useUpdateSearchParams();
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const parsedSearchParams = ZBaseTableSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeamMembers.useQuery(
{
teamId,
query: parsedSearchParams.query,
term: parsedSearchParams.query,
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,
},
-47
View File
@@ -1,47 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createOpenApiNextHandler } from 'trpc-openapi';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { buildLogger } from '@documenso/lib/utils/logger';
import type { TRPCError } from '@documenso/trpc/server';
import { createTrpcContext } from '@documenso/trpc/server/context';
import { appRouter } from '@documenso/trpc/server/router';
const logger = buildLogger();
export default createOpenApiNextHandler<typeof appRouter>({
router: appRouter,
createContext: async ({ req, res }: { req: NextApiRequest; res: NextApiResponse }) =>
createTrpcContext({ req, res }),
onError: ({ error, path }: { error: TRPCError; path?: string }) => {
// Always log the error for now.
console.error(error.message);
const appError = AppError.parseError(error.cause || error);
const isAppError = error.cause instanceof AppError;
// Only log AppErrors that are explicitly set to 500 or the error code
// is in the errorCodesToAlertOn list.
const isLoggableAppError =
isAppError && (appError.statusCode === 500 || errorCodesToAlertOn.includes(appError.code));
// Only log TRPC errors that are in the `errorCodesToAlertOn` list and is
// not an AppError.
const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code);
if (isLoggableAppError || isLoggableTrpcError) {
logger.error(error, {
method: path,
context: {
source: '/v2/api',
appError: AppError.toJSON(appError),
},
});
}
},
responseMeta: () => {},
});
const errorCodesToAlertOn = [AppErrorCode.UNKNOWN_ERROR, 'INTERNAL_SERVER_ERROR'];
@@ -1,9 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { openApiDocument } from '@documenso/trpc/server/open-api';
const handler = (_req: NextApiRequest, res: NextApiResponse) => {
res.status(200).send(openApiDocument);
};
export default handler;
-1
View File
@@ -45,7 +45,6 @@ export default trpcNext.createNextApiHandler({
logger.error(error, {
method: path,
context: {
source: 'trpc',
appError: AppError.toJSON(appError),
},
});
+19 -303
View File
@@ -1,12 +1,12 @@
{
"name": "@documenso/root",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@documenso/root",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"workspaces": [
"apps/*",
"packages/*"
@@ -79,7 +79,7 @@
},
"apps/marketing": {
"name": "@documenso/marketing",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"license": "AGPL-3.0",
"dependencies": {
"@documenso/assets": "*",
@@ -492,7 +492,7 @@
},
"apps/web": {
"name": "@documenso/web",
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"license": "AGPL-3.0",
"dependencies": {
"@documenso/api": "*",
@@ -536,7 +536,6 @@
"recharts": "^2.7.2",
"remeda": "^2.17.3",
"sharp": "0.32.6",
"trpc-openapi": "^1.2.0",
"ts-pattern": "^5.0.5",
"ua-parser-js": "^1.0.37",
"uqr": "^0.1.2",
@@ -3495,11 +3494,6 @@
"node": ">=6"
}
},
"node_modules/@hapi/bourne": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz",
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
@@ -12013,18 +12007,6 @@
"node": ">=6.5"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@@ -14329,21 +14311,6 @@
}
}
},
"node_modules/co-body": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz",
"integrity": "sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==",
"dependencies": {
"@hapi/bourne": "^3.0.0",
"inflation": "^2.0.0",
"qs": "^6.5.2",
"raw-body": "^2.3.3",
"type-is": "^1.6.16"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/code-block-writer": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
@@ -14645,14 +14612,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/consola": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz",
"integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==",
"engines": {
"node": "^14.18.0 || >=16.10.0"
}
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -14668,17 +14627,6 @@
"simple-wcswidth": "^1.0.1"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
@@ -14764,9 +14712,9 @@
}
},
"node_modules/cookie-es": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz",
"integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ=="
},
"node_modules/copy-anything": {
"version": "3.0.5",
@@ -14932,14 +14880,6 @@
"node": ">= 8"
}
},
"node_modules/crossws": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.1.tgz",
"integrity": "sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw==",
"dependencies": {
"uncrypto": "^0.1.3"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
@@ -15647,11 +15587,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/defu": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
@@ -15758,11 +15693,6 @@
"node": ">=6"
}
},
"node_modules/destr": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz",
"integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ=="
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@@ -18193,14 +18123,6 @@
}
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/from": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
@@ -18871,23 +18793,6 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/h3": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.13.0.tgz",
"integrity": "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==",
"dependencies": {
"cookie-es": "^1.2.2",
"crossws": ">=0.2.0 <0.4.0",
"defu": "^6.1.4",
"destr": "^2.0.3",
"iron-webcrypto": "^1.2.1",
"ohash": "^1.1.4",
"radix3": "^1.1.2",
"ufo": "^1.5.4",
"uncrypto": "^0.1.3",
"unenv": "^1.10.0"
}
},
"node_modules/hard-rejection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
@@ -19939,14 +19844,6 @@
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"dev": true
},
"node_modules/inflation": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz",
"integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/inflection": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/inflection/-/inflection-2.0.1.tgz",
@@ -20322,14 +20219,6 @@
"node": ">= 10"
}
},
"node_modules/iron-webcrypto": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
"integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
"funding": {
"url": "https://github.com/sponsors/brc-dd"
}
},
"node_modules/is-alphabetical": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -22146,7 +22035,8 @@
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
"dev": true
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
@@ -22973,14 +22863,6 @@
"esbuild": "0.*"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
@@ -23035,14 +22917,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-refs": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-1.2.2.tgz",
@@ -23116,14 +22990,6 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/micro": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/micro/-/micro-10.0.1.tgz",
@@ -23867,17 +23733,6 @@
"node": ">=8.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -24341,6 +24196,7 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@@ -24809,11 +24665,6 @@
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-fetch-native": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz",
"integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ=="
},
"node_modules/node-gyp": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
@@ -25142,38 +24993,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/node-mocks-http": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.16.2.tgz",
"integrity": "sha512-2Sh6YItRp1oqewZNlck3LaFp5vbyW2u51HX2p1VLxQ9U/bG90XV8JY9O7Nk+HDd6OOn/oV3nA5Tx5k4Rki0qlg==",
"dependencies": {
"accepts": "^1.3.7",
"content-disposition": "^0.5.3",
"depd": "^1.1.0",
"fresh": "^0.5.2",
"merge-descriptors": "^1.0.1",
"methods": "^1.1.2",
"mime": "^1.3.4",
"parseurl": "^1.3.3",
"range-parser": "^1.2.0",
"type-is": "^1.6.18"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@types/express": "^4.17.21 || ^5.0.0",
"@types/node": "*"
},
"peerDependenciesMeta": {
"@types/express": {
"optional": true
},
"@types/node": {
"optional": true
}
}
},
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -25761,11 +25580,6 @@
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
"dev": true
},
"node_modules/ohash": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
"integrity": "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="
},
"node_modules/oidc-token-hash": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
@@ -25867,11 +25681,6 @@
}
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="
},
"node_modules/openapi3-ts": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-2.0.2.tgz",
@@ -26631,14 +26440,6 @@
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/partysocket": {
"version": "0.0.17",
"resolved": "https://registry.npmjs.org/partysocket/-/partysocket-0.0.17.tgz",
@@ -26794,7 +26595,8 @@
"node_modules/pathe": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"dev": true
},
"node_modules/pathval": {
"version": "1.1.1",
@@ -28106,11 +27908,6 @@
"node": ">=8"
}
},
"node_modules/radix3": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="
},
"node_modules/raf-schd": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
@@ -28149,14 +27946,6 @@
"safe-buffer": "^5.1.0"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
@@ -32993,32 +32782,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/trpc-openapi": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/trpc-openapi/-/trpc-openapi-1.2.0.tgz",
"integrity": "sha512-pfYoCd/3KYXWXvUPZBKJw455OOwngKN/6SIcj7Yit19OMLJ+8yVZkEvGEeg5wUSwfsiTdRsKuvqkRPXVSwV7ew==",
"workspaces": [
".",
"examples/with-nextjs",
"examples/with-express",
"examples/with-interop",
"examples/with-serverless",
"examples/with-fastify",
"examples/with-nuxtjs"
],
"dependencies": {
"co-body": "^6.1.0",
"h3": "^1.6.4",
"lodash.clonedeep": "^4.5.0",
"node-mocks-http": "^1.12.2",
"openapi-types": "^12.1.1",
"zod-to-json-schema": "^3.21.1"
},
"peerDependencies": {
"@trpc/server": "^10.0.0",
"zod": "^3.14.4"
}
},
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@@ -33784,18 +33547,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/typed-array-buffer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
@@ -33914,9 +33665,10 @@
}
},
"node_modules/ufo": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz",
"integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==",
"dev": true
},
"node_modules/ulid": {
"version": "2.3.0",
@@ -33951,11 +33703,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/uncrypto": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="
},
"node_modules/undici": {
"version": "5.28.2",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz",
@@ -33973,29 +33720,6 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unenv": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz",
"integrity": "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==",
"dependencies": {
"consola": "^3.2.3",
"defu": "^6.1.4",
"mime": "^3.0.0",
"node-fetch-native": "^1.6.4",
"pathe": "^1.1.2"
}
},
"node_modules/unenv/node_modules/mime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/unified": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
@@ -35685,9 +35409,9 @@
}
},
"node_modules/zod": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@@ -35730,14 +35454,6 @@
"@prisma/debug": "5.22.0"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz",
"integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==",
"peerDependencies": {
"zod": "^3.24.1"
}
},
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "1.9.0-rc.3",
"version": "1.9.0-rc.0",
"scripts": {
"build": "turbo run build",
"build:web": "turbo run build --filter=@documenso/web",
+63 -46
View File
@@ -29,13 +29,12 @@ import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/s
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
import { createTeamMemberInvites } from '@documenso/lib/server-only/team/create-team-member-invites';
import { deleteTeamMembers } from '@documenso/lib/server-only/team/delete-team-members';
import type { TCreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
import type { CreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/template/create-document-from-template-legacy';
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { extractDerivedDocumentEmailSettings } from '@documenso/lib/types/document-email';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import {
ZCheckboxFieldMeta,
@@ -345,7 +344,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
});
}
const { recipients } = await setRecipientsForDocument({
const recipients = await setRecipientsForDocument({
userId: user.id,
teamId: team?.id,
documentId: document.id,
@@ -428,7 +427,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
const perPage = Number(args.query.perPage) || 10;
try {
const { data: templates, totalPages } = await findTemplates({
const { templates, totalPages } = await findTemplates({
page,
perPage,
userId: user.id,
@@ -560,7 +559,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
const templateId = Number(params.templateId);
let document: TCreateDocumentFromTemplateResponse | null = null;
let document: CreateDocumentFromTemplateResponse | null = null;
try {
document = await createDocumentFromTemplate({
@@ -630,6 +629,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
token: recipient.token,
role: recipient.role,
signingOrder: recipient.signingOrder,
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
})),
},
@@ -637,52 +637,69 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
}),
sendDocument: authenticatedMiddleware(async (args, user, team) => {
const { id: documentId } = args.params;
const { sendEmail, sendCompletionEmails } = args.body;
const { id } = args.params;
const { sendEmail = true } = args.body ?? {};
const document = await getDocumentById({
documentId: Number(id),
userId: user.id,
teamId: team?.id,
});
if (!document) {
return {
status: 404,
body: {
message: 'Document not found',
},
};
}
if (document.status === DocumentStatus.COMPLETED) {
return {
status: 400,
body: {
message: 'Document is already complete',
},
};
}
try {
const document = await getDocumentById({
documentId: Number(documentId),
userId: user.id,
teamId: team?.id,
});
// await setRecipientsForDocument({
// userId: user.id,
// documentId: Number(id),
// recipients: [
// {
// email: body.signerEmail,
// name: body.signerName ?? '',
// },
// ],
// });
if (!document) {
return {
status: 404,
body: {
message: 'Document not found',
},
};
}
// await setFieldsForDocument({
// documentId: Number(id),
// userId: user.id,
// fields: body.fields.map((field) => ({
// signerEmail: body.signerEmail,
// type: field.fieldType,
// pageNumber: field.pageNumber,
// pageX: field.pageX,
// pageY: field.pageY,
// pageWidth: field.pageWidth,
// pageHeight: field.pageHeight,
// })),
// });
if (document.status === DocumentStatus.COMPLETED) {
return {
status: 400,
body: {
message: 'Document is already complete',
},
};
}
const emailSettings = extractDerivedDocumentEmailSettings(document.documentMeta);
// Update document email settings if sendCompletionEmails is provided
if (typeof sendCompletionEmails === 'boolean') {
await upsertDocumentMeta({
documentId: document.id,
userId: user.id,
emailSettings: {
...emailSettings,
documentCompleted: sendCompletionEmails,
ownerDocumentCompleted: sendCompletionEmails,
},
requestMetadata: extractNextApiRequestMetadata(args.req),
});
}
// if (body.emailBody || body.emailSubject) {
// await upsertDocumentMeta({
// documentId: Number(id),
// subject: body.emailSubject ?? '',
// message: body.emailBody ?? '',
// });
// }
const { Recipient: recipients, ...sentDocument } = await sendDocument({
documentId: document.id,
documentId: Number(id),
userId: user.id,
teamId: team?.id,
sendEmail,
@@ -785,7 +802,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
}
try {
const { recipients: newRecipients } = await setRecipientsForDocument({
const newRecipients = await setRecipientsForDocument({
documentId: Number(documentId),
userId: user.id,
teamId: team?.id,
+1 -5
View File
@@ -88,12 +88,8 @@ export const ZSendDocumentForSigningMutationSchema = z
description:
'Whether to send an email to the recipients asking them to action the document. If you disable this, you will need to manually distribute the document to the recipients using the generated signing links.',
}),
sendCompletionEmails: z.boolean().optional().openapi({
description:
'Whether to send completion emails when the document is fully signed. This will override the document email settings.',
}),
})
.or(z.literal('').transform(() => ({ sendEmail: true, sendCompletionEmails: undefined })));
.or(z.literal('').transform(() => ({ sendEmail: true })));
export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema;
@@ -1,137 +0,0 @@
import { expect, test } from '@playwright/test';
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { prisma } from '@documenso/prisma';
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
test.describe('Document API', () => {
test('sendDocument: should respect sendCompletionEmails setting', async ({ request }) => {
const user = await seedUser();
const { document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['signer@example.com'],
});
const { token } = await createApiToken({
userId: user.id,
tokenName: 'test',
expiresIn: null,
});
// Test with sendCompletionEmails: false
const response = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${document.id}/send`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
data: {
sendCompletionEmails: false,
},
});
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
// Verify email settings were updated
const updatedDocument = await prisma.document.findUnique({
where: { id: document.id },
include: { documentMeta: true },
});
expect(updatedDocument?.documentMeta?.emailSettings).toMatchObject({
documentCompleted: false,
ownerDocumentCompleted: false,
});
// Test with sendCompletionEmails: true
const response2 = await request.post(
`${WEBAPP_BASE_URL}/api/v1/documents/${document.id}/send`,
{
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
data: {
sendCompletionEmails: true,
},
},
);
expect(response2.ok()).toBeTruthy();
expect(response2.status()).toBe(200);
// Verify email settings were updated
const updatedDocument2 = await prisma.document.findUnique({
where: { id: document.id },
include: { documentMeta: true },
});
expect(updatedDocument2?.documentMeta?.emailSettings ?? {}).toMatchObject({
documentCompleted: true,
ownerDocumentCompleted: true,
});
});
test('sendDocument: should not modify email settings when sendCompletionEmails is not provided', async ({
request,
}) => {
const user = await seedUser();
const { document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: ['signer@example.com'],
});
// Set initial email settings
await prisma.documentMeta.upsert({
where: { documentId: document.id },
create: {
documentId: document.id,
emailSettings: {
documentCompleted: true,
ownerDocumentCompleted: false,
},
},
update: {
documentId: document.id,
emailSettings: {
documentCompleted: true,
ownerDocumentCompleted: false,
},
},
});
const { token } = await createApiToken({
userId: user.id,
tokenName: 'test',
expiresIn: null,
});
const response = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${document.id}/send`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
data: {
sendEmail: true,
},
});
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
// Verify email settings were not modified
const updatedDocument = await prisma.document.findUnique({
where: { id: document.id },
include: { documentMeta: true },
});
expect(updatedDocument?.documentMeta?.emailSettings ?? {}).toMatchObject({
documentCompleted: true,
ownerDocumentCompleted: false,
});
});
});
@@ -1,10 +1,8 @@
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
import { subscriptionsContainActiveEnterprisePlan } from '@documenso/lib/utils/billing';
import { prisma } from '@documenso/prisma';
import type { Subscription } from '@documenso/prisma/client';
import { getEnterprisePlanPriceIds } from '../stripe/get-enterprise-plan-prices';
export type IsUserEnterpriseOptions = {
userId: number;
teamId?: number;
@@ -54,11 +52,5 @@ export const isUserEnterprise = async ({
.then((user) => user.Subscription);
}
if (subscriptions.length === 0) {
return false;
}
const enterprisePlanPriceIds = await getEnterprisePlanPriceIds();
return subscriptionsContainsActivePlan(subscriptions, enterprisePlanPriceIds, true);
return subscriptionsContainActiveEnterprisePlan(subscriptions);
};
-7
View File
@@ -46,10 +46,3 @@ export const PASSKEY_TIMEOUT = 60000;
* The maximum number of passkeys are user can have.
*/
export const MAXIMUM_PASSKEYS = 50;
export const useSecureCookies =
process.env.NODE_ENV === 'production' && String(process.env.NEXTAUTH_URL).startsWith('https://');
const secureCookiePrefix = useSecureCookies ? '__Secure-' : '';
export const formatSecureCookieName = (name: string) => `${secureCookiePrefix}${name}`;
+9 -6
View File
@@ -13,7 +13,6 @@ import { env } from 'next-runtime-env';
import { prisma } from '@documenso/prisma';
import { IdentityProvider, UserSecurityAuditLogType } from '@documenso/prisma/client';
import { formatSecureCookieName, useSecureCookies } from '../constants/auth';
import { AppError, AppErrorCode } from '../errors/app-error';
import { jobsClient } from '../jobs/client';
import { isTwoFactorAuthenticationEnabled } from '../server-only/2fa/is-2fa-availble';
@@ -27,6 +26,10 @@ import { extractNextAuthRequestMetadata } from '../universal/extract-request-met
import { getAuthenticatorOptions } from '../utils/authenticator';
import { ErrorCode } from './error-codes';
const useSecureCookies =
process.env.NODE_ENV === 'production' && String(process.env.NEXTAUTH_URL).startsWith('https://');
const cookiePrefix = useSecureCookies ? '__Secure-' : '';
export const NEXT_AUTH_OPTIONS: AuthOptions = {
adapter: PrismaAdapter(prisma),
secret: process.env.NEXTAUTH_SECRET ?? 'secret',
@@ -434,7 +437,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
},
cookies: {
sessionToken: {
name: formatSecureCookieName('next-auth.session-token'),
name: `${cookiePrefix}next-auth.session-token`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
@@ -443,7 +446,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
},
},
callbackUrl: {
name: formatSecureCookieName('next-auth.callback-url'),
name: `${cookiePrefix}next-auth.callback-url`,
options: {
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
@@ -453,7 +456,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
csrfToken: {
// Default to __Host- for CSRF token for additional protection if using useSecureCookies
// NB: The `__Host-` prefix is stricter than the `__Secure-` prefix.
name: formatSecureCookieName('next-auth.csrf-token'),
name: `${cookiePrefix}next-auth.csrf-token`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
@@ -462,7 +465,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
},
},
pkceCodeVerifier: {
name: formatSecureCookieName('next-auth.pkce.code_verifier'),
name: `${cookiePrefix}next-auth.pkce.code_verifier`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
@@ -471,7 +474,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
},
},
state: {
name: formatSecureCookieName('next-auth.state'),
name: `${cookiePrefix}next-auth.state`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
@@ -1,20 +1,18 @@
import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export interface FindDocumentsOptions {
query?: string;
term?: string;
page?: number;
perPage?: number;
}
export const findDocuments = async ({ query, page = 1, perPage = 10 }: FindDocumentsOptions) => {
const termFilters: Prisma.DocumentWhereInput | undefined = !query
export const findDocuments = async ({ term, page = 1, perPage = 10 }: FindDocumentsOptions) => {
const termFilters: Prisma.DocumentWhereInput | undefined = !term
? undefined
: {
title: {
contains: query,
contains: term,
mode: 'insensitive',
},
};
@@ -53,5 +51,5 @@ export const findDocuments = async ({ query, page = 1, perPage = 10 }: FindDocum
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
};
};
@@ -1,5 +1,5 @@
import { prisma } from '@documenso/prisma';
import { DocumentStatus, Prisma } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
export type SigningVolume = {
id: number;
@@ -43,41 +43,34 @@ export async function getSigningVolume({
],
});
const orderByClause = getOrderByClause({ sortBy, sortOrder });
const [subscriptions, totalCount] = await Promise.all([
prisma.subscription.findMany({
where: whereClause,
include: {
User: {
select: {
name: true,
email: true,
include: {
Document: {
where: {
status: DocumentStatus.COMPLETED,
status: 'COMPLETED',
deletedAt: null,
teamId: null,
},
},
},
},
team: {
select: {
name: true,
include: {
document: {
where: {
status: DocumentStatus.COMPLETED,
status: 'COMPLETED',
deletedAt: null,
},
},
},
},
},
orderBy:
sortBy === 'name'
? [{ User: { name: sortOrder } }, { team: { name: sortOrder } }, { createdAt: 'desc' }]
: sortBy === 'createdAt'
? [{ createdAt: sortOrder }]
: undefined,
orderBy: orderByClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
}),
@@ -89,8 +82,10 @@ export async function getSigningVolume({
const leaderboardWithVolume: SigningVolume[] = subscriptions.map((subscription) => {
const name =
subscription.User?.name || subscription.team?.name || subscription.User?.email || 'Unknown';
const userSignedDocs = subscription.User?.Document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;
return {
id: subscription.id,
name,
@@ -100,16 +95,54 @@ export async function getSigningVolume({
};
});
if (sortBy === 'signingVolume') {
leaderboardWithVolume.sort((a, b) => {
return sortOrder === 'desc'
? b.signingVolume - a.signingVolume
: a.signingVolume - b.signingVolume;
});
}
return {
leaderboard: leaderboardWithVolume,
totalPages: Math.ceil(totalCount / perPage),
};
}
function getOrderByClause(options: {
sortBy: string;
sortOrder: 'asc' | 'desc';
}): Prisma.SubscriptionOrderByWithRelationInput | Prisma.SubscriptionOrderByWithRelationInput[] {
const { sortBy, sortOrder } = options;
if (sortBy === 'name') {
return [
{
User: {
name: sortOrder,
},
},
{
team: {
name: sortOrder,
},
},
];
}
if (sortBy === 'createdAt') {
return {
createdAt: sortOrder,
};
}
// Default: sort by signing volume
return [
{
User: {
Document: {
_count: sortOrder,
},
},
},
{
team: {
document: {
_count: sortOrder,
},
},
},
];
}
@@ -1,12 +1,11 @@
import type { FindResultSet } from '@documenso/lib/types/find-result-set';
import { prisma } from '@documenso/prisma';
import type { Passkey } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export interface FindPasskeysOptions {
userId: number;
query?: string;
term?: string;
page?: number;
perPage?: number;
orderBy?: {
@@ -18,7 +17,7 @@ export interface FindPasskeysOptions {
export const findPasskeys = async ({
userId,
query = '',
term = '',
page = 1,
perPage = 10,
orderBy,
@@ -31,9 +30,9 @@ export const findPasskeys = async ({
userId,
};
if (query.length > 0) {
if (term.length > 0) {
whereClause.name = {
contains: query,
contains: term,
mode: Prisma.QueryMode.insensitive,
};
}
@@ -73,5 +72,5 @@ export const findPasskeys = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
@@ -1,7 +1,5 @@
'use server';
import type { z } from 'zod';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@@ -10,7 +8,6 @@ import { prisma } from '@documenso/prisma';
import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@documenso/prisma/client';
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import { DocumentSchema } from '@documenso/prisma/generated/zod';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@@ -25,10 +22,6 @@ export type CreateDocumentOptions = {
requestMetadata?: RequestMetadata;
};
export const ZCreateDocumentResponseSchema = DocumentSchema;
export type TCreateDocumentResponse = z.infer<typeof ZCreateDocumentResponseSchema>;
export const createDocument = async ({
userId,
title,
@@ -37,7 +30,7 @@ export const createDocument = async ({
teamId,
formValues,
requestMetadata,
}: CreateDocumentOptions): Promise<TCreateDocumentResponse> => {
}: CreateDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
@@ -1,27 +1,19 @@
import { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { DocumentSource, type Prisma } from '@documenso/prisma/client';
import { getDocumentWhereInput } from './get-document-by-id';
export interface DuplicateDocumentOptions {
export interface DuplicateDocumentByIdOptions {
documentId: number;
userId: number;
teamId?: number;
}
export const ZDuplicateDocumentResponseSchema = z.object({
documentId: z.number(),
});
export type TDuplicateDocumentResponse = z.infer<typeof ZDuplicateDocumentResponseSchema>;
export const duplicateDocument = async ({
export const duplicateDocumentById = async ({
documentId,
userId,
teamId,
}: DuplicateDocumentOptions): Promise<TDuplicateDocumentResponse> => {
}: DuplicateDocumentByIdOptions) => {
const documentWhereInput = await getDocumentWhereInput({
documentId,
userId,
@@ -86,7 +78,5 @@ export const duplicateDocument = async ({
const createdDocument = await prisma.document.create(createDocumentArguments);
return {
documentId: createdDocument.id,
};
return createdDocument.id;
};
@@ -1,9 +1,9 @@
import type { FindResultSet } from '@documenso/lib/types/find-result-set';
import { prisma } from '@documenso/prisma';
import type { DocumentAuditLog, Prisma } from '@documenso/prisma/client';
import type { DocumentAuditLog } from '@documenso/prisma/client';
import type { Prisma } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { FindResultResponse } from '../../types/search-params';
import { parseDocumentAuditLogData } from '../../utils/document-audit-logs';
export interface FindDocumentAuditLogsOptions {
@@ -31,7 +31,7 @@ export const findDocumentAuditLogs = async ({
const orderByColumn = orderBy?.column ?? 'createdAt';
const orderByDirection = orderBy?.direction ?? 'desc';
const document = await prisma.document.findFirst({
const documentFilter = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
OR: [
@@ -51,10 +51,6 @@ export const findDocumentAuditLogs = async ({
},
});
if (!document) {
throw new AppError(AppErrorCode.NOT_FOUND);
}
const whereClause: Prisma.DocumentAuditLogWhereInput = {
documentId,
};
@@ -117,5 +113,5 @@ export const findDocumentAuditLogs = async ({
perPage,
totalPages: Math.ceil(count / perPage),
nextCursor,
} satisfies FindResultResponse<typeof parsedData> & { nextCursor?: string };
} satisfies FindResultSet<typeof parsedData> & { nextCursor?: string };
};
@@ -1,8 +1,8 @@
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
import type { z } from 'zod';
import { P, match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
import type {
Document,
DocumentSource,
@@ -11,17 +11,10 @@ import type {
TeamEmail,
User,
} from '@documenso/prisma/client';
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
import {
DocumentSchema,
RecipientSchema,
TeamSchema,
UserSchema,
} from '@documenso/prisma/generated/zod';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { DocumentVisibility } from '../../types/document-visibility';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
import type { FindResultSet } from '../../types/find-result-set';
import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-tokens-for-document';
export type PeriodSelectorValue = '' | '7d' | '14d' | '30d';
@@ -29,6 +22,7 @@ export type PeriodSelectorValue = '' | '7d' | '14d' | '30d';
export type FindDocumentsOptions = {
userId: number;
teamId?: number;
term?: string;
templateId?: number;
source?: DocumentSource;
status?: ExtendedDocumentStatus;
@@ -40,29 +34,13 @@ export type FindDocumentsOptions = {
};
period?: PeriodSelectorValue;
senderIds?: number[];
query?: string;
search?: string;
};
export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
data: DocumentSchema.extend({
User: UserSchema.pick({
id: true,
name: true,
email: true,
}),
Recipient: RecipientSchema.array(),
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
}).array(), // Todo: openapi remap.
});
export type TFindDocumentsResponse = z.infer<typeof ZFindDocumentsResponseSchema>;
export const findDocuments = async ({
userId,
teamId,
term,
templateId,
source,
status = ExtendedDocumentStatus.ALL,
@@ -71,8 +49,8 @@ export const findDocuments = async ({
orderBy,
period,
senderIds,
query,
}: FindDocumentsOptions): Promise<TFindDocumentsResponse> => {
search,
}: FindDocumentsOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
@@ -109,11 +87,22 @@ export const findDocuments = async ({
const orderByDirection = orderBy?.direction ?? 'desc';
const teamMemberRole = team?.members[0].role ?? null;
const termFilters = match(term)
.with(P.string.minLength(1), () => {
return {
title: {
contains: term,
mode: 'insensitive',
},
} as const;
})
.otherwise(() => undefined);
const searchFilter: Prisma.DocumentWhereInput = {
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: query, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: query, mode: 'insensitive' } } } },
{ title: { contains: search, mode: 'insensitive' } },
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
],
};
@@ -220,6 +209,7 @@ export const findDocuments = async ({
}
const whereAndClause: Prisma.DocumentWhereInput['AND'] = [
{ ...termFilters },
{ ...filters },
{ ...deletedFilter },
{ ...searchFilter },
@@ -300,7 +290,7 @@ export const findDocuments = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
@@ -1,15 +1,6 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import {
DocumentDataSchema,
DocumentMetaSchema,
DocumentSchema,
FieldSchema,
RecipientSchema,
} from '@documenso/prisma/generated/zod';
import type { DocumentWithDetails } from '@documenso/prisma/types/document';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { getDocumentWhereInput } from './get-document-by-id';
export type GetDocumentWithDetailsByIdOptions = {
@@ -18,29 +9,18 @@ export type GetDocumentWithDetailsByIdOptions = {
teamId?: number;
};
export const ZGetDocumentWithDetailsByIdResponseSchema = DocumentSchema.extend({
documentData: DocumentDataSchema,
documentMeta: DocumentMetaSchema.nullable(),
Recipient: RecipientSchema.array(),
Field: FieldSchema.array(),
});
export type TGetDocumentWithDetailsByIdResponse = z.infer<
typeof ZGetDocumentWithDetailsByIdResponseSchema
>;
export const getDocumentWithDetailsById = async ({
documentId,
userId,
teamId,
}: GetDocumentWithDetailsByIdOptions): Promise<TGetDocumentWithDetailsByIdResponse> => {
}: GetDocumentWithDetailsByIdOptions): Promise<DocumentWithDetails> => {
const documentWhereInput = await getDocumentWhereInput({
documentId,
userId,
teamId,
});
const document = await prisma.document.findFirst({
return await prisma.document.findFirstOrThrow({
where: documentWhereInput,
include: {
documentData: true,
@@ -49,12 +29,4 @@ export const getDocumentWithDetailsById = async ({
Field: true,
},
});
if (!document) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document not found',
});
}
return document;
};
@@ -1,9 +1,7 @@
import { TRPCError } from '@trpc/server';
import type { z } from 'zod';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { prisma } from '@documenso/prisma';
import { DocumentSchema } from '@documenso/prisma/generated/zod';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
@@ -15,16 +13,12 @@ export type MoveDocumentToTeamOptions = {
requestMetadata?: RequestMetadata;
};
export const ZMoveDocumentToTeamResponseSchema = DocumentSchema;
export type TMoveDocumentToTeamResponse = z.infer<typeof ZMoveDocumentToTeamResponseSchema>;
export const moveDocumentToTeam = async ({
documentId,
teamId,
userId,
requestMetadata,
}: MoveDocumentToTeamOptions): Promise<TMoveDocumentToTeamResponse> => {
}: MoveDocumentToTeamOptions) => {
return await prisma.$transaction(async (tx) => {
const user = await tx.user.findUniqueOrThrow({
where: { id: userId },
@@ -38,7 +38,7 @@ export const resendDocument = async ({
recipients,
teamId,
requestMetadata,
}: ResendDocumentOptions): Promise<void> => {
}: ResendDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
@@ -6,12 +6,8 @@ import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-po
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import {
DocumentStatus,
RecipientRole,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
@@ -72,19 +72,14 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
const i18n = await getI18nInstance(document.documentMeta?.language);
const emailSettings = extractDerivedDocumentEmailSettings(document.documentMeta);
const isDocumentCompletedEmailEnabled = emailSettings.documentCompleted;
const isOwnerDocumentCompletedEmailEnabled = emailSettings.ownerDocumentCompleted;
const isDocumentCompletedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
).documentCompleted;
// Send email to document owner if:
// 1. Owner document completed emails are enabled AND
// 2. Either:
// - The owner is not a recipient, OR
// - Recipient emails are disabled
// If the document owner is not a recipient, OR recipient emails are disabled, then send the email to them separately.
if (
isOwnerDocumentCompletedEmailEnabled &&
(!document.Recipient.find((recipient) => recipient.email === owner.email) ||
!isDocumentCompletedEmailEnabled)
!document.Recipient.find((recipient) => recipient.email === owner.email) ||
!isDocumentCompletedEmailEnabled
) {
const template = createElement(DocumentCompletedEmailTemplate, {
documentName: document.title,
@@ -1,5 +1,3 @@
import type { z } from 'zod';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
@@ -11,13 +9,8 @@ import {
RecipientRole,
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import {
DocumentMetaSchema,
DocumentSchema,
RecipientSchema,
} from '@documenso/prisma/generated/zod';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
@@ -34,20 +27,13 @@ export type SendDocumentOptions = {
requestMetadata?: RequestMetadata;
};
export const ZSendDocumentResponseSchema = DocumentSchema.extend({
documentMeta: DocumentMetaSchema.nullable(),
Recipient: RecipientSchema.array(),
});
export type TSendDocumentResponse = z.infer<typeof ZSendDocumentResponseSchema>;
export const sendDocument = async ({
documentId,
userId,
teamId,
sendEmail,
requestMetadata,
}: SendDocumentOptions): Promise<TSendDocumentResponse> => {
}: SendDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
@@ -225,7 +211,6 @@ export const sendDocument = async ({
id: documentId,
},
include: {
documentMeta: true,
Recipient: true,
},
});
@@ -1,7 +1,6 @@
'use server';
import { match } from 'ts-pattern';
import type { z } from 'zod';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
@@ -11,7 +10,6 @@ import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-
import { prisma } from '@documenso/prisma';
import { DocumentVisibility } from '@documenso/prisma/client';
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
import { DocumentSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
@@ -31,17 +29,13 @@ export type UpdateDocumentSettingsOptions = {
requestMetadata?: RequestMetadata;
};
export const ZUpdateDocumentSettingsResponseSchema = DocumentSchema;
export type TUpdateDocumentSettingsResponse = z.infer<typeof ZUpdateDocumentSettingsResponseSchema>;
export const updateDocumentSettings = async ({
userId,
teamId,
documentId,
data,
requestMetadata,
}: UpdateDocumentSettingsOptions): Promise<TUpdateDocumentSettingsResponse> => {
}: UpdateDocumentSettingsOptions) => {
if (!data.title && !data.globalAccessAuth && !data.globalActionAuth) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: 'Missing data to update',
@@ -1,9 +1,4 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { FieldSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
export type GetFieldByIdOptions = {
userId: number;
@@ -13,17 +8,13 @@ export type GetFieldByIdOptions = {
templateId?: number;
};
export const ZGetFieldByIdResponseSchema = FieldSchema;
export type TGetFieldByIdResponse = z.infer<typeof ZGetFieldByIdResponseSchema>;
export const getFieldById = async ({
userId,
teamId,
fieldId,
documentId,
templateId,
}: GetFieldByIdOptions): Promise<TGetFieldByIdResponse> => {
}: GetFieldByIdOptions) => {
const field = await prisma.field.findFirst({
where: {
id: fieldId,
@@ -54,11 +45,5 @@ export const getFieldById = async ({
},
});
if (!field) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Field not found',
});
}
return field;
};
@@ -1,5 +1,4 @@
import { isDeepEqual } from 'remeda';
import { z } from 'zod';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
@@ -24,7 +23,6 @@ import {
import { prisma } from '@documenso/prisma';
import type { Field } from '@documenso/prisma/client';
import { FieldType } from '@documenso/prisma/client';
import { FieldSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';
@@ -36,18 +34,12 @@ export interface SetFieldsForDocumentOptions {
requestMetadata?: RequestMetadata;
}
export const ZSetFieldsForDocumentResponseSchema = z.object({
fields: z.array(FieldSchema),
});
export type TSetFieldsForDocumentResponse = z.infer<typeof ZSetFieldsForDocumentResponseSchema>;
export const setFieldsForDocument = async ({
userId,
documentId,
fields,
requestMetadata,
}: SetFieldsForDocumentOptions): Promise<TSetFieldsForDocumentResponse> => {
}: SetFieldsForDocumentOptions): Promise<Field[]> => {
const document = await prisma.document.findFirst({
where: {
id: documentId,
@@ -83,15 +75,11 @@ export const setFieldsForDocument = async ({
});
if (!document) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document not found',
});
throw new Error('Document not found');
}
if (document.completedAt) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Document already complete',
});
throw new Error('Document already complete');
}
const existingFields = await prisma.field.findMany({
@@ -347,9 +335,7 @@ export const setFieldsForDocument = async ({
return !isRemoved && !isUpdated;
});
return {
fields: [...filteredFields, ...persistedFields],
};
return [...filteredFields, ...persistedFields];
};
/**
@@ -1,5 +1,3 @@
import { z } from 'zod';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
@@ -16,7 +14,6 @@ import {
} from '@documenso/lib/types/field-meta';
import { prisma } from '@documenso/prisma';
import { FieldType } from '@documenso/prisma/client';
import { FieldSchema } from '@documenso/prisma/generated/zod';
export type SetFieldsForTemplateOptions = {
userId: number;
@@ -34,17 +31,11 @@ export type SetFieldsForTemplateOptions = {
}[];
};
export const ZSetFieldsForTemplateResponseSchema = z.object({
fields: z.array(FieldSchema),
});
export type TSetFieldsForTemplateResponse = z.infer<typeof ZSetFieldsForTemplateResponseSchema>;
export const setFieldsForTemplate = async ({
userId,
templateId,
fields,
}: SetFieldsForTemplateOptions): Promise<TSetFieldsForTemplateResponse> => {
}: SetFieldsForTemplateOptions) => {
const template = await prisma.template.findFirst({
where: {
id: templateId,
@@ -215,7 +206,5 @@ export const setFieldsForTemplate = async ({
return !isRemoved && !isUpdated;
});
return {
fields: [...filteredFields, ...persistedFields],
};
return [...filteredFields, ...persistedFields];
};
+1 -14
View File
@@ -1,11 +1,9 @@
import type { PDFField, PDFWidgetAnnotation } from 'pdf-lib';
import { PDFCheckBox, PDFRadioGroup, PDFRef } from 'pdf-lib';
import {
PDFCheckBox,
PDFDict,
type PDFDocument,
PDFName,
PDFRadioGroup,
PDFRef,
drawObject,
popGraphicsState,
pushGraphicsState,
@@ -13,18 +11,7 @@ import {
translate,
} from 'pdf-lib';
export const removeOptionalContentGroups = (document: PDFDocument) => {
const context = document.context;
const catalog = context.lookup(context.trailerInfo.Root);
if (catalog instanceof PDFDict) {
catalog.delete(PDFName.of('OCProperties'));
}
};
export const flattenForm = (document: PDFDocument) => {
removeOptionalContentGroups(document);
const form = document.getForm();
form.updateFieldAppearances();
@@ -1,7 +1,6 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { z } from 'zod';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { mailer } from '@documenso/email/mailer';
@@ -22,7 +21,6 @@ import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { RecipientRole } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { RecipientSchema } from '@documenso/prisma/generated/zod';
import { getI18nInstance } from '../../client-only/providers/i18n.server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
@@ -41,21 +39,13 @@ export interface SetRecipientsForDocumentOptions {
requestMetadata?: RequestMetadata;
}
export const ZSetRecipientsForDocumentResponseSchema = z.object({
recipients: RecipientSchema.array(),
});
export type TSetRecipientsForDocumentResponse = z.infer<
typeof ZSetRecipientsForDocumentResponseSchema
>;
export const setRecipientsForDocument = async ({
userId,
teamId,
documentId,
recipients,
requestMetadata,
}: SetRecipientsForDocumentOptions): Promise<TSetRecipientsForDocumentResponse> => {
}: SetRecipientsForDocumentOptions): Promise<Recipient[]> => {
const document = await prisma.document.findFirst({
where: {
id: documentId,
@@ -354,9 +344,7 @@ export const setRecipientsForDocument = async ({
return !isRemoved && !isUpdated;
});
return {
recipients: [...filteredRecipients, ...persistedRecipients],
};
return [...filteredRecipients, ...persistedRecipients];
};
/**
@@ -1,5 +1,3 @@
import { z } from 'zod';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import {
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
@@ -8,7 +6,6 @@ import {
import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { RecipientRole } from '@documenso/prisma/client';
import { RecipientSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import {
@@ -32,20 +29,12 @@ export type SetRecipientsForTemplateOptions = {
}[];
};
export const ZSetRecipientsForTemplateResponseSchema = z.object({
recipients: RecipientSchema.array(),
});
export type TSetRecipientsForTemplateResponse = z.infer<
typeof ZSetRecipientsForTemplateResponseSchema
>;
export const setRecipientsForTemplate = async ({
userId,
teamId,
templateId,
recipients,
}: SetRecipientsForTemplateOptions): Promise<TSetRecipientsForTemplateResponse> => {
}: SetRecipientsForTemplateOptions) => {
const template = await prisma.template.findFirst({
where: {
id: templateId,
@@ -231,7 +220,5 @@ export const setRecipientsForTemplate = async ({
return !isRemoved && !isUpdated;
});
return {
recipients: [...filteredRecipients, ...persistedRecipients],
};
return [...filteredRecipients, ...persistedRecipients];
};
@@ -7,12 +7,12 @@ import { Prisma } from '@documenso/prisma/client';
import { TeamMemberInviteSchema } from '@documenso/prisma/generated/zod';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
import { type FindResultSet, ZFindResultSet } from '../../types/find-result-set';
export interface FindTeamMemberInvitesOptions {
userId: number;
teamId: number;
query?: string;
term?: string;
page?: number;
perPage?: number;
orderBy?: {
@@ -21,7 +21,7 @@ export interface FindTeamMemberInvitesOptions {
};
}
export const ZFindTeamMemberInvitesResponseSchema = ZFindResultResponse.extend({
export const ZFindTeamMemberInvitesResponseSchema = ZFindResultSet.extend({
data: TeamMemberInviteSchema.pick({
id: true,
teamId: true,
@@ -36,7 +36,7 @@ export type TFindTeamMemberInvitesResponse = z.infer<typeof ZFindTeamMemberInvit
export const findTeamMemberInvites = async ({
userId,
teamId,
query,
term,
page = 1,
perPage = 10,
orderBy,
@@ -59,10 +59,10 @@ export const findTeamMemberInvites = async ({
},
});
const termFilters: Prisma.TeamMemberInviteWhereInput | undefined = match(query)
const termFilters: Prisma.TeamMemberInviteWhereInput | undefined = match(term)
.with(P.string.minLength(1), () => ({
email: {
contains: query,
contains: term,
mode: Prisma.QueryMode.insensitive,
},
}))
@@ -101,5 +101,5 @@ export const findTeamMemberInvites = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
@@ -6,13 +6,12 @@ import type { TeamMember } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
import { TeamMemberSchema, UserSchema } from '@documenso/prisma/generated/zod';
import type { FindResultResponse } from '../../types/search-params';
import { ZFindResultResponse } from '../../types/search-params';
import { type FindResultSet, ZFindResultSet } from '../../types/find-result-set';
export interface FindTeamMembersOptions {
userId: number;
teamId: number;
query?: string;
term?: string;
page?: number;
perPage?: number;
orderBy?: {
@@ -21,7 +20,7 @@ export interface FindTeamMembersOptions {
};
}
export const ZFindTeamMembersResponseSchema = ZFindResultResponse.extend({
export const ZFindTeamMembersResponseSchema = ZFindResultSet.extend({
data: TeamMemberSchema.extend({
user: UserSchema.pick({
name: true,
@@ -35,7 +34,7 @@ export type TFindTeamMembersResponse = z.infer<typeof ZFindTeamMembersResponseSc
export const findTeamMembers = async ({
userId,
teamId,
query,
term,
page = 1,
perPage = 10,
orderBy,
@@ -55,11 +54,11 @@ export const findTeamMembers = async ({
},
});
const termFilters: Prisma.TeamMemberWhereInput | undefined = match(query)
const termFilters: Prisma.TeamMemberWhereInput | undefined = match(term)
.with(P.string.minLength(1), () => ({
user: {
name: {
contains: query,
contains: term,
mode: Prisma.QueryMode.insensitive,
},
},
@@ -110,5 +109,5 @@ export const findTeamMembers = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
@@ -5,11 +5,11 @@ import type { Team } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
import { TeamPendingSchema } from '@documenso/prisma/generated/zod';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
import { type FindResultSet, ZFindResultSet } from '../../types/find-result-set';
export interface FindTeamsPendingOptions {
userId: number;
query?: string;
term?: string;
page?: number;
perPage?: number;
orderBy?: {
@@ -18,7 +18,7 @@ export interface FindTeamsPendingOptions {
};
}
export const ZFindTeamsPendingResponseSchema = ZFindResultResponse.extend({
export const ZFindTeamsPendingResponseSchema = ZFindResultSet.extend({
data: TeamPendingSchema.array(),
});
@@ -26,7 +26,7 @@ export type TFindTeamsPendingResponse = z.infer<typeof ZFindTeamsPendingResponse
export const findTeamsPending = async ({
userId,
query,
term,
page = 1,
perPage = 10,
orderBy,
@@ -38,9 +38,9 @@ export const findTeamsPending = async ({
ownerUserId: userId,
};
if (query && query.length > 0) {
if (term && term.length > 0) {
whereClause.name = {
contains: query,
contains: term,
mode: Prisma.QueryMode.insensitive,
};
}
@@ -65,5 +65,5 @@ export const findTeamsPending = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
+6 -7
View File
@@ -1,12 +1,11 @@
import type { FindResultSet } from '@documenso/lib/types/find-result-set';
import { prisma } from '@documenso/prisma';
import type { Team } from '@documenso/prisma/client';
import { Prisma } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export interface FindTeamsOptions {
userId: number;
query?: string;
term?: string;
page?: number;
perPage?: number;
orderBy?: {
@@ -17,7 +16,7 @@ export interface FindTeamsOptions {
export const findTeams = async ({
userId,
query,
term,
page = 1,
perPage = 10,
orderBy,
@@ -33,9 +32,9 @@ export const findTeams = async ({
},
};
if (query && query.length > 0) {
if (term && term.length > 0) {
whereClause.name = {
contains: query,
contains: term,
mode: Prisma.QueryMode.insensitive,
};
}
@@ -73,5 +72,5 @@ export const findTeams = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof maskedData>;
} satisfies FindResultSet<typeof maskedData>;
};
@@ -3,7 +3,6 @@ import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { mailer } from '@documenso/email/mailer';
import { DocumentCreatedFromDirectTemplateEmailTemplate } from '@documenso/email/templates/document-created-from-direct-template';
@@ -68,16 +67,6 @@ type CreatedDirectRecipientField = {
derivedRecipientActionAuth: TRecipientActionAuthTypes | null;
};
export const ZCreateDocumentFromDirectTemplateResponseSchema = z.object({
token: z.string(),
documentId: z.number(),
recipientId: z.number(),
});
export type TCreateDocumentFromDirectTemplateResponse = z.infer<
typeof ZCreateDocumentFromDirectTemplateResponseSchema
>;
export const createDocumentFromDirectTemplate = async ({
directRecipientName: initialDirectRecipientName,
directRecipientEmail,
@@ -87,7 +76,7 @@ export const createDocumentFromDirectTemplate = async ({
templateUpdatedAt,
requestMetadata,
user,
}: CreateDocumentFromDirectTemplateOptions): Promise<TCreateDocumentFromDirectTemplateResponse> => {
}: CreateDocumentFromDirectTemplateOptions) => {
const template = await prisma.template.findFirst({
where: {
directLink: {
@@ -1,5 +1,3 @@
import type { z } from 'zod';
import { nanoid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import type { DocumentDistributionMethod } from '@documenso/prisma/client';
@@ -13,11 +11,6 @@ import {
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import {
DocumentDataSchema,
DocumentSchema,
RecipientSchema,
} from '@documenso/prisma/generated/zod';
import type { SupportedLanguageCodes } from '../../constants/i18n';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -43,6 +36,10 @@ type FinalRecipient = Pick<
fields: Field[];
};
export type CreateDocumentFromTemplateResponse = Awaited<
ReturnType<typeof createDocumentFromTemplate>
>;
export type CreateDocumentFromTemplateOptions = {
templateId: number;
externalId?: string | null;
@@ -75,15 +72,6 @@ export type CreateDocumentFromTemplateOptions = {
requestMetadata?: RequestMetadata;
};
export const ZCreateDocumentFromTemplateResponseSchema = DocumentSchema.extend({
documentData: DocumentDataSchema,
Recipient: RecipientSchema.array(),
});
export type TCreateDocumentFromTemplateResponse = z.infer<
typeof ZCreateDocumentFromTemplateResponseSchema
>;
export const createDocumentFromTemplate = async ({
templateId,
externalId,
@@ -92,7 +80,7 @@ export const createDocumentFromTemplate = async ({
recipients,
override,
requestMetadata,
}: CreateDocumentFromTemplateOptions): Promise<TCreateDocumentFromTemplateResponse> => {
}: CreateDocumentFromTemplateOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
@@ -1,15 +1,13 @@
'use server';
import { nanoid } from 'nanoid';
import type { z } from 'zod';
import {
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
DIRECT_TEMPLATE_RECIPIENT_NAME,
} from '@documenso/lib/constants/direct-templates';
import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
import type { Recipient, TemplateDirectLink } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -19,17 +17,11 @@ export type CreateTemplateDirectLinkOptions = {
directRecipientId?: number;
};
export const ZCreateTemplateDirectLinkResponseSchema = TemplateDirectLinkSchema;
export type TCreateTemplateDirectLinkResponse = z.infer<
typeof ZCreateTemplateDirectLinkResponseSchema
>;
export const createTemplateDirectLink = async ({
templateId,
userId,
directRecipientId,
}: CreateTemplateDirectLinkOptions): Promise<TCreateTemplateDirectLinkResponse> => {
}: CreateTemplateDirectLinkOptions): Promise<TemplateDirectLink> => {
const template = await prisma.template.findFirst({
where: {
id: templateId,
@@ -1,7 +1,4 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import type { TCreateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema';
export type CreateTemplateOptions = TCreateTemplateMutationSchema & {
@@ -9,10 +6,6 @@ export type CreateTemplateOptions = TCreateTemplateMutationSchema & {
teamId?: number;
};
export const ZCreateTemplateResponseSchema = TemplateSchema;
export type TCreateTemplateResponse = z.infer<typeof ZCreateTemplateResponseSchema>;
export const createTemplate = async ({
title,
userId,
@@ -1,25 +1,19 @@
import { omit } from 'remeda';
import type { z } from 'zod';
import { nanoid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import type { TDuplicateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema';
export type DuplicateTemplateOptions = TDuplicateTemplateMutationSchema & {
userId: number;
};
export const ZDuplicateTemplateResponseSchema = TemplateSchema;
export type TDuplicateTemplateResponse = z.infer<typeof ZDuplicateTemplateResponseSchema>;
export const duplicateTemplate = async ({
templateId,
userId,
teamId,
}: DuplicateTemplateOptions): Promise<TDuplicateTemplateResponse> => {
}: DuplicateTemplateOptions) => {
let templateWhereFilter: Prisma.TemplateWhereUniqueInput = {
id: templateId,
userId,
@@ -1,18 +1,5 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import type { Prisma, Template } from '@documenso/prisma/client';
import {
DocumentDataSchema,
FieldSchema,
RecipientSchema,
TeamSchema,
TemplateDirectLinkSchema,
TemplateMetaSchema,
TemplateSchema,
} from '@documenso/prisma/generated/zod';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
export type FindTemplatesOptions = {
userId: number;
@@ -22,28 +9,8 @@ export type FindTemplatesOptions = {
perPage?: number;
};
export const ZFindTemplatesResponseSchema = ZFindResultResponse.extend({
data: TemplateSchema.extend({
templateDocumentData: DocumentDataSchema,
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
Field: FieldSchema.array(),
Recipient: RecipientSchema.array(),
templateMeta: TemplateMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.pick({
token: true,
enabled: true,
}).nullable(),
}).array(), // Todo: openapi.
});
export type TFindTemplatesResponse = z.infer<typeof ZFindTemplatesResponseSchema>;
export type FindTemplateRow = TFindTemplatesResponse['data'][number];
export type FindTemplatesResponse = Awaited<ReturnType<typeof findTemplates>>;
export type FindTemplateRow = FindTemplatesResponse['templates'][number];
export const findTemplates = async ({
userId,
@@ -51,7 +18,7 @@ export const findTemplates = async ({
type,
page = 1,
perPage = 10,
}: FindTemplatesOptions): Promise<TFindTemplatesResponse> => {
}: FindTemplatesOptions) => {
let whereFilter: Prisma.TemplateWhereInput = {
userId,
teamId: null,
@@ -71,7 +38,7 @@ export const findTemplates = async ({
};
}
const [data, count] = await Promise.all([
const [templates, count] = await Promise.all([
prisma.template.findMany({
where: whereFilter,
include: {
@@ -108,10 +75,7 @@ export const findTemplates = async ({
]);
return {
data,
count,
currentPage: Math.max(page, 1),
perPage,
templates,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
};
};
@@ -1,9 +1,6 @@
import type { z } from 'zod';
import { TRPCError } from '@trpc/server';
import { prisma } from '@documenso/prisma';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
export type MoveTemplateToTeamOptions = {
templateId: number;
@@ -11,15 +8,11 @@ export type MoveTemplateToTeamOptions = {
userId: number;
};
export const ZMoveTemplateToTeamResponseSchema = TemplateSchema;
export type TMoveTemplateToTeamResponse = z.infer<typeof ZMoveTemplateToTeamResponseSchema>;
export const moveTemplateToTeam = async ({
templateId,
teamId,
userId,
}: MoveTemplateToTeamOptions): Promise<TMoveTemplateToTeamResponse> => {
}: MoveTemplateToTeamOptions) => {
return await prisma.$transaction(async (tx) => {
const template = await tx.template.findFirst({
where: {
@@ -30,7 +23,8 @@ export const moveTemplateToTeam = async ({
});
if (!template) {
throw new AppError(AppErrorCode.NOT_FOUND, {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Template not found or already associated with a team.',
});
}
@@ -47,8 +41,9 @@ export const moveTemplateToTeam = async ({
});
if (!team) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'Team does not exist or you are not a member of this team.',
throw new TRPCError({
code: 'FORBIDDEN',
message: 'You are not a member of this team.',
});
}
@@ -1,9 +1,6 @@
'use server';
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -13,17 +10,11 @@ export type ToggleTemplateDirectLinkOptions = {
enabled: boolean;
};
export const ZToggleTemplateDirectLinkResponseSchema = TemplateDirectLinkSchema;
export type TToggleTemplateDirectLinkResponse = z.infer<
typeof ZToggleTemplateDirectLinkResponseSchema
>;
export const toggleTemplateDirectLink = async ({
templateId,
userId,
enabled,
}: ToggleTemplateDirectLinkOptions): Promise<TToggleTemplateDirectLinkResponse> => {
}: ToggleTemplateDirectLinkOptions) => {
const template = await prisma.template.findFirst({
where: {
id: templateId,
@@ -1,12 +1,9 @@
'use server';
import type { z } from 'zod';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { prisma } from '@documenso/prisma';
import type { Template, TemplateMeta } from '@documenso/prisma/client';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
@@ -29,17 +26,13 @@ export type UpdateTemplateSettingsOptions = {
requestMetadata?: RequestMetadata;
};
export const ZUpdateTemplateSettingsResponseSchema = TemplateSchema;
export type TUpdateTemplateSettingsResponse = z.infer<typeof ZUpdateTemplateSettingsResponseSchema>;
export const updateTemplateSettings = async ({
userId,
teamId,
templateId,
meta,
data,
}: UpdateTemplateSettingsOptions): Promise<TUpdateTemplateSettingsResponse> => {
}: UpdateTemplateSettingsOptions) => {
if (Object.values(data).length === 0 && Object.keys(meta ?? {}).length === 0) {
throw new AppError(AppErrorCode.INVALID_BODY, {
message: 'Missing data to update',
@@ -1,8 +1,7 @@
import type { FindResultSet } from '@documenso/lib/types/find-result-set';
import { prisma } from '@documenso/prisma';
import type { UserSecurityAuditLog, UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { FindResultResponse } from '../../types/search-params';
export type FindUserSecurityAuditLogsOptions = {
userId: number;
type?: UserSecurityAuditLogType;
@@ -49,5 +48,5 @@ export const findUserSecurityAuditLogs = async ({
currentPage: Math.max(page, 1),
perPage,
totalPages: Math.ceil(count / perPage),
} satisfies FindResultResponse<typeof data>;
} satisfies FindResultSet<typeof data>;
};
-4
View File
@@ -9,7 +9,6 @@ export enum DocumentEmailEvents {
DocumentPending = 'documentPending',
DocumentCompleted = 'documentCompleted',
DocumentDeleted = 'documentDeleted',
OwnerDocumentCompleted = 'ownerDocumentCompleted',
}
export const ZDocumentEmailSettingsSchema = z
@@ -19,7 +18,6 @@ export const ZDocumentEmailSettingsSchema = z
documentPending: z.boolean().default(true),
documentCompleted: z.boolean().default(true),
documentDeleted: z.boolean().default(true),
ownerDocumentCompleted: z.boolean().default(true),
})
.strip()
.catch(() => ({
@@ -28,7 +26,6 @@ export const ZDocumentEmailSettingsSchema = z
documentPending: true,
documentCompleted: true,
documentDeleted: true,
ownerDocumentCompleted: true,
}));
export type TDocumentEmailSettings = z.infer<typeof ZDocumentEmailSettingsSchema>;
@@ -51,6 +48,5 @@ export const extractDerivedDocumentEmailSettings = (
documentPending: false,
documentCompleted: false,
documentDeleted: false,
ownerDocumentCompleted: emailSettings.ownerDocumentCompleted,
};
};
+18
View File
@@ -0,0 +1,18 @@
import { z } from 'zod';
export const ZFindResultSet = z.object({
data: z.union([z.array(z.unknown()), z.unknown()]),
count: z.number(),
currentPage: z.number(),
perPage: z.number(),
totalPages: z.number(),
});
// Can't infer generics from Zod.
export type FindResultSet<T> = {
data: T extends Array<unknown> ? T : T[];
count: number;
currentPage: number;
perPage: number;
totalPages: number;
};
+2 -35
View File
@@ -1,24 +1,6 @@
import { z } from 'zod';
/**
* Backend only schema is used for find search params.
*
* Does not catch, because TRPC Open API won't allow catches as a type.
*
* Keep this and `ZUrlSearchParamsSchema` in sync.
*/
export const ZFindSearchParamsSchema = z.object({
query: z.string().optional(),
page: z.coerce.number().min(1).optional(),
perPage: z.coerce.number().min(1).optional(),
});
/**
* Frontend schema used to parse search params from URL.
*
* Keep this and `ZFindSearchParamsSchema` in sync.
*/
export const ZUrlSearchParamsSchema = z.object({
export const ZBaseTableSearchParamsSchema = z.object({
query: z
.string()
.optional()
@@ -35,19 +17,4 @@ export const ZUrlSearchParamsSchema = z.object({
.catch(() => undefined),
});
export const ZFindResultResponse = z.object({
data: z.union([z.array(z.unknown()), z.unknown()]),
count: z.number(),
currentPage: z.number(),
perPage: z.number(),
totalPages: z.number(),
});
// Can't infer generics from Zod.
export type FindResultResponse<T> = {
data: T extends Array<unknown> ? T : T[];
count: number;
currentPage: number;
perPage: number;
totalPages: number;
};
export type TBaseTableSearchParamsSchema = z.infer<typeof ZBaseTableSearchParamsSchema>;
+7 -13
View File
@@ -8,7 +8,6 @@ import { DocumentDataType } from '@documenso/prisma/client';
import { AppError } from '../../errors/app-error';
import { createDocumentData } from '../../server-only/document-data/create-document-data';
import { removeOptionalContentGroups } from '../../server-only/pdf/flatten-form';
type File = {
name: string;
@@ -25,25 +24,20 @@ export const putPdfFile = async (file: File) => {
() => false,
);
const pdf = await PDFDocument.load(await file.arrayBuffer()).catch((e) => {
console.error(`PDF upload parse error: ${e.message}`);
// This will prevent uploading encrypted PDFs or anything that can't be opened.
if (!isEncryptedDocumentsAllowed) {
await PDFDocument.load(await file.arrayBuffer()).catch((e) => {
console.error(`PDF upload parse error: ${e.message}`);
throw new AppError('INVALID_DOCUMENT_FILE');
});
if (!isEncryptedDocumentsAllowed && pdf.isEncrypted) {
throw new AppError('INVALID_DOCUMENT_FILE');
throw new AppError('INVALID_DOCUMENT_FILE');
});
}
if (!file.name.endsWith('.pdf')) {
file.name = `${file.name}.pdf`;
}
removeOptionalContentGroups(pdf);
const bytes = await pdf.save();
const { type, data } = await putFile(new File([bytes], file.name, { type: 'application/pdf' }));
const { type, data } = await putFile(file);
return await createDocumentData({ type, data });
};
+16 -10
View File
@@ -1,3 +1,6 @@
import { env } from 'next-runtime-env';
import { IS_BILLING_ENABLED } from '../constants/app';
import type { Subscription } from '.prisma/client';
import { SubscriptionStatus } from '.prisma/client';
@@ -7,21 +10,12 @@ import { SubscriptionStatus } from '.prisma/client';
export const subscriptionsContainsActivePlan = (
subscriptions: Subscription[],
priceIds: string[],
allowPastDue?: boolean,
) => {
const allowedSubscriptionStatuses: SubscriptionStatus[] = [SubscriptionStatus.ACTIVE];
if (allowPastDue) {
allowedSubscriptionStatuses.push(SubscriptionStatus.PAST_DUE);
}
return subscriptions.some(
(subscription) =>
allowedSubscriptionStatuses.includes(subscription.status) &&
priceIds.includes(subscription.priceId),
subscription.status === SubscriptionStatus.ACTIVE && priceIds.includes(subscription.priceId),
);
};
/**
* Returns true if there is a subscription that is active and is one of the provided product IDs.
*/
@@ -34,3 +28,15 @@ export const subscriptionsContainsActiveProductId = (
subscription.status === SubscriptionStatus.ACTIVE && productId.includes(subscription.planId),
);
};
export const subscriptionsContainActiveEnterprisePlan = (
subscriptions?: Subscription[],
): boolean => {
const enterprisePlanId = env('NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID');
if (!enterprisePlanId || !subscriptions || !IS_BILLING_ENABLED()) {
return false;
}
return subscriptionsContainsActivePlan(subscriptions, [enterprisePlanId]);
};
+1 -3
View File
@@ -78,9 +78,7 @@ class HoneybadgerLogger implements Logger {
error(error: Error, options?: LoggerDescriptionOptions): void {
const { context = {}, level = 'error', method, path } = options || {};
// const tags = [`level:${level}`];
const tags = [];
const tags = [`level:${level}`];
let errorMessage = error.message;
if (method) {
-1
View File
@@ -16,7 +16,6 @@ module.exports = {
},
colors: {
border: 'hsl(var(--border))',
'field-border': 'hsl(var(--field-border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
+2 -2
View File
@@ -24,9 +24,9 @@ import {
export const adminRouter = router({
findDocuments: adminProcedure.input(ZAdminFindDocumentsQuerySchema).query(async ({ input }) => {
const { query, page, perPage } = input;
const { term, page, perPage } = input;
return await findDocuments({ query, page, perPage });
return await findDocuments({ term, page, perPage });
}),
updateUser: adminProcedure
+3 -2
View File
@@ -2,9 +2,10 @@ import { Role } from '@prisma/client';
import z from 'zod';
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
export const ZAdminFindDocumentsQuerySchema = ZFindSearchParamsSchema.extend({
export const ZAdminFindDocumentsQuerySchema = z.object({
term: z.string().optional(),
page: z.number().optional().default(1),
perPage: z.number().optional().default(20),
});
+1 -3
View File
@@ -4,7 +4,6 @@ import { parse } from 'cookie-es';
import { env } from 'next-runtime-env';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { formatSecureCookieName } from '@documenso/lib/constants/auth';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { jobsClient } from '@documenso/lib/jobs/client';
import { ErrorCode } from '@documenso/lib/next-auth/error-codes';
@@ -112,8 +111,7 @@ export const authRouter = router({
const cookies = parse(ctx.req.headers.cookie ?? '');
const sessionIdToken =
cookies[formatSecureCookieName('__Host-next-auth.csrf-token')] ||
cookies[formatSecureCookieName('next-auth.csrf-token')];
cookies['__Host-next-auth.csrf-token'] || cookies['next-auth.csrf-token'];
if (!sessionIdToken) {
throw new Error('Missing CSRF token');
+2 -2
View File
@@ -1,6 +1,6 @@
import { z } from 'zod';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZRegistrationResponseJSONSchema } from '@documenso/lib/types/webauthn';
export const ZCurrentPasswordSchema = z
@@ -55,7 +55,7 @@ export const ZUpdatePasskeyMutationSchema = z.object({
name: z.string().trim().min(1),
});
export const ZFindPasskeysQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindPasskeysQuerySchema = ZBaseTableSearchParamsSchema.extend({
orderBy: z
.object({
column: z.enum(['createdAt', 'updatedAt', 'name']),
+39 -130
View File
@@ -8,40 +8,19 @@ import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto';
import { AppError } from '@documenso/lib/errors/app-error';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import {
ZCreateDocumentResponseSchema,
createDocument,
} from '@documenso/lib/server-only/document/create-document';
import { createDocument } from '@documenso/lib/server-only/document/create-document';
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
import {
ZDuplicateDocumentResponseSchema,
duplicateDocument,
} from '@documenso/lib/server-only/document/duplicate-document-by-id';
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
import {
ZFindDocumentsResponseSchema,
findDocuments,
} from '@documenso/lib/server-only/document/find-documents';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import {
ZGetDocumentWithDetailsByIdResponseSchema,
getDocumentWithDetailsById,
} from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import {
ZMoveDocumentToTeamResponseSchema,
moveDocumentToTeam,
} from '@documenso/lib/server-only/document/move-document-to-team';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
import {
ZSendDocumentResponseSchema,
sendDocument,
} from '@documenso/lib/server-only/document/send-document';
import {
ZUpdateDocumentSettingsResponseSchema,
updateDocumentSettings,
} from '@documenso/lib/server-only/document/update-document-settings';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { updateDocumentSettings } from '@documenso/lib/server-only/document/update-document-settings';
import { updateTitle } from '@documenso/lib/server-only/document/update-title';
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@@ -53,13 +32,12 @@ import {
ZDeleteDocumentMutationSchema,
ZDownloadAuditLogsMutationSchema,
ZDownloadCertificateMutationSchema,
ZDuplicateDocumentMutationSchema,
ZFindDocumentAuditLogsQuerySchema,
ZFindDocumentsQuerySchema,
ZGetDocumentByIdQuerySchema,
ZGetDocumentByTokenQuerySchema,
ZGetDocumentWithDetailsByIdQuerySchema,
ZMoveDocumentToTeamSchema,
ZMoveDocumentsToTeamSchema,
ZResendDocumentMutationSchema,
ZSearchDocumentsMutationSchema,
ZSendDocumentMutationSchema,
@@ -71,9 +49,7 @@ import {
} from './schema';
export const documentRouter = router({
/**
* @private
*/
// Internal endpoint for now.
getDocumentById: authenticatedProcedure
.input(ZGetDocumentByIdQuerySchema)
.query(async ({ input, ctx }) => {
@@ -83,9 +59,7 @@ export const documentRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
getDocumentByToken: procedure
.input(ZGetDocumentByTokenQuerySchema)
.query(async ({ input, ctx }) => {
@@ -97,9 +71,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
findDocuments: authenticatedProcedure
.meta({
openapi: {
@@ -111,42 +82,27 @@ export const documentRouter = router({
},
})
.input(ZFindDocumentsQuerySchema)
.output(ZFindDocumentsResponseSchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
const { user } = ctx;
const {
query,
teamId,
templateId,
page,
perPage,
orderByDirection,
orderByColumn,
source,
status,
} = input;
const { search, teamId, templateId, page, perPage, orderBy, source, status } = input;
const documents = await findDocuments({
userId: user.id,
teamId,
templateId,
query,
search,
source,
status,
page,
perPage,
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
orderBy,
});
return documents;
}),
/**
* @public
*
* Todo: Refactor to getDocumentById.
*/
getDocumentWithDetailsById: authenticatedProcedure
.meta({
openapi: {
@@ -158,7 +114,7 @@ export const documentRouter = router({
},
})
.input(ZGetDocumentWithDetailsByIdQuerySchema)
.output(ZGetDocumentWithDetailsByIdResponseSchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await getDocumentWithDetailsById({
...input,
@@ -166,9 +122,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
createDocument: authenticatedProcedure
.meta({
openapi: {
@@ -179,7 +132,7 @@ export const documentRouter = router({
},
})
.input(ZCreateDocumentMutationSchema)
.output(ZCreateDocumentResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { title, documentDataId, teamId } = input;
@@ -201,11 +154,7 @@ export const documentRouter = router({
});
}),
/**
* @public
*
* Todo: Refactor to updateDocument.
*/
// Todo: Refactor to updateDocument.
setSettingsForDocument: authenticatedProcedure
.meta({
openapi: {
@@ -216,7 +165,7 @@ export const documentRouter = router({
},
})
.input(ZSetSettingsForDocumentMutationSchema)
.output(ZUpdateDocumentSettingsResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, data, meta } = input;
@@ -245,9 +194,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
deleteDocument: authenticatedProcedure
.meta({
openapi: {
@@ -258,13 +204,13 @@ export const documentRouter = router({
},
})
.input(ZDeleteDocumentMutationSchema)
.output(z.void())
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId } = input;
const userId = ctx.user.id;
await deleteDocument({
return await deleteDocument({
id: documentId,
userId,
teamId,
@@ -272,9 +218,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
moveDocumentToTeam: authenticatedProcedure
.meta({
openapi: {
@@ -285,8 +228,8 @@ export const documentRouter = router({
tags: ['Documents'],
},
})
.input(ZMoveDocumentToTeamSchema)
.output(ZMoveDocumentToTeamResponseSchema)
.input(ZMoveDocumentsToTeamSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId } = input;
const userId = ctx.user.id;
@@ -299,9 +242,7 @@ export const documentRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
// Should probably use `updateDocument`
setTitleForDocument: authenticatedProcedure
.input(ZSetTitleForDocumentMutationSchema)
@@ -319,9 +260,7 @@ export const documentRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
setPasswordForDocument: authenticatedProcedure
.input(ZSetPasswordForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -346,9 +285,7 @@ export const documentRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
setSigningOrderForDocument: authenticatedProcedure
.input(ZSetSigningOrderForDocumentMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -362,9 +299,7 @@ export const documentRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
updateTypedSignatureSettings: authenticatedProcedure
.input(ZUpdateTypedSignatureSettingsMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -391,12 +326,8 @@ export const documentRouter = router({
});
}),
/**
* @public
*
* Todo: Refactor to distributeDocument.
* Todo: Rework before releasing API.
*/
// Todo: Refactor to distributeDocument.
// Todo: Rework before releasing API.
sendDocument: authenticatedProcedure
.meta({
openapi: {
@@ -408,7 +339,7 @@ export const documentRouter = router({
},
})
.input(ZSendDocumentMutationSchema)
.output(ZSendDocumentResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, meta } = input;
@@ -443,9 +374,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
resendDocument: authenticatedProcedure
.meta({
openapi: {
@@ -458,7 +386,7 @@ export const documentRouter = router({
},
})
.input(ZResendDocumentMutationSchema)
.output(z.void())
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
return await resendDocument({
userId: ctx.user.id,
@@ -467,9 +395,6 @@ export const documentRouter = router({
});
}),
/**
* @public
*/
duplicateDocument: authenticatedProcedure
.meta({
openapi: {
@@ -479,18 +404,16 @@ export const documentRouter = router({
tags: ['Documents'],
},
})
.input(ZDuplicateDocumentMutationSchema)
.output(ZDuplicateDocumentResponseSchema)
.input(ZGetDocumentByIdQuerySchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
return await duplicateDocument({
return await duplicateDocumentById({
userId: ctx.user.id,
...input,
});
}),
/**
* @private
*/
// Internal endpoint for now.
searchDocuments: authenticatedProcedure
.input(ZSearchDocumentsMutationSchema)
.query(async ({ input, ctx }) => {
@@ -504,21 +427,11 @@ export const documentRouter = router({
return documents;
}),
/**
* @private
*/
// Internal endpoint for now.
findDocumentAuditLogs: authenticatedProcedure
.input(ZFindDocumentAuditLogsQuerySchema)
.query(async ({ input, ctx }) => {
const {
page,
perPage,
documentId,
cursor,
filterForRecentActivity,
orderByColumn,
orderByDirection,
} = input;
const { page, perPage, documentId, cursor, filterForRecentActivity, orderBy } = input;
return await findDocumentAuditLogs({
page,
@@ -526,14 +439,12 @@ export const documentRouter = router({
documentId,
cursor,
filterForRecentActivity,
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
orderBy,
userId: ctx.user.id,
});
}),
/**
* @private
*/
// Internal endpoint for now.
downloadAuditLogs: authenticatedProcedure
.input(ZDownloadAuditLogsMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -562,9 +473,7 @@ export const documentRouter = router({
};
}),
/**
* @private
*/
// Internal endpoint for now.
downloadCertificate: authenticatedProcedure
.input(ZDownloadCertificateMutationSchema)
.mutation(async ({ input, ctx }) => {
+21 -14
View File
@@ -6,7 +6,7 @@ import {
ZDocumentActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
import {
DocumentDistributionMethod,
@@ -18,21 +18,33 @@ import {
RecipientRole,
} from '@documenso/prisma/client';
export const ZFindDocumentsQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindDocumentsQuerySchema = ZBaseTableSearchParamsSchema.extend({
teamId: z.number().min(1).optional(),
templateId: z.number().min(1).optional(),
search: z
.string()
.optional()
.catch(() => undefined),
source: z.nativeEnum(DocumentSource).optional(),
status: z.nativeEnum(DocumentStatus).optional(),
orderByColumn: z.enum(['createdAt']).optional(),
orderByDirection: z.enum(['asc', 'desc']).default('desc'),
});
orderBy: z
.object({
column: z.enum(['createdAt']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
}).omit({ query: true });
export const ZFindDocumentAuditLogsQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindDocumentAuditLogsQuerySchema = ZBaseTableSearchParamsSchema.extend({
documentId: z.number().min(1),
cursor: z.string().optional(),
filterForRecentActivity: z.boolean().optional(),
orderByColumn: z.enum(['createdAt', 'type']).optional(),
orderByDirection: z.enum(['asc', 'desc']).default('desc'),
orderBy: z
.object({
column: z.enum(['createdAt', 'type']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
});
export const ZGetDocumentByIdQuerySchema = z.object({
@@ -40,11 +52,6 @@ export const ZGetDocumentByIdQuerySchema = z.object({
teamId: z.number().min(1).optional(),
});
export const ZDuplicateDocumentMutationSchema = z.object({
documentId: z.number().min(1),
teamId: z.number().min(1).optional(),
});
export type TGetDocumentByIdQuerySchema = z.infer<typeof ZGetDocumentByIdQuerySchema>;
export const ZGetDocumentByTokenQuerySchema = z.object({
@@ -220,7 +227,7 @@ export const ZDownloadCertificateMutationSchema = z.object({
teamId: z.number().optional(),
});
export const ZMoveDocumentToTeamSchema = z.object({
export const ZMoveDocumentsToTeamSchema = z.object({
documentId: z.number(),
teamId: z.number(),
});
+10 -30
View File
@@ -1,16 +1,9 @@
import {
ZGetFieldByIdResponseSchema,
getFieldById,
} from '@documenso/lib/server-only/field/get-field-by-id';
import { z } from 'zod';
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
import { removeSignedFieldWithToken } from '@documenso/lib/server-only/field/remove-signed-field-with-token';
import {
ZSetFieldsForDocumentResponseSchema,
setFieldsForDocument,
} from '@documenso/lib/server-only/field/set-fields-for-document';
import {
ZSetFieldsForTemplateResponseSchema,
setFieldsForTemplate,
} from '@documenso/lib/server-only/field/set-fields-for-template';
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-fields-for-template';
import { signFieldWithToken } from '@documenso/lib/server-only/field/sign-field-with-token';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@@ -24,9 +17,6 @@ import {
} from './schema';
export const fieldRouter = router({
/**
* @public
*/
getField: authenticatedProcedure
.meta({
openapi: {
@@ -38,7 +28,7 @@ export const fieldRouter = router({
},
})
.input(ZGetFieldQuerySchema)
.output(ZGetFieldByIdResponseSchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
const { fieldId, teamId } = input;
@@ -49,9 +39,6 @@ export const fieldRouter = router({
});
}),
/**
* @public
*/
addFields: authenticatedProcedure
.meta({
openapi: {
@@ -62,7 +49,7 @@ export const fieldRouter = router({
},
})
.input(ZAddFieldsMutationSchema)
.output(ZSetFieldsForDocumentResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, fields } = input;
@@ -84,9 +71,6 @@ export const fieldRouter = router({
});
}),
/**
* @public
*/
addTemplateFields: authenticatedProcedure
.meta({
openapi: {
@@ -97,7 +81,7 @@ export const fieldRouter = router({
},
})
.input(ZAddTemplateFieldsMutationSchema)
.output(ZSetFieldsForTemplateResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, fields } = input;
@@ -118,9 +102,7 @@ export const fieldRouter = router({
});
}),
/**
* @internal
*/
// Internal endpoint for now.
signFieldWithToken: procedure
.input(ZSignFieldWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -137,9 +119,7 @@ export const fieldRouter = router({
});
}),
/**
* @internal
*/
// Internal endpoint for now.
removeSignedFieldWithToken: procedure
.input(ZRemovedSignedFieldWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
-12
View File
@@ -1,12 +0,0 @@
import { generateOpenApiDocument } from 'trpc-openapi';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { appRouter } from './router';
export const openApiDocument = generateOpenApiDocument(appRouter, {
title: 'Do not use.',
version: '0.0.0',
baseUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/api/beta`,
// docsUrl: '', // Todo
});
@@ -1,13 +1,9 @@
import { z } from 'zod';
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
import { rejectDocumentWithToken } from '@documenso/lib/server-only/document/reject-document-with-token';
import {
ZSetRecipientsForDocumentResponseSchema,
setRecipientsForDocument,
} from '@documenso/lib/server-only/recipient/set-recipients-for-document';
import {
ZSetRecipientsForTemplateResponseSchema,
setRecipientsForTemplate,
} from '@documenso/lib/server-only/recipient/set-recipients-for-template';
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
import { setRecipientsForTemplate } from '@documenso/lib/server-only/recipient/set-recipients-for-template';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { authenticatedProcedure, procedure, router } from '../trpc';
@@ -19,9 +15,6 @@ import {
} from './schema';
export const recipientRouter = router({
/**
* @internal
*/
addSigners: authenticatedProcedure
.meta({
openapi: {
@@ -32,7 +25,7 @@ export const recipientRouter = router({
},
})
.input(ZAddSignersMutationSchema)
.output(ZSetRecipientsForDocumentResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { documentId, teamId, signers } = input;
@@ -52,9 +45,6 @@ export const recipientRouter = router({
});
}),
/**
* @internal
*/
addTemplateSigners: authenticatedProcedure
.meta({
openapi: {
@@ -65,7 +55,7 @@ export const recipientRouter = router({
},
})
.input(ZAddTemplateSignersMutationSchema)
.output(ZSetRecipientsForTemplateResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, signers, teamId } = input;
@@ -84,9 +74,7 @@ export const recipientRouter = router({
});
}),
/**
* @internal
*/
// Internal endpoint for now.
completeDocumentWithToken: procedure
.input(ZCompleteDocumentWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -101,9 +89,7 @@ export const recipientRouter = router({
});
}),
/**
* @internal
*/
// Internal endpoint for now.
rejectDocumentWithToken: procedure
.input(ZRejectDocumentWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {
+141 -156
View File
@@ -74,57 +74,46 @@ import {
} from './schema';
export const teamRouter = router({
// Internal endpoint for now.
getTeams: authenticatedProcedure.query(async ({ ctx }) => {
return await getTeams({ userId: ctx.user.id });
}),
// Todo: Public endpoint.
findTeams: authenticatedProcedure
// .meta({
// openapi: {
// method: 'GET',
// path: '/team',
// summary: 'Find teams',
// description: 'Find your teams based on a search criteria',
// tags: ['Teams'],
// },
// })
.input(ZFindTeamsQuerySchema)
getTeams: authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/team',
summary: 'Get teams',
description: 'Returns all teams that you are a member of',
tags: ['Teams'],
},
})
.input(z.void())
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await findTeams({
userId: ctx.user.id,
...input,
});
.query(async ({ ctx }) => {
return await getTeams({ userId: ctx.user.id });
}),
// Todo: Public endpoint.
getTeam: authenticatedProcedure
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}',
// summary: 'Get team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}',
summary: 'Get team',
tags: ['Teams'],
},
})
.input(ZGetTeamQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await getTeamById({ teamId: input.teamId, userId: ctx.user.id });
}),
// Todo: Public endpoint.
createTeam: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/create',
// summary: 'Create team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/create',
summary: 'Create team',
tags: ['Teams'],
},
})
.input(ZCreateTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -134,16 +123,15 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
updateTeam: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}',
// summary: 'Update team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}',
summary: 'Update team',
tags: ['Teams'],
},
})
.input(ZUpdateTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -153,16 +141,15 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeam: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/delete',
// summary: 'Delete team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/delete',
summary: 'Delete team',
tags: ['Teams'],
},
})
.input(ZDeleteTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -172,17 +159,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
leaveTeam: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/leave',
// summary: 'Leave a team',
// description: '',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/leave',
summary: 'Leave a team',
description: '',
tags: ['Teams'],
},
})
.input(ZLeaveTeamMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -192,17 +178,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
findTeamMemberInvites: authenticatedProcedure
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member/invite',
// summary: 'Find member invites',
// description: 'Returns pending team member invites',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member/invite',
summary: 'Find member invites',
description: 'Returns pending team member invites',
tags: ['Teams'],
},
})
.input(ZFindTeamMemberInvitesQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
@@ -212,17 +197,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
createTeamMemberInvites: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite',
// summary: 'Invite members',
// description: 'Send email invitations to users to join the team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite',
summary: 'Invite members',
description: 'Send email invitations to users to join the team',
tags: ['Teams'],
},
})
.input(ZCreateTeamMemberInvitesMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -233,17 +217,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
resendTeamMemberInvitation: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite/{invitationId}/resend',
// summary: 'Resend member invite',
// description: 'Resend an email invitation to a user to join the team',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite/{invitationId}/resend',
summary: 'Resend member invite',
description: 'Resend an email invitation to a user to join the team',
tags: ['Teams'],
},
})
.input(ZResendTeamMemberInvitationMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -254,17 +237,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeamMemberInvitations: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/invite/delete',
// summary: 'Delete member invite',
// description: 'Delete a pending team member invite',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/invite/delete',
summary: 'Delete member invite',
description: 'Delete a pending team member invite',
tags: ['Teams'],
},
})
.input(ZDeleteTeamMemberInvitationsMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -274,33 +256,31 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
getTeamMembers: authenticatedProcedure
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member',
// summary: 'Get members',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member',
summary: 'Get members',
tags: ['Teams'],
},
})
.input(ZGetTeamMembersQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await getTeamMembers({ teamId: input.teamId, userId: ctx.user.id });
}),
// Todo: Public endpoint.
findTeamMembers: authenticatedProcedure
// .meta({
// openapi: {
// method: 'GET',
// path: '/team/{teamId}/member/find',
// summary: 'Find members',
// description: 'Find team members based on a search criteria',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'GET',
path: '/team/{teamId}/member/find',
summary: 'Find members',
description: 'Find team members based on a search criteria',
tags: ['Teams'],
},
})
.input(ZFindTeamMembersQuerySchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
@@ -310,16 +290,15 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
updateTeamMember: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/{teamMemberId}',
// summary: 'Update member',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/{teamMemberId}',
summary: 'Update member',
tags: ['Teams'],
},
})
.input(ZUpdateTeamMemberMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -329,17 +308,16 @@ export const teamRouter = router({
});
}),
// Todo: Public endpoint.
deleteTeamMembers: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/member/delete',
// summary: 'Delete members',
// description: '',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/member/delete',
summary: 'Delete members',
description: '',
tags: ['Teams'],
},
})
.input(ZDeleteTeamMembersMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
@@ -349,6 +327,14 @@ export const teamRouter = router({
});
}),
// Todo: Refactor, seems to be a redundant endpoint.
findTeams: authenticatedProcedure.input(ZFindTeamsQuerySchema).query(async ({ input, ctx }) => {
return await findTeams({
userId: ctx.user.id,
...input,
});
}),
// Internal endpoint for now.
createTeamEmailVerification: authenticatedProcedure
// .meta({
@@ -388,17 +374,16 @@ export const teamRouter = router({
return await getTeamInvitations({ email: ctx.user.email });
}),
// Todo: Public endpoint.
updateTeamPublicProfile: authenticatedProcedure
// .meta({
// openapi: {
// method: 'POST',
// path: '/team/{teamId}/profile',
// summary: 'Update a team public profile',
// description: '',
// tags: ['Teams'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/team/{teamId}/profile',
summary: 'Update a team public profile',
description: '',
tags: ['Teams'],
},
})
.input(ZUpdateTeamPublicProfileMutationSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
+11 -5
View File
@@ -2,11 +2,17 @@ import { z } from 'zod';
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
import { ZUpdatePublicProfileMutationSchema } from '../profile-router/schema';
// Consider refactoring to use ZBaseTableSearchParamsSchema.
const GenericFindQuerySchema = z.object({
term: z.string().optional(),
page: z.number().min(1).optional(),
perPage: z.number().min(1).optional(),
});
/**
* Restrict team URLs schema.
*
@@ -116,17 +122,17 @@ export const ZFindTeamInvoicesQuerySchema = z.object({
teamId: z.number(),
});
export const ZFindTeamMemberInvitesQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindTeamMemberInvitesQuerySchema = GenericFindQuerySchema.extend({
teamId: z.number(),
});
export const ZFindTeamMembersQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindTeamMembersQuerySchema = GenericFindQuerySchema.extend({
teamId: z.number(),
});
export const ZFindTeamsQuerySchema = ZFindSearchParamsSchema;
export const ZFindTeamsQuerySchema = GenericFindQuerySchema;
export const ZFindTeamsPendingQuerySchema = ZFindSearchParamsSchema;
export const ZFindTeamsPendingQuerySchema = GenericFindQuerySchema;
export const ZGetTeamQuerySchema = z.object({
teamId: z.number(),
+28 -103
View File
@@ -4,50 +4,19 @@ import { z } from 'zod';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import {
ZGetDocumentWithDetailsByIdResponseSchema,
getDocumentWithDetailsById,
} from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import {
ZCreateDocumentFromDirectTemplateResponseSchema,
createDocumentFromDirectTemplate,
} from '@documenso/lib/server-only/template/create-document-from-direct-template';
import { createDocumentFromDirectTemplate } from '@documenso/lib/server-only/template/create-document-from-direct-template';
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
import {
ZCreateTemplateResponseSchema,
createTemplate,
} from '@documenso/lib/server-only/template/create-template';
import {
ZCreateTemplateDirectLinkResponseSchema,
createTemplateDirectLink,
} from '@documenso/lib/server-only/template/create-template-direct-link';
import { createTemplate } from '@documenso/lib/server-only/template/create-template';
import { createTemplateDirectLink } from '@documenso/lib/server-only/template/create-template-direct-link';
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
import { deleteTemplateDirectLink } from '@documenso/lib/server-only/template/delete-template-direct-link';
import {
ZDuplicateTemplateResponseSchema,
duplicateTemplate,
} from '@documenso/lib/server-only/template/duplicate-template';
import {
ZFindTemplatesResponseSchema,
findTemplates,
} from '@documenso/lib/server-only/template/find-templates';
import {
ZGetTemplateByIdResponseSchema,
getTemplateById,
} from '@documenso/lib/server-only/template/get-template-by-id';
import {
ZMoveTemplateToTeamResponseSchema,
moveTemplateToTeam,
} from '@documenso/lib/server-only/template/move-template-to-team';
import {
ZToggleTemplateDirectLinkResponseSchema,
toggleTemplateDirectLink,
} from '@documenso/lib/server-only/template/toggle-template-direct-link';
import {
ZUpdateTemplateSettingsResponseSchema,
updateTemplateSettings,
} from '@documenso/lib/server-only/template/update-template-settings';
import { duplicateTemplate } from '@documenso/lib/server-only/template/duplicate-template';
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { moveTemplateToTeam } from '@documenso/lib/server-only/template/move-template-to-team';
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
import { updateTemplateSettings } from '@documenso/lib/server-only/template/update-template-settings';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { Document } from '@documenso/prisma/client';
@@ -70,9 +39,6 @@ import {
} from './schema';
export const templateRouter = router({
/**
* @public
*/
findTemplates: authenticatedProcedure
.meta({
openapi: {
@@ -84,7 +50,7 @@ export const templateRouter = router({
},
})
.input(ZFindTemplatesQuerySchema)
.output(ZFindTemplatesResponseSchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
return await findTemplates({
userId: ctx.user.id,
@@ -92,9 +58,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
getTemplateById: authenticatedProcedure
.meta({
openapi: {
@@ -105,7 +68,7 @@ export const templateRouter = router({
},
})
.input(ZGetTemplateByIdQuerySchema)
.output(ZGetTemplateByIdResponseSchema)
.output(z.unknown())
.query(async ({ input, ctx }) => {
const { templateId, teamId } = input;
@@ -116,9 +79,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
createTemplate: authenticatedProcedure
.meta({
openapi: {
@@ -130,7 +90,7 @@ export const templateRouter = router({
},
})
.input(ZCreateTemplateMutationSchema)
.output(ZCreateTemplateResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { teamId, title, templateDocumentDataId } = input;
@@ -142,9 +102,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
updateTemplate: authenticatedProcedure
.meta({
openapi: {
@@ -155,7 +112,7 @@ export const templateRouter = router({
},
})
.input(ZUpdateTemplateSettingsMutationSchema)
.output(ZUpdateTemplateSettingsResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, data, meta } = input;
@@ -176,9 +133,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
duplicateTemplate: authenticatedProcedure
.meta({
openapi: {
@@ -189,7 +143,7 @@ export const templateRouter = router({
},
})
.input(ZDuplicateTemplateMutationSchema)
.output(ZDuplicateTemplateResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { teamId, templateId } = input;
@@ -200,9 +154,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
deleteTemplate: authenticatedProcedure
.meta({
openapi: {
@@ -213,7 +164,7 @@ export const templateRouter = router({
},
})
.input(ZDeleteTemplateMutationSchema)
.output(z.void())
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId } = input;
@@ -222,9 +173,6 @@ export const templateRouter = router({
await deleteTemplate({ userId, id: templateId, teamId });
}),
/**
* @public
*/
createDocumentFromTemplate: authenticatedProcedure
.meta({
openapi: {
@@ -236,9 +184,9 @@ export const templateRouter = router({
},
})
.input(ZCreateDocumentFromTemplateMutationSchema)
.output(ZGetDocumentWithDetailsByIdResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, recipients, distributeDocument } = input;
const { templateId, teamId, recipients } = input;
const limits = await getServerLimits({ email: ctx.user.email, teamId });
@@ -248,7 +196,7 @@ export const templateRouter = router({
const requestMetadata = extractNextApiRequestMetadata(ctx.req);
const document: Document = await createDocumentFromTemplate({
let document: Document = await createDocumentFromTemplate({
templateId,
teamId,
userId: ctx.user.id,
@@ -256,8 +204,8 @@ export const templateRouter = router({
requestMetadata,
});
if (distributeDocument) {
await sendDocument({
if (input.distributeDocument) {
document = await sendDocument({
documentId: document.id,
userId: ctx.user.id,
teamId,
@@ -269,16 +217,9 @@ export const templateRouter = router({
});
}
return getDocumentWithDetailsById({
documentId: document.id,
userId: ctx.user.id,
teamId,
});
return document;
}),
/**
* @public
*/
createDocumentFromDirectTemplate: maybeAuthenticatedProcedure
.meta({
openapi: {
@@ -290,7 +231,7 @@ export const templateRouter = router({
},
})
.input(ZCreateDocumentFromDirectTemplateMutationSchema)
.output(ZCreateDocumentFromDirectTemplateResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const {
directRecipientName,
@@ -321,9 +262,7 @@ export const templateRouter = router({
});
}),
/**
* @private
*/
// Internal endpoint for now.
setSigningOrderForTemplate: authenticatedProcedure
.input(ZSetSigningOrderForTemplateMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -339,9 +278,6 @@ export const templateRouter = router({
});
}),
/**
* @public
*/
createTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@@ -353,7 +289,7 @@ export const templateRouter = router({
},
})
.input(ZCreateTemplateDirectLinkMutationSchema)
.output(ZCreateTemplateDirectLinkResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId, directRecipientId } = input;
@@ -372,9 +308,6 @@ export const templateRouter = router({
return await createTemplateDirectLink({ userId, templateId, directRecipientId });
}),
/**
* @public
*/
deleteTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@@ -386,7 +319,7 @@ export const templateRouter = router({
},
})
.input(ZDeleteTemplateDirectLinkMutationSchema)
.output(z.void())
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId } = input;
@@ -395,9 +328,6 @@ export const templateRouter = router({
await deleteTemplateDirectLink({ userId, templateId });
}),
/**
* @public
*/
toggleTemplateDirectLink: authenticatedProcedure
.meta({
openapi: {
@@ -409,7 +339,7 @@ export const templateRouter = router({
},
})
.input(ZToggleTemplateDirectLinkMutationSchema)
.output(ZToggleTemplateDirectLinkResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, enabled } = input;
@@ -418,9 +348,6 @@ export const templateRouter = router({
return await toggleTemplateDirectLink({ userId, templateId, enabled });
}),
/**
* @public
*/
moveTemplateToTeam: authenticatedProcedure
.meta({
openapi: {
@@ -432,7 +359,7 @@ export const templateRouter = router({
},
})
.input(ZMoveTemplatesToTeamSchema)
.output(ZMoveTemplateToTeamResponseSchema)
.output(z.unknown())
.mutation(async ({ input, ctx }) => {
const { templateId, teamId } = input;
const userId = ctx.user.id;
@@ -444,9 +371,7 @@ export const templateRouter = router({
});
}),
/**
* @internal
*/
// Internal endpoint for now.
updateTemplateTypedSignatureSettings: authenticatedProcedure
.input(ZUpdateTemplateTypedSignatureSettingsMutationSchema)
.mutation(async ({ input, ctx }) => {
@@ -6,7 +6,7 @@ import {
ZDocumentActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
import {
DocumentDistributionMethod,
@@ -125,7 +125,7 @@ export const ZSetSigningOrderForTemplateMutationSchema = z.object({
signingOrder: z.nativeEnum(DocumentSigningOrder),
});
export const ZFindTemplatesQuerySchema = ZFindSearchParamsSchema.extend({
export const ZFindTemplatesQuerySchema = ZBaseTableSearchParamsSchema.extend({
teamId: z.number().optional(),
type: z.nativeEnum(TemplateType).optional(),
});
+23 -54
View File
@@ -1,73 +1,43 @@
import { TRPCError, initTRPC } from '@trpc/server';
import SuperJSON from 'superjson';
import type { OpenApiMeta } from 'trpc-openapi';
import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
import type { TrpcContext } from './context';
const t = initTRPC
.meta<OpenApiMeta>()
.context<TrpcContext>()
.create({
transformer: SuperJSON,
errorFormatter(opts) {
const { shape, error } = opts;
const t = initTRPC.context<TrpcContext>().create({
transformer: SuperJSON,
errorFormatter(opts) {
const { shape, error } = opts;
const originalError = error.cause;
const originalError = error.cause;
let data: Record<string, unknown> = shape.data;
let data: Record<string, unknown> = shape.data;
// Default unknown errors to 400, since if you're throwing an AppError it is expected
// that you already know what you're doing.
if (originalError instanceof AppError) {
data = {
...data,
appError: AppError.toJSON(originalError),
code: originalError.code,
httpStatus:
originalError.statusCode ??
genericErrorCodeToTrpcErrorCodeMap[originalError.code]?.status ??
400,
};
}
return {
...shape,
data,
if (originalError instanceof AppError) {
data = {
...data,
appError: AppError.toJSON(originalError),
code: originalError.code,
httpStatus:
originalError.statusCode ??
genericErrorCodeToTrpcErrorCodeMap[originalError.code]?.status ??
500,
};
},
});
}
return {
...shape,
data,
};
},
});
/**
* Middlewares
*/
export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
const authorizationHeader = ctx.req.headers.authorization;
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
if (authorizationHeader) {
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
const [token] = (authorizationHeader || '').split('Bearer ').filter((s) => s.length > 0);
if (!token) {
throw new Error('Token was not provided for authenticated middleware');
}
const apiToken = await getApiTokenByToken({ token });
return await next({
ctx: {
...ctx,
user: apiToken.user,
session: null,
source: 'api',
},
});
}
if (!ctx.session) {
throw new TRPCError({
code: 'UNAUTHORIZED',
@@ -80,7 +50,6 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
...ctx,
user: ctx.user,
session: ctx.session,
source: 'app',
},
});
});
@@ -1,14 +1,13 @@
import { Trans } from '@lingui/macro';
import { InfoIcon } from 'lucide-react';
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
import { DocumentEmailEvents } from '@documenso/lib/types/document-email';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { cn } from '../../lib/utils';
import { Checkbox } from '../../primitives/checkbox';
type Value = TDocumentEmailSettings;
type Value = Record<DocumentEmailEvents, boolean>;
type DocumentEmailCheckboxesProps = {
value: Value;
@@ -218,46 +217,6 @@ export const DocumentEmailCheckboxes = ({
</Tooltip>
</label>
</div>
<div className="flex flex-row items-center">
<Checkbox
id={DocumentEmailEvents.OwnerDocumentCompleted}
className="h-5 w-5"
checkClassName="dark:text-white text-primary"
checked={value.ownerDocumentCompleted}
onCheckedChange={(checked) =>
onChange({ ...value, [DocumentEmailEvents.OwnerDocumentCompleted]: Boolean(checked) })
}
/>
<label
className="text-muted-foreground ml-2 flex flex-row items-center text-sm"
htmlFor={DocumentEmailEvents.OwnerDocumentCompleted}
>
<Trans>Send document completed email to the owner</Trans>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<h2>
<strong>
<Trans>Document completed email to the owner</Trans>
</strong>
</h2>
<p>
<Trans>
This will be sent to the document owner once the document has been fully
completed.
</Trans>
</p>
</TooltipContent>
</Tooltip>
</label>
</div>
</div>
);
};
@@ -33,12 +33,12 @@ export const CheckboxField = ({ field }: CheckboxFieldProps) => {
parsedFieldMeta.values.map((item: { value: string; checked: boolean }, index: number) => (
<div key={index} className="flex items-center gap-x-1.5">
<Checkbox
className="dark:border-field-border h-3 w-3 bg-white"
className="h-3 w-3"
checkClassName="text-white"
id={`checkbox-${index}`}
checked={item.checked}
/>
<Label htmlFor={`checkbox-${index}`} className="text-xs font-normal text-black">
<Label htmlFor={`checkbox-${index}`} className="text-xs">
{item.value}
</Label>
</div>
@@ -34,12 +34,12 @@ export const RadioField = ({ field }: RadioFieldProps) => {
{parsedFieldMeta.values?.map((item, index) => (
<div key={index} className="flex items-center gap-x-1.5">
<RadioGroupItem
className="dark:border-field-border pointer-events-none h-3 w-3"
className="pointer-events-none h-3 w-3"
value={item.value}
id={`option-${index}`}
checked={item.checked}
/>
<Label htmlFor={`option-${index}`} className="text-xs font-normal text-black">
<Label htmlFor={`option-${index}`} className="text-xs">
{item.value}
</Label>
</div>
@@ -351,17 +351,10 @@ export const SignaturePad = ({
const newValue = event.target.value;
setTypedSignature(newValue);
if ($el.current) {
const ctx = $el.current.getContext('2d');
ctx?.clearRect(0, 0, $el.current.width, $el.current.height);
}
if (newValue.trim() !== '') {
onChange?.(newValue);
onValidityChange?.(true);
} else {
onChange?.(null);
onValidityChange?.(false);
}
};
@@ -461,7 +454,7 @@ export const SignaturePad = ({
return (
<div
className={cn('relative block select-none', containerClassName, {
className={cn('relative block', containerClassName, {
'pointer-events-none opacity-50': disabled,
})}
>
-2
View File
@@ -159,8 +159,6 @@
--border: 0 0% 27.9%;
--input: 0 0% 27.9%;
--field-border: 214.3 31.8% 91.4%;
--primary: 95.08 71.08% 67.45%;
--primary-foreground: 95.08 71.08% 10%;