Merge branch 'main' into feat/public-api

This commit is contained in:
Lucas Smith
2024-02-09 16:00:40 +11:00
committed by GitHub
401 changed files with 20803 additions and 2358 deletions

View File

@ -1,8 +1,16 @@
export const IS_APP_MARKETING = process.env.NEXT_PUBLIC_PROJECT === 'marketing';
export const IS_APP_WEB = process.env.NEXT_PUBLIC_PROJECT === 'web';
export const IS_BILLING_ENABLED = process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true';
export const APP_DOCUMENT_UPLOAD_SIZE_LIMIT =
Number(process.env.NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT) || 50;
export const APP_FOLDER = IS_APP_MARKETING ? 'marketing' : 'web';
export const APP_BASE_URL = IS_APP_WEB
? process.env.NEXT_PUBLIC_WEBAPP_URL
: process.env.NEXT_PUBLIC_MARKETING_URL;
export const WEBAPP_BASE_URL = process.env.NEXT_PUBLIC_WEBAPP_URL ?? 'http://localhost:3000';
export const MARKETING_BASE_URL = process.env.NEXT_PUBLIC_MARKETING_URL ?? 'http://localhost:3001';

View File

@ -1 +1,25 @@
import { IdentityProvider, UserSecurityAuditLogType } from '@documenso/prisma/client';
export const SALT_ROUNDS = 12;
export const IDENTITY_PROVIDER_NAME: { [key in IdentityProvider]: string } = {
[IdentityProvider.DOCUMENSO]: 'Documenso',
[IdentityProvider.GOOGLE]: 'Google',
};
export const IS_GOOGLE_SSO_ENABLED = Boolean(
process.env.NEXT_PRIVATE_GOOGLE_CLIENT_ID && process.env.NEXT_PRIVATE_GOOGLE_CLIENT_SECRET,
);
export const USER_SECURITY_AUDIT_LOG_MAP: { [key in UserSecurityAuditLogType]: string } = {
[UserSecurityAuditLogType.ACCOUNT_SSO_LINK]: 'Linked account to SSO',
[UserSecurityAuditLogType.ACCOUNT_PROFILE_UPDATE]: 'Profile updated',
[UserSecurityAuditLogType.AUTH_2FA_DISABLE]: '2FA Disabled',
[UserSecurityAuditLogType.AUTH_2FA_ENABLE]: '2FA Enabled',
[UserSecurityAuditLogType.PASSWORD_RESET]: 'Password reset',
[UserSecurityAuditLogType.PASSWORD_UPDATE]: 'Password updated',
[UserSecurityAuditLogType.SIGN_OUT]: 'Signed Out',
[UserSecurityAuditLogType.SIGN_IN]: 'Signed In',
[UserSecurityAuditLogType.SIGN_IN_FAIL]: 'Sign in attempt failed',
[UserSecurityAuditLogType.SIGN_IN_2FA_FAIL]: 'Sign in 2FA attempt failed',
};

View File

@ -0,0 +1,11 @@
export enum STRIPE_CUSTOMER_TYPE {
INDIVIDUAL = 'individual',
TEAM = 'team',
}
export enum STRIPE_PLAN_TYPE {
TEAM = 'team',
COMMUNITY = 'community',
}
export const TEAM_BILLING_DOMAIN = 'billing.team.documenso.com';

View File

@ -1 +1,25 @@
export const DOCUMENSO_ENCRYPTION_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_KEY;
export const DOCUMENSO_ENCRYPTION_SECONDARY_KEY = process.env.NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY;
if (typeof window === 'undefined') {
if (!DOCUMENSO_ENCRYPTION_KEY || !DOCUMENSO_ENCRYPTION_SECONDARY_KEY) {
throw new Error('Missing DOCUMENSO_ENCRYPTION_KEY or DOCUMENSO_ENCRYPTION_SECONDARY_KEY keys');
}
if (DOCUMENSO_ENCRYPTION_KEY === DOCUMENSO_ENCRYPTION_SECONDARY_KEY) {
throw new Error(
'DOCUMENSO_ENCRYPTION_KEY and DOCUMENSO_ENCRYPTION_SECONDARY_KEY cannot be equal',
);
}
}
if (DOCUMENSO_ENCRYPTION_KEY === 'CAFEBABE') {
console.warn('*********************************************************************');
console.warn('*');
console.warn('*');
console.warn('Please change the encryption key from the default value of "CAFEBABE"');
console.warn('*');
console.warn('*');
console.warn('*********************************************************************');
}

