mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
feat: migrate nextjs to rr7
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { type Field, FieldType } from '@documenso/prisma/client';
|
||||
import { type Field, FieldType } from '@prisma/client';
|
||||
|
||||
import { ZFieldMetaSchema } from '../types/field-meta';
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { WEBAPP_BASE_URL } from '../constants/app';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
import { PASSKEY_TIMEOUT } from '../constants/auth';
|
||||
|
||||
/**
|
||||
* Extracts common fields to identify the RP (relying party)
|
||||
*/
|
||||
export const getAuthenticatorOptions = () => {
|
||||
const webAppBaseUrl = new URL(WEBAPP_BASE_URL);
|
||||
const webAppBaseUrl = new URL(NEXT_PUBLIC_WEBAPP_URL());
|
||||
const rpId = webAppBaseUrl.hostname;
|
||||
|
||||
return {
|
||||
rpName: 'Documenso',
|
||||
rpId,
|
||||
origin: WEBAPP_BASE_URL,
|
||||
origin: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
timeout: PASSKEY_TIMEOUT,
|
||||
};
|
||||
};
|
||||
|
||||
9
packages/lib/utils/avatars.ts
Normal file
9
packages/lib/utils/avatars.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
|
||||
export const formatAvatarUrl = (imageId?: string | null) => {
|
||||
if (!imageId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return `${NEXT_PUBLIC_WEBAPP_URL()}/api/avatar/${imageId}`;
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Subscription } from '.prisma/client';
|
||||
import { SubscriptionStatus } from '.prisma/client';
|
||||
import type { Subscription } from '@prisma/client';
|
||||
import { SubscriptionStatus } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* Returns true if there is a subscription that is active and is one of the provided price IDs.
|
||||
|
||||
17
packages/lib/utils/debugger.ts
Normal file
17
packages/lib/utils/debugger.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export const appLog = (context: string, ...args: Parameters<typeof console.log>) => {
|
||||
// if (env('NEXT_DEBUG') === 'true') {
|
||||
console.log(`[${context}]: ${args[0]}`, ...args.slice(1));
|
||||
// }
|
||||
};
|
||||
|
||||
export class AppLogger {
|
||||
public context: string;
|
||||
|
||||
constructor(context: string) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public log(...args: Parameters<typeof console.log>) {
|
||||
appLog(this.context, ...args);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,9 @@
|
||||
import type { I18n } from '@lingui/core';
|
||||
import { msg } from '@lingui/macro';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import type { DocumentAuditLog, DocumentMeta, Field, Recipient } from '@prisma/client';
|
||||
import { RecipientRole } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import type { DocumentAuditLog, DocumentMeta, Field, Recipient } from '@documenso/prisma/client';
|
||||
import { RecipientRole } from '@documenso/prisma/client';
|
||||
|
||||
import type {
|
||||
TDocumentAuditLog,
|
||||
TDocumentAuditLogDocumentMetaDiffSchema,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Document, Recipient } from '@documenso/prisma/client';
|
||||
import type { Document, Recipient } from '@prisma/client';
|
||||
|
||||
import type {
|
||||
TDocumentAuthOptions,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { DocumentVisibility, TeamMemberRole } from '@prisma/client';
|
||||
|
||||
export const determineDocumentVisibility = (
|
||||
globalVisibility: DocumentVisibility | null | undefined,
|
||||
|
||||
20
packages/lib/utils/env.ts
Normal file
20
packages/lib/utils/env.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference types="@documenso/tsconfig/process-env.d.ts" />
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__ENV__?: Record<string, string | undefined>;
|
||||
}
|
||||
}
|
||||
|
||||
type EnvironmentVariable = keyof NodeJS.ProcessEnv;
|
||||
|
||||
export const env = (variable: EnvironmentVariable | (string & object)): string | undefined => {
|
||||
if (typeof window !== 'undefined' && typeof window.__ENV__ === 'object') {
|
||||
return window.__ENV__[variable];
|
||||
}
|
||||
|
||||
return process?.env?.[variable];
|
||||
};
|
||||
|
||||
export const createPublicEnv = () =>
|
||||
Object.fromEntries(Object.entries(process.env).filter(([key]) => key.startsWith('NEXT_PUBLIC_')));
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
import type { Field } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* Sort the fields by the Y position on the document.
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';
|
||||
|
||||
import type { I18n, MessageDescriptor } from '@lingui/core';
|
||||
import { i18n } from '@lingui/core';
|
||||
|
||||
import { IS_APP_WEB, IS_APP_WEB_I18N_ENABLED } from '../constants/app';
|
||||
import type { I18nLocaleData, SupportedLanguageCodes } from '../constants/i18n';
|
||||
import { APP_I18N_OPTIONS } from '../constants/i18n';
|
||||
import { env } from './env';
|
||||
|
||||
export async function dynamicActivate(i18nInstance: I18n, locale: string) {
|
||||
const extension = process.env.NODE_ENV === 'development' ? 'po' : 'js';
|
||||
export async function dynamicActivate(locale: string) {
|
||||
const extension = env('NODE_ENV') === 'development' ? 'po' : 'mjs';
|
||||
|
||||
const { messages } = await import(`../translations/${locale}/web.${extension}`);
|
||||
|
||||
i18nInstance.loadAndActivate({ locale, messages });
|
||||
i18n.loadAndActivate({ locale, messages });
|
||||
}
|
||||
|
||||
const parseLanguageFromLocale = (locale: string): SupportedLanguageCodes | null => {
|
||||
@ -28,25 +27,6 @@ const parseLanguageFromLocale = (locale: string): SupportedLanguageCodes | null
|
||||
return foundSupportedLanguage;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract the language if supported from the cookies header.
|
||||
*
|
||||
* Returns `null` if not supported or not found.
|
||||
*/
|
||||
export const extractLocaleDataFromCookies = (
|
||||
cookies: ReadonlyRequestCookies,
|
||||
): SupportedLanguageCodes | null => {
|
||||
const preferredLocale = cookies.get('language')?.value || '';
|
||||
|
||||
const language = parseLanguageFromLocale(preferredLocale || '');
|
||||
|
||||
if (!language) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return language;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the language from the `accept-language` header.
|
||||
*/
|
||||
@ -65,35 +45,24 @@ export const extractLocaleDataFromHeaders = (
|
||||
|
||||
type ExtractLocaleDataOptions = {
|
||||
headers: Headers;
|
||||
cookies: ReadonlyRequestCookies;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract the supported language from the cookies, then header if not found.
|
||||
* Extract the supported language from the header.
|
||||
*
|
||||
* Will return the default fallback language if not found.
|
||||
*/
|
||||
export const extractLocaleData = ({
|
||||
headers,
|
||||
cookies,
|
||||
}: ExtractLocaleDataOptions): I18nLocaleData => {
|
||||
let lang: SupportedLanguageCodes | null = extractLocaleDataFromCookies(cookies);
|
||||
export const extractLocaleData = ({ headers }: ExtractLocaleDataOptions): I18nLocaleData => {
|
||||
const headerLocales = (headers.get('accept-language') ?? '').split(',');
|
||||
|
||||
const langHeader = extractLocaleDataFromHeaders(headers);
|
||||
|
||||
if (!lang && langHeader?.lang) {
|
||||
lang = langHeader.lang;
|
||||
}
|
||||
|
||||
// Override web app to be English.
|
||||
if (!IS_APP_WEB_I18N_ENABLED && IS_APP_WEB) {
|
||||
lang = 'en';
|
||||
}
|
||||
const unknownLanguages = headerLocales
|
||||
.map((locale) => parseLanguageFromLocale(locale))
|
||||
.filter((value): value is SupportedLanguageCodes => value !== null);
|
||||
|
||||
// Filter out locales that are not valid.
|
||||
const locales = (langHeader?.locales ?? []).filter((locale) => {
|
||||
const languages = (unknownLanguages ?? []).filter((language) => {
|
||||
try {
|
||||
new Intl.Locale(locale);
|
||||
new Intl.Locale(language);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@ -101,8 +70,8 @@ export const extractLocaleData = ({
|
||||
});
|
||||
|
||||
return {
|
||||
lang: lang || APP_I18N_OPTIONS.sourceLang,
|
||||
locales,
|
||||
lang: languages[0] || APP_I18N_OPTIONS.sourceLang,
|
||||
locales: headerLocales,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import Honeybadger from '@honeybadger-io/js';
|
||||
|
||||
import { env } from './env';
|
||||
|
||||
export const buildLogger = () => {
|
||||
if (process.env.NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY) {
|
||||
if (env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) {
|
||||
return new HoneybadgerLogger();
|
||||
}
|
||||
|
||||
@ -45,12 +47,12 @@ class DefaultLogger implements Logger {
|
||||
|
||||
class HoneybadgerLogger implements Logger {
|
||||
constructor() {
|
||||
if (!process.env.NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY) {
|
||||
if (!env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) {
|
||||
throw new Error('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY is not set');
|
||||
}
|
||||
|
||||
Honeybadger.configure({
|
||||
apiKey: process.env.NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY,
|
||||
apiKey: env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY'),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { User } from '@documenso/prisma/client';
|
||||
import type { User } from '@prisma/client';
|
||||
|
||||
import type { DocumentWithRecipients } from '@documenso/prisma/types/document-with-recipient';
|
||||
|
||||
export type MaskRecipientTokensForDocumentOptions<T extends DocumentWithRecipients> = {
|
||||
|
||||
@ -19,7 +19,7 @@ type GetRootHrefOptions = {
|
||||
};
|
||||
|
||||
export const getRootHref = (
|
||||
params: Record<string, string | string[]> | null,
|
||||
params: Record<string, string | string[] | undefined> | null,
|
||||
options: GetRootHrefOptions = {},
|
||||
) => {
|
||||
if (typeof params?.teamUrl === 'string') {
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { WEBAPP_BASE_URL } from '../constants/app';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
|
||||
export const formatUserProfilePath = (
|
||||
profileUrl: string,
|
||||
options: { excludeBaseUrl?: boolean } = {},
|
||||
) => {
|
||||
return `${!options?.excludeBaseUrl ? WEBAPP_BASE_URL : ''}/p/${profileUrl}`;
|
||||
return `${!options?.excludeBaseUrl ? NEXT_PUBLIC_WEBAPP_URL() : ''}/p/${profileUrl}`;
|
||||
};
|
||||
|
||||
export const formatTeamProfilePath = (
|
||||
profileUrl: string,
|
||||
options: { excludeBaseUrl?: boolean } = {},
|
||||
) => {
|
||||
return `${!options?.excludeBaseUrl ? WEBAPP_BASE_URL : ''}/p/${profileUrl}`;
|
||||
return `${!options?.excludeBaseUrl ? NEXT_PUBLIC_WEBAPP_URL() : ''}/p/${profileUrl}`;
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
|
||||
export const extractInitials = (text: string) =>
|
||||
text
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { I18nProvider } from '@lingui/react';
|
||||
import type { RenderOptions } from '@documenso/email/render';
|
||||
import { render } from '@documenso/email/render';
|
||||
|
||||
import { getI18nInstance } from '../client-only/providers/i18n.server';
|
||||
import { getI18nInstance } from '../client-only/providers/i18n-server';
|
||||
import {
|
||||
APP_I18N_OPTIONS,
|
||||
type SupportedLanguageCodes,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { TeamGlobalSettings } from '@documenso/prisma/client';
|
||||
import type { TeamGlobalSettings } from '@prisma/client';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { WEBAPP_BASE_URL } from '../constants/app';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
import type { TEAM_MEMBER_ROLE_MAP } from '../constants/teams';
|
||||
import { TEAM_MEMBER_ROLE_HIERARCHY, TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../constants/teams';
|
||||
|
||||
export const formatTeamUrl = (teamUrl: string, baseUrl?: string) => {
|
||||
const formattedBaseUrl = (baseUrl ?? WEBAPP_BASE_URL).replace(/https?:\/\//, '');
|
||||
const formattedBaseUrl = (baseUrl ?? NEXT_PUBLIC_WEBAPP_URL()).replace(/https?:\/\//, '');
|
||||
|
||||
return `${formattedBaseUrl}/t/${teamUrl}`;
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
|
||||
import { WEBAPP_BASE_URL } from '../constants/app';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
|
||||
|
||||
export const formatDirectTemplatePath = (token: string) => {
|
||||
return `${WEBAPP_BASE_URL}/d/${token}`;
|
||||
return `${NEXT_PUBLIC_WEBAPP_URL()}/d/${token}`;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user