diff --git a/apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx b/apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx index 1f1bb5317..5976d5261 100644 --- a/apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx +++ b/apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx @@ -131,7 +131,7 @@ export default function AdminDocumentsPage() {
setTerm(e.target.value)} /> diff --git a/packages/lib/constants/database.ts b/packages/lib/constants/database.ts new file mode 100644 index 000000000..947d1b0af --- /dev/null +++ b/packages/lib/constants/database.ts @@ -0,0 +1,6 @@ +/** + * The maximum value of a Postgres `INT4` (32-bit signed integer) column, which + * is what Prisma `Int` ID columns are stored as. Filtering an `Int` column by + * a larger value makes Prisma throw a conversion error at runtime. + */ +export const MAX_POSTGRES_INT = 2147483647; diff --git a/packages/lib/server-only/admin/admin-find-documents.ts b/packages/lib/server-only/admin/admin-find-documents.ts index 994422ba7..b13b58e9c 100644 --- a/packages/lib/server-only/admin/admin-find-documents.ts +++ b/packages/lib/server-only/admin/admin-find-documents.ts @@ -2,6 +2,7 @@ import { prisma } from '@documenso/prisma'; import { EnvelopeType, type Prisma } from '@prisma/client'; import { z } from 'zod'; +import { MAX_POSTGRES_INT } from '../../constants/database'; import type { FindResultResponse } from '../../types/search-params'; export interface AdminFindDocumentsOptions { @@ -10,7 +11,7 @@ export interface AdminFindDocumentsOptions { perPage?: number; } -const ZPositiveIntegerSchema = z.coerce.number().int().positive(); +const ZPositiveIntegerSchema = z.coerce.number().int().positive().max(MAX_POSTGRES_INT); const emptyResponse = { data: [], @@ -58,6 +59,40 @@ export const adminFindDocuments = async ({ query, page = 1, perPage = 10 }: Admi } } + if (query?.startsWith('recipient:')) { + const recipientQuery = query.slice('recipient:'.length).trim(); + + if (!recipientQuery) { + return emptyResponse; + } + + // Match admin-global-search semantics: only bare digit strings are ID + // lookups, so numeric-looking email fragments (1e3, 0x10, +40) stay text. + const isRecipientIdLookup = /^\d+$/.test(recipientQuery); + + const parsedRecipientId = ZPositiveIntegerSchema.safeParse(recipientQuery); + + termFilters = + isRecipientIdLookup && parsedRecipientId.success + ? { + recipients: { + some: { + id: parsedRecipientId.data, + }, + }, + } + : { + recipients: { + some: { + email: { + contains: recipientQuery, + mode: 'insensitive', + }, + }, + }, + }; + } + if (query && query?.startsWith('envelope_')) { termFilters = { id: { diff --git a/packages/lib/server-only/admin/admin-global-search.ts b/packages/lib/server-only/admin/admin-global-search.ts index 3a6e5fd40..b9f7b140a 100644 --- a/packages/lib/server-only/admin/admin-global-search.ts +++ b/packages/lib/server-only/admin/admin-global-search.ts @@ -1,9 +1,9 @@ import { prisma } from '@documenso/prisma'; import { EnvelopeType } from '@prisma/client'; -export const ADMIN_SEARCH_RESULTS_PER_TYPE = 5; +import { MAX_POSTGRES_INT } from '../../constants/database'; -const MAX_POSTGRES_INT = 2147483647; +export const ADMIN_SEARCH_RESULTS_PER_TYPE = 5; const GROUP_ORDER = ['document', 'user', 'organisation', 'team', 'recipient', 'subscription'] as const;