View File

@ -0,0 +1,79 @@
import { DateTime } from 'luxon';
import { DEFAULT_DOCUMENT_TIME_ZONE } from './time-zones';
export const DEFAULT_DOCUMENT_DATE_FORMAT = 'yyyy-MM-dd hh:mm a';
export const DATE_FORMATS = [
{
key: 'yyyy-MM-dd_hh:mm_a',
label: 'YYYY-MM-DD HH:mm a',
value: DEFAULT_DOCUMENT_DATE_FORMAT,
},
{
key: 'YYYYMMDD',
label: 'YYYY-MM-DD',
value: 'YYYY-MM-DD',
},
{
key: 'DDMMYYYY',
label: 'DD/MM/YYYY',
value: 'dd/MM/yyyy hh:mm a',
},
{
key: 'MMDDYYYY',
label: 'MM/DD/YYYY',
value: 'MM/dd/yyyy hh:mm a',
},
{
key: 'YYYYMMDDHHmm',
label: 'YYYY-MM-DD HH:mm',
value: 'yyyy-MM-dd HH:mm',
},
{
key: 'YYMMDD',
label: 'YY-MM-DD',
value: 'yy-MM-dd hh:mm a',
},
{
key: 'YYYYMMDDhhmmss',
label: 'YYYY-MM-DD HH:mm:ss',
value: 'yyyy-MM-dd HH:mm:ss',
},
{
key: 'MonthDateYear',
label: 'Month Date, Year',
value: 'MMMM dd, yyyy hh:mm a',
},
{
key: 'DayMonthYear',
label: 'Day, Month Year',
value: 'EEEE, MMMM dd, yyyy hh:mm a',
},
{
key: 'ISO8601',
label: 'ISO 8601',
value: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
},
];
export const convertToLocalSystemFormat = (
customText: string,
dateFormat: string | null = DEFAULT_DOCUMENT_DATE_FORMAT,
timeZone: string | null = DEFAULT_DOCUMENT_TIME_ZONE,
): string => {
const coalescedDateFormat = dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT;
const coalescedTimeZone = timeZone ?? DEFAULT_DOCUMENT_TIME_ZONE;
const parsedDate = DateTime.fromFormat(customText, coalescedDateFormat, {
zone: coalescedTimeZone,
});
if (!parsedDate.isValid) {
return 'Invalid date';
}
const formattedDate = parsedDate.toLocal().toFormat(coalescedDateFormat);
return formattedDate;
};

View File

@ -17,6 +17,7 @@ export const FEATURE_FLAG_POLL_INTERVAL = 30000;
*/
export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
app_billing: process.env.NEXT_PUBLIC_FEATURE_BILLING_ENABLED === 'true',
app_teams: true,
marketing_header_single_player_mode: false,
} as const;

View File

@ -1,2 +1,3 @@
export const SETTINGS_PAGE_SHORTCUT = 'N+S';
export const DOCUMENTS_PAGE_SHORTCUT = 'N+D';
export const TEMPLATES_PAGE_SHORTCUT = 'N+T';

View File

@ -0,0 +1,26 @@
import { RecipientRole } from '@documenso/prisma/client';
export const RECIPIENT_ROLES_DESCRIPTION: {
[key in RecipientRole]: { actionVerb: string; progressiveVerb: string; roleName: string };
} = {
[RecipientRole.APPROVER]: {
actionVerb: 'Approve',
progressiveVerb: 'Approving',
roleName: 'Approver',
},
[RecipientRole.CC]: {
actionVerb: 'CC',
progressiveVerb: 'CC',
roleName: 'CC',
},
[RecipientRole.SIGNER]: {
actionVerb: 'Sign',
progressiveVerb: 'Signing',
roleName: 'Signer',
},
[RecipientRole.VIEWER]: {
actionVerb: 'View',
progressiveVerb: 'Viewing',
roleName: 'Viewer',
},
};

