mirror of
https://github.com/documenso/documenso.git
synced 2026-06-22 20:32:07 +10:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fb2abffd1 | |||
| d1f34d0acd | |||
| daf01ac77b | |||
| 881e985b73 | |||
| 1455a7806a | |||
| b29da84678 |
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
@@ -23,7 +24,7 @@ import { useParams } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
const ZCreateFolderFormSchema = z.object({
|
||||
name: z.string().min(1, { message: 'Folder name is required' }),
|
||||
name: ZNameSchema,
|
||||
});
|
||||
|
||||
type TCreateFolderFormSchema = z.infer<typeof ZCreateFolderFormSchema>;
|
||||
@@ -65,7 +66,7 @@ export const FolderCreateDialog = ({ type, trigger, parentFolderId, ...props }:
|
||||
toast({
|
||||
description: t`Folder created successfully`,
|
||||
});
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
toast({
|
||||
title: t`Failed to create folder`,
|
||||
description: t`An unknown error occurred while creating the folder.`,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -23,8 +24,6 @@ import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { useOptionalCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type FolderUpdateDialogProps = {
|
||||
folder: TFolderWithSubfolders | null;
|
||||
isOpen: boolean;
|
||||
@@ -32,7 +31,7 @@ export type FolderUpdateDialogProps = {
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
export const ZUpdateFolderFormSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
name: ZNameSchema,
|
||||
visibility: z.nativeEnum(DocumentVisibility).optional(),
|
||||
});
|
||||
|
||||
@@ -40,7 +39,6 @@ export type TUpdateFolderFormSchema = z.infer<typeof ZUpdateFolderFormSchema>;
|
||||
|
||||
export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdateDialogProps) => {
|
||||
const { t } = useLingui();
|
||||
const team = useOptionalCurrentTeam();
|
||||
|
||||
const { toast } = useToast();
|
||||
const { mutateAsync: updateFolder } = trpc.folder.updateFolder.useMutation();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MAXIMUM_PASSKEYS } from '@documenso/lib/constants/auth';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -25,14 +26,13 @@ import { useForm } from 'react-hook-form';
|
||||
import { match } from 'ts-pattern';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type PasskeyCreateDialogProps = {
|
||||
trigger?: React.ReactNode;
|
||||
onSuccess?: () => void;
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
const ZCreatePasskeyFormSchema = z.object({
|
||||
passkeyName: z.string().min(3),
|
||||
passkeyName: ZNameSchema,
|
||||
});
|
||||
|
||||
type TCreatePasskeyFormSchema = z.infer<typeof ZCreatePasskeyFormSchema>;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZUpdateTeamEmailMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -19,16 +20,16 @@ import type * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRevalidator } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
import type { z } from 'zod';
|
||||
|
||||
export type TeamEmailUpdateDialogProps = {
|
||||
teamEmail: TeamEmail;
|
||||
trigger?: React.ReactNode;
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
const ZUpdateTeamEmailFormSchema = z.object({
|
||||
name: z.string().trim().min(1, { message: 'Please enter a valid name.' }),
|
||||
});
|
||||
const ZUpdateTeamEmailFormSchema = ZUpdateTeamEmailMutationSchema.pick({
|
||||
data: true,
|
||||
}).shape.data;
|
||||
|
||||
type TUpdateTeamEmailFormSchema = z.infer<typeof ZUpdateTeamEmailFormSchema>;
|
||||
|
||||
@@ -44,6 +45,7 @@ export const TeamEmailUpdateDialog = ({ teamEmail, trigger, ...props }: TeamEmai
|
||||
defaultValues: {
|
||||
name: teamEmail.name,
|
||||
},
|
||||
mode: 'onSubmit',
|
||||
});
|
||||
|
||||
const { mutateAsync: updateTeamEmail } = trpc.team.email.update.useMutation();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -15,8 +16,8 @@ import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
const ZEmailTransportFormSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
fromName: z.string().min(1),
|
||||
name: ZNameSchema,
|
||||
fromName: ZNameSchema,
|
||||
fromAddress: z.string().email(),
|
||||
type: z.enum(['SMTP_AUTH', 'SMTP_API', 'RESEND', 'MAILCHANNELS']),
|
||||
host: z.string().optional(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import communityCardsImage from '@documenso/assets/images/community-cards.png';
|
||||
import { authClient } from '@documenso/auth/client';
|
||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||
import { ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { env } from '@documenso/lib/utils/env';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { authClient } from '@documenso/auth/client';
|
||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { env } from '@documenso/lib/utils/env';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||
@@ -19,7 +20,6 @@ import { useRef } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SIGNUP_ERROR_MESSAGES } from '~/components/forms/signup';
|
||||
|
||||
export type ClaimAccountProps = {
|
||||
@@ -30,7 +30,7 @@ export type ClaimAccountProps = {
|
||||
|
||||
export const ZClaimAccountFormSchema = z
|
||||
.object({
|
||||
name: z.string().trim().min(1, { message: msg`Please enter a valid name.`.id }),
|
||||
name: ZNameSchema,
|
||||
email: zEmail().min(1),
|
||||
password: ZPasswordSchema,
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -29,7 +30,7 @@ export type SettingsSecurityPasskeyTableActionsProps = {
|
||||
};
|
||||
|
||||
const ZUpdatePasskeySchema = z.object({
|
||||
name: z.string(),
|
||||
name: ZNameSchema,
|
||||
});
|
||||
|
||||
type TUpdatePasskeySchema = z.infer<typeof ZUpdatePasskeySchema>;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ORGANISATION_MEMBER_ROLE_HIERARCHY } from '@documenso/lib/constants/org
|
||||
import { EXTENDED_ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations';
|
||||
import { TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams-translations';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TFindOrganisationGroupsResponse } from '@documenso/trpc/server/organisation-router/find-organisation-groups.types';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -28,7 +29,6 @@ import { useMemo, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Link } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { OrganisationGroupDeleteDialog } from '~/components/dialogs/organisation-group-delete-dialog';
|
||||
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
|
||||
import {
|
||||
@@ -36,7 +36,6 @@ import {
|
||||
OrganisationMembersMultiSelectCombobox,
|
||||
} from '~/components/general/organisation-members-multiselect-combobox';
|
||||
import { SettingsHeader } from '~/components/general/settings-header';
|
||||
|
||||
import type { Route } from './+types/o.$orgUrl.settings.groups.$id';
|
||||
|
||||
export default function OrganisationGroupSettingsPage({ params }: Route.ComponentProps) {
|
||||
@@ -113,7 +112,7 @@ export default function OrganisationGroupSettingsPage({ params }: Route.Componen
|
||||
}
|
||||
|
||||
const ZUpdateOrganisationGroupFormSchema = z.object({
|
||||
name: z.string().min(1, msg`Name is required`.id),
|
||||
name: ZNameSchema,
|
||||
organisationRole: z.nativeEnum(OrganisationMemberRole),
|
||||
memberIds: z.array(z.string()),
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
import MailChecker from 'mailchecker';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { env } from '../utils/env';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from './app';
|
||||
|
||||
export const SALT_ROUNDS = 12;
|
||||
|
||||
export const URL_PATTERN = /https?:\/\/|www\./i;
|
||||
|
||||
/**
|
||||
* Shared name schema that disallows URLs to prevent phishing via email rendering.
|
||||
*/
|
||||
export const ZNameSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, { message: 'Please enter a valid name.' })
|
||||
.max(255, { message: 'Name cannot be more than 255 characters.' })
|
||||
.refine((value) => !URL_PATTERN.test(value), {
|
||||
message: 'Name cannot contain URLs.',
|
||||
});
|
||||
|
||||
export const IDENTITY_PROVIDER_NAME: Record<string, string> = {
|
||||
DOCUMENSO: 'Documenso',
|
||||
GOOGLE: 'Google',
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { ZNameSchema } from './name';
|
||||
|
||||
describe('ZNameSchema', () => {
|
||||
describe('valid names', () => {
|
||||
it('accepts a normal name', () => {
|
||||
expect(ZNameSchema.safeParse('Example User')).toEqual({
|
||||
success: true,
|
||||
data: 'Example User',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts international characters', () => {
|
||||
expect(ZNameSchema.safeParse('Døcumensø Üser')).toEqual({
|
||||
success: true,
|
||||
data: 'Døcumensø Üser',
|
||||
});
|
||||
});
|
||||
|
||||
it('trims surrounding whitespace', () => {
|
||||
expect(ZNameSchema.safeParse(' Documenso User ')).toEqual({
|
||||
success: true,
|
||||
data: 'Documenso User',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts names at the minimum length', () => {
|
||||
expect(ZNameSchema.safeParse('DU')).toEqual({
|
||||
success: true,
|
||||
data: 'DU',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts names at the maximum length', () => {
|
||||
const name =
|
||||
'DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser Do';
|
||||
|
||||
expect(name.length).toBe(100);
|
||||
expect(ZNameSchema.safeParse(name)).toEqual({
|
||||
success: true,
|
||||
data: name,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('length validation', () => {
|
||||
it('rejects names shorter than 2 characters', () => {
|
||||
expect(ZNameSchema.safeParse('D')).toMatchObject({
|
||||
success: false,
|
||||
error: {
|
||||
issues: [{ message: 'Please enter a valid name.' }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects names longer than 100 characters', () => {
|
||||
const name =
|
||||
'DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser DocumensoUser Doc';
|
||||
|
||||
expect(name.length).toBe(101);
|
||||
expect(ZNameSchema.safeParse(name)).toMatchObject({
|
||||
success: false,
|
||||
error: {
|
||||
issues: [{ message: 'Name cannot be more than 100 characters.' }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects whitespace-only input after trim', () => {
|
||||
expect(ZNameSchema.safeParse(' ')).toMatchObject({
|
||||
success: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL validation', () => {
|
||||
it.each([
|
||||
'https://example.com',
|
||||
'http://example.com',
|
||||
'HTTPS://EXAMPLE.COM',
|
||||
'Northwind www.example.com',
|
||||
'www.example.com',
|
||||
])('rejects URLs in names: %s', (value) => {
|
||||
expect(ZNameSchema.safeParse(value)).toMatchObject({
|
||||
success: false,
|
||||
error: {
|
||||
issues: expect.arrayContaining([expect.objectContaining({ message: 'Name cannot contain URLs.' })]),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid character validation', () => {
|
||||
it.each([
|
||||
['NUL character', 'Acme\u0000Corp'],
|
||||
['zero-width space', 'Acme\u200bCorp'],
|
||||
['bidi override', 'Acme\u202eCorp'],
|
||||
['byte order mark', 'Acme\ufeffCorp'],
|
||||
])('rejects names containing a %s', (_label, value) => {
|
||||
expect(ZNameSchema.safeParse(value)).toMatchObject({
|
||||
success: false,
|
||||
error: {
|
||||
issues: expect.arrayContaining([expect.objectContaining({ message: 'Name contains invalid characters.' })]),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects literal \\u escape sequences stored as text', () => {
|
||||
expect(ZNameSchema.safeParse(String.raw`Acme\u200bCorp`)).toMatchObject({
|
||||
success: false,
|
||||
error: {
|
||||
issues: expect.arrayContaining([expect.objectContaining({ message: 'Name contains invalid characters.' })]),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import { hasInvalidTextCharacters } from '../utils/zod';
|
||||
|
||||
export const URL_PATTERN = /https?:\/\/|www\./i;
|
||||
|
||||
/**
|
||||
* Shared name schema that disallows URLs to prevent phishing via email rendering,
|
||||
* and invisible/control characters that render as empty or break the UI.
|
||||
*/
|
||||
export const ZNameSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(2, { message: 'Please enter a valid name.' })
|
||||
.max(100, { message: 'Name cannot be more than 100 characters.' })
|
||||
.refine((value) => !URL_PATTERN.test(value), {
|
||||
message: 'Name cannot contain URLs.',
|
||||
})
|
||||
.refine((value) => !hasInvalidTextCharacters(value), {
|
||||
message: 'Name contains invalid characters.',
|
||||
});
|
||||
|
||||
export type TName = z.infer<typeof ZNameSchema>;
|
||||
@@ -13,6 +13,54 @@ const EMAIL_REGEX =
|
||||
|
||||
const DEFAULT_EMAIL_MESSAGE = 'Invalid email address';
|
||||
|
||||
/**
|
||||
* Code point ranges for control and invisible/formatting characters that render
|
||||
* as empty or break the UI (e.g. NUL, zero-width spaces, bidi overrides, BOM).
|
||||
*/
|
||||
const INVALID_TEXT_CODE_POINT_RANGES: [number, number][] = [
|
||||
[0x0000, 0x001f],
|
||||
[0x007f, 0x009f],
|
||||
[0x00ad, 0x00ad],
|
||||
[0x034f, 0x034f],
|
||||
[0x061c, 0x061c],
|
||||
[0x180e, 0x180e],
|
||||
[0x200b, 0x200f],
|
||||
[0x2028, 0x202e],
|
||||
[0x2060, 0x206f],
|
||||
[0xfeff, 0xfeff],
|
||||
[0xd800, 0xdfff],
|
||||
];
|
||||
|
||||
/**
|
||||
* The same characters expressed as literal "\\uXXXX" text, which can be stored
|
||||
* verbatim (e.g. the 6 characters `\`, `u`, `0`, `0`, `0`, `0`) and still break
|
||||
* rendering downstream. This regex is pure ASCII so it is safe to inline.
|
||||
*/
|
||||
const INVALID_TEXT_ESCAPE_SEQUENCE_REGEX =
|
||||
/\\u(?:00[0-1][0-9a-f]|007f|00[89][0-9a-f]|00ad|034f|061c|180e|200[b-f]|202[8-e]|206[0-9a-f]|feff)/iu;
|
||||
|
||||
export const hasInvalidTextCharacters = (value: string) => {
|
||||
if (INVALID_TEXT_ESCAPE_SEQUENCE_REGEX.test(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const char of value) {
|
||||
const codePoint = char.codePointAt(0);
|
||||
|
||||
if (codePoint === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const isInvalid = INVALID_TEXT_CODE_POINT_RANGES.some(([start, end]) => codePoint >= start && codePoint <= end);
|
||||
|
||||
if (isInvalid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Zod email schema using an RFC 5322 compliant regex.
|
||||
*
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZOrganisationNameSchema } from '../organisation-router/create-organisation.types';
|
||||
|
||||
export const ZCreateAdminOrganisationRequestSchema = z.object({
|
||||
ownerUserId: z.number(),
|
||||
data: z.object({
|
||||
name: ZOrganisationNameSchema,
|
||||
name: ZNameSchema,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { ZClaimFlagsSchema, ZRateLimitArraySchema } from '@documenso/lib/types/subscription';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateSubscriptionClaimRequestSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
name: ZNameSchema,
|
||||
teamCount: z.number().int().min(0),
|
||||
memberCount: z.number().int().min(0),
|
||||
envelopeItemCount: z.number().int().min(1),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateUserRequestSchema = z.object({
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ZEmailTransportConfigSchema } from '@documenso/lib/server-only/email/email-transport-config';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateEmailTransportRequestSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
fromName: z.string().min(1),
|
||||
name: ZNameSchema,
|
||||
fromName: ZNameSchema,
|
||||
fromAddress: z.string().email(),
|
||||
config: ZEmailTransportConfigSchema,
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ZSmtpApiConfigSchema,
|
||||
ZSmtpAuthConfigSchema,
|
||||
} from '@documenso/lib/server-only/email/email-transport-config';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Reuses the canonical transport config schemas, but relaxes the secret field so
|
||||
@@ -21,8 +22,8 @@ const ZUpdateConfigSchema = z.discriminatedUnion('type', [
|
||||
export const ZUpdateEmailTransportRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
data: z.object({
|
||||
name: z.string().min(1),
|
||||
fromName: z.string().min(1),
|
||||
name: ZNameSchema,
|
||||
fromName: ZNameSchema,
|
||||
fromAddress: z.string().email(),
|
||||
config: ZUpdateConfigSchema,
|
||||
}),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZOrganisationNameSchema } from '../organisation-router/create-organisation.types';
|
||||
import { ZTeamUrlSchema } from '../team-router/schema';
|
||||
import { ZCreateSubscriptionClaimRequestSchema } from './create-subscription-claim.types';
|
||||
|
||||
export const ZUpdateAdminOrganisationRequestSchema = z.object({
|
||||
organisationId: z.string(),
|
||||
data: z.object({
|
||||
name: ZOrganisationNameSchema.optional(),
|
||||
name: ZNameSchema.optional(),
|
||||
url: ZTeamUrlSchema.optional(),
|
||||
claims: ZCreateSubscriptionClaimRequestSchema.pick({
|
||||
teamCount: true,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { Role } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZUpdateUserRequestSchema = z.object({
|
||||
id: z.number().min(1),
|
||||
name: z.string().nullish(),
|
||||
name: ZNameSchema.nullish(),
|
||||
email: zEmail().optional(),
|
||||
roles: z.array(z.nativeEnum(Role)).optional(),
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateApiTokenRequestSchema = z.object({
|
||||
teamId: z.number(),
|
||||
tokenName: z.string().min(3, { message: 'The token name should be 3 characters or longer' }),
|
||||
tokenName: ZNameSchema,
|
||||
expirationDate: z.string().nullable(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { ZRegistrationResponseJSONSchema } from '@documenso/lib/types/webauthn';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreatePasskeyRequestSchema = z.object({
|
||||
passkeyName: z.string().trim().min(1),
|
||||
passkeyName: ZNameSchema,
|
||||
verificationResponse: ZRegistrationResponseJSONSchema,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZUpdatePasskeyRequestSchema = z.object({
|
||||
passkeyId: z.string().trim().min(1),
|
||||
name: z.string().trim().min(1),
|
||||
name: ZNameSchema,
|
||||
});
|
||||
|
||||
export const ZUpdatePasskeyResponseSchema = z.void();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZCreateOrganisationEmailRequestSchema = z.object({
|
||||
emailDomainId: z.string(),
|
||||
emailName: z.string().min(1).max(100),
|
||||
emailName: ZNameSchema,
|
||||
email: zEmail().toLowerCase(),
|
||||
|
||||
// This does not need to be validated to be part of the domain.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ZFolderTypeSchema } from '@documenso/lib/types/folder-type';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { DocumentVisibility } from '@documenso/prisma/generated/types';
|
||||
import FolderSchema from '@documenso/prisma/generated/zod/modelSchema/FolderSchema';
|
||||
@@ -42,7 +43,7 @@ const ZFolderParentIdSchema = z
|
||||
.describe('The folder ID to place this folder within. Leave empty to place folder at the root level.');
|
||||
|
||||
export const ZCreateFolderRequestSchema = z.object({
|
||||
name: z.string(),
|
||||
name: ZNameSchema,
|
||||
parentId: ZFolderParentIdSchema.optional(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
@@ -52,7 +53,7 @@ export const ZCreateFolderResponseSchema = ZFolderSchema;
|
||||
export const ZUpdateFolderRequestSchema = z.object({
|
||||
folderId: z.string().describe('The ID of the folder to update'),
|
||||
data: z.object({
|
||||
name: z.string().optional().describe('The name of the folder'),
|
||||
name: ZNameSchema.optional().describe('The name of the folder'),
|
||||
parentId: ZFolderParentIdSchema.optional().nullable(),
|
||||
visibility: z.nativeEnum(DocumentVisibility).optional().describe('The visibility of the folder'),
|
||||
pinned: z.boolean().optional().describe('Whether the folder should be pinned'),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { OrganisationMemberRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -14,7 +15,7 @@ import { z } from 'zod';
|
||||
export const ZCreateOrganisationGroupRequestSchema = z.object({
|
||||
organisationId: z.string(),
|
||||
organisationRole: z.nativeEnum(OrganisationMemberRole),
|
||||
name: z.string().max(100),
|
||||
name: ZNameSchema,
|
||||
memberIds: z.array(z.string()),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
// export const createOrganisationMeta: TrpcOpenApiMeta = {
|
||||
@@ -10,13 +11,8 @@ import { z } from 'zod';
|
||||
// },
|
||||
// };
|
||||
|
||||
export const ZOrganisationNameSchema = z
|
||||
.string()
|
||||
.min(3, { message: 'Minimum 3 characters' })
|
||||
.max(50, { message: 'Maximum 50 characters' });
|
||||
|
||||
export const ZCreateOrganisationRequestSchema = z.object({
|
||||
name: ZOrganisationNameSchema,
|
||||
name: ZNameSchema,
|
||||
priceId: z.string().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { OrganisationMemberRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -14,7 +15,7 @@ import { z } from 'zod';
|
||||
|
||||
export const ZUpdateOrganisationGroupRequestSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullable().optional(),
|
||||
name: ZNameSchema.nullable().optional(),
|
||||
organisationRole: z.nativeEnum(OrganisationMemberRole).optional(),
|
||||
memberIds: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZFindUserSecurityAuditLogsSchema = z.object({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
import { ZTeamNameSchema, ZTeamUrlSchema } from './schema';
|
||||
import { ZTeamUrlSchema } from './schema';
|
||||
|
||||
// export const createTeamMeta: TrpcOpenApiMeta = {
|
||||
// openapi: {
|
||||
@@ -13,7 +14,7 @@ import { ZTeamNameSchema, ZTeamUrlSchema } from './schema';
|
||||
|
||||
export const ZCreateTeamRequestSchema = z.object({
|
||||
organisationId: z.string(),
|
||||
teamName: ZTeamNameSchema,
|
||||
teamName: ZNameSchema,
|
||||
teamUrl: ZTeamUrlSchema,
|
||||
inheritMembers: z
|
||||
.boolean()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { URL_PATTERN, ZNameSchema } from '@documenso/lib/constants/auth';
|
||||
import { PROTECTED_TEAM_URLS } from '@documenso/lib/constants/teams';
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
@@ -32,15 +32,6 @@ export const ZTeamUrlSchema = z
|
||||
message: 'This URL is already in use.',
|
||||
});
|
||||
|
||||
export const ZTeamNameSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, { message: 'Team name must be at least 3 characters long.' })
|
||||
.max(30, { message: 'Team name must not exceed 30 characters.' })
|
||||
.refine((value) => !URL_PATTERN.test(value), {
|
||||
message: 'Team name cannot contain URLs.',
|
||||
});
|
||||
|
||||
export const ZCreateTeamEmailVerificationMutationSchema = z.object({
|
||||
teamId: z.number(),
|
||||
name: ZNameSchema,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ZNameSchema } from '@documenso/lib/types/name';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZTeamNameSchema, ZTeamUrlSchema } from './schema';
|
||||
import { ZTeamUrlSchema } from './schema';
|
||||
|
||||
export const MAX_PROFILE_BIO_LENGTH = 256;
|
||||
|
||||
@@ -19,7 +20,7 @@ export const MAX_PROFILE_BIO_LENGTH = 256;
|
||||
export const ZUpdateTeamRequestSchema = z.object({
|
||||
teamId: z.number(),
|
||||
data: z.object({
|
||||
name: ZTeamNameSchema.optional(),
|
||||
name: ZNameSchema.optional(),
|
||||
url: ZTeamUrlSchema.optional(),
|
||||
profileBio: z
|
||||
.string()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPrivateUrl } from '@documenso/lib/server-only/webhooks/is-private-url';
|
||||
import { URL_PATTERN } from '@documenso/lib/types/name';
|
||||
import { WebhookTriggerEvents } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -7,6 +8,13 @@ export const ZWebhookUrlSchema = z
|
||||
.url()
|
||||
.refine((url) => !isPrivateUrl(url), {
|
||||
message: 'Webhook URL cannot point to a private or loopback address',
|
||||
})
|
||||
/*
|
||||
* Without this, values like "foo: bar" would be valid URLs.
|
||||
* Keep the same error message as the zod url() validator.
|
||||
*/
|
||||
.refine((value) => URL_PATTERN.test(value), {
|
||||
message: 'Invalid url',
|
||||
});
|
||||
|
||||
export const ZCreateWebhookRequestSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user