View File

@ -0,0 +1,103 @@
import { TeamMemberRole } from '@documenso/prisma/client';
export const TEAM_URL_ROOT_REGEX = new RegExp('^/t/[^/]+$');
export const TEAM_URL_REGEX = new RegExp('^/t/[^/]+');
export const TEAM_MEMBER_ROLE_MAP: Record<keyof typeof TeamMemberRole, string> = {
ADMIN: 'Admin',
MANAGER: 'Manager',
MEMBER: 'Member',
};
export const TEAM_MEMBER_ROLE_PERMISSIONS_MAP = {
/**
* Includes permissions to:
* - Manage team members
* - Manage team settings, changing name, url, etc.
*/
MANAGE_TEAM: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER],
MANAGE_BILLING: [TeamMemberRole.ADMIN],
DELETE_TEAM_TRANSFER_REQUEST: [TeamMemberRole.ADMIN],
} satisfies Record<string, TeamMemberRole[]>;
/**
* A hierarchy of team member roles to determine which role has higher permission than another.
*/
export const TEAM_MEMBER_ROLE_HIERARCHY = {
[TeamMemberRole.ADMIN]: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER, TeamMemberRole.MEMBER],
[TeamMemberRole.MANAGER]: [TeamMemberRole.MANAGER, TeamMemberRole.MEMBER],
[TeamMemberRole.MEMBER]: [TeamMemberRole.MEMBER],
} satisfies Record<TeamMemberRole, TeamMemberRole[]>;
export const PROTECTED_TEAM_URLS = [
'403',
'404',
'500',
'502',
'503',
'504',
'about',
'account',
'admin',
'administrator',
'api',
'app',
'archive',
'auth',
'backup',
'config',
'configure',
'contact',
'contact-us',
'copyright',
'crime',
'criminal',
'dashboard',
'docs',
'documenso',
'documentation',
'document',
'documents',
'error',
'exploit',
'exploitation',
'exploiter',
'feedback',
'finance',
'forgot-password',
'fraud',
'fraudulent',
'hack',
'hacker',
'harassment',
'help',
'helpdesk',
'illegal',
'internal',
'legal',
'login',
'logout',
'maintenance',
'malware',
'newsletter',
'policy',
'privacy',
'profile',
'public',
'reset-password',
'scam',
'scammer',
'settings',
'setup',
'sign',
'signin',
'signout',
'signup',
'spam',
'support',
'system',
'team',
'terms',
'virus',
'webhook',
];

View File

@ -0,0 +1,44 @@
import { rawTimeZones, timeZonesNames } from '@vvo/tzdb';
export const TIME_ZONE_DATA = rawTimeZones;
export const DEFAULT_DOCUMENT_TIME_ZONE = 'Etc/UTC';
export type TimeZone = {
name: string;
rawOffsetInMinutes: number;
};
export const minutesToHours = (minutes: number): string => {
const hours = Math.abs(Math.floor(minutes / 60));
const min = Math.abs(minutes % 60);
const sign = minutes >= 0 ? '+' : '-';
return `${sign}${String(hours).padStart(2, '0')}:${String(min).padStart(2, '0')}`;
};
const getGMTOffsets = (timezones: TimeZone[]): string[] => {
const gmtOffsets: string[] = [];
for (const timezone of timezones) {
const offsetValue = minutesToHours(timezone.rawOffsetInMinutes);
const gmtText = `(${offsetValue})`;
gmtOffsets.push(`${timezone.name} ${gmtText}`);
}
return gmtOffsets;
};
export const splitTimeZone = (input: string | null): string => {
if (input === null) {
return '';
}
const [timeZone] = input.split('(');
return timeZone.trim();
};
export const TIME_ZONES_FULL = getGMTOffsets(TIME_ZONE_DATA);
export const TIME_ZONES = ['Etc/UTC', ...timeZonesNames];