mirror of
https://github.com/documenso/documenso.git
synced 2025-11-09 20:12:31 +10:00
173 lines
5.4 KiB
TypeScript
173 lines
5.4 KiB
TypeScript
import { DocumentStatus, RecipientRole } from '@prisma/client';
|
|
import { data } from 'react-router';
|
|
import { match } from 'ts-pattern';
|
|
|
|
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
|
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
|
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
|
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
|
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
|
|
import { getIsRecipientsTurnToSign } from '@documenso/lib/server-only/recipient/get-is-recipient-turn';
|
|
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
|
import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant';
|
|
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
|
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
|
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
|
|
|
import { EmbedSignDocumentClientPage } from '~/components/embed/embed-document-signing-page';
|
|
import { DocumentSigningAuthProvider } from '~/components/general/document-signing/document-signing-auth-provider';
|
|
import { DocumentSigningProvider } from '~/components/general/document-signing/document-signing-provider';
|
|
import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
|
|
|
|
import type { Route } from './+types/sign.$url';
|
|
|
|
export async function loader({ params, request }: Route.LoaderArgs) {
|
|
if (!params.url) {
|
|
throw new Response('Not found', { status: 404 });
|
|
}
|
|
|
|
const token = params.url;
|
|
|
|
const { user } = await getOptionalSession(request);
|
|
|
|
const [document, fields, recipient] = await Promise.all([
|
|
getDocumentAndSenderByToken({
|
|
token,
|
|
userId: user?.id,
|
|
requireAccessAuth: false,
|
|
}).catch(() => null),
|
|
getFieldsForToken({ token }),
|
|
getRecipientByToken({ token }).catch(() => null),
|
|
]);
|
|
|
|
// `document.directLink` is always available but we're doing this to
|
|
// satisfy the type checker.
|
|
if (!document || !recipient) {
|
|
throw new Response('Not found', { status: 404 });
|
|
}
|
|
|
|
// TODO: Make this more robust, we need to ensure the owner is either
|
|
// TODO: the member of a team that has an active subscription, is an early
|
|
// TODO: adopter or is an enterprise user.
|
|
if (IS_BILLING_ENABLED() && !document.teamId) {
|
|
throw data(
|
|
{
|
|
type: 'embed-paywall',
|
|
},
|
|
{
|
|
status: 403,
|
|
},
|
|
);
|
|
}
|
|
|
|
const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([
|
|
isDocumentPlatform(document),
|
|
isUserEnterprise({
|
|
userId: document.userId,
|
|
teamId: document.teamId ?? undefined,
|
|
}),
|
|
]);
|
|
|
|
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
|
documentAuth: document.authOptions,
|
|
});
|
|
|
|
const isAccessAuthValid = match(derivedRecipientAccessAuth)
|
|
.with(DocumentAccessAuth.ACCOUNT, () => user !== null)
|
|
.with(null, () => true)
|
|
.exhaustive();
|
|
|
|
if (!isAccessAuthValid) {
|
|
throw data(
|
|
{
|
|
type: 'embed-authentication-required',
|
|
email: user?.email || recipient.email,
|
|
returnTo: `/embed/sign/${token}`,
|
|
},
|
|
{
|
|
status: 401,
|
|
},
|
|
);
|
|
}
|
|
|
|
const isRecipientsTurnToSign = await getIsRecipientsTurnToSign({ token });
|
|
|
|
if (!isRecipientsTurnToSign) {
|
|
throw data(
|
|
{
|
|
type: 'embed-waiting-for-turn',
|
|
},
|
|
{
|
|
status: 403,
|
|
},
|
|
);
|
|
}
|
|
|
|
const allRecipients =
|
|
recipient.role === RecipientRole.ASSISTANT
|
|
? await getRecipientsForAssistant({
|
|
token,
|
|
})
|
|
: [];
|
|
|
|
const team = document.teamId
|
|
? await getTeamById({ teamId: document.teamId, userId: document.userId }).catch(() => null)
|
|
: null;
|
|
|
|
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
|
|
|
|
return superLoaderJson({
|
|
token,
|
|
user,
|
|
document,
|
|
allRecipients,
|
|
recipient,
|
|
fields,
|
|
hidePoweredBy,
|
|
isPlatformDocument,
|
|
isEnterpriseDocument,
|
|
});
|
|
}
|
|
|
|
export default function EmbedSignDocumentPage() {
|
|
const {
|
|
token,
|
|
user,
|
|
document,
|
|
allRecipients,
|
|
recipient,
|
|
fields,
|
|
hidePoweredBy,
|
|
isPlatformDocument,
|
|
isEnterpriseDocument,
|
|
} = useSuperLoaderData<typeof loader>();
|
|
|
|
return (
|
|
<DocumentSigningProvider
|
|
email={recipient.email}
|
|
fullName={user?.email === recipient.email ? user?.name : recipient.name}
|
|
signature={user?.email === recipient.email ? user?.signature : undefined}
|
|
>
|
|
<DocumentSigningAuthProvider
|
|
documentAuthOptions={document.authOptions}
|
|
recipient={recipient}
|
|
user={user}
|
|
>
|
|
<EmbedSignDocumentClientPage
|
|
token={token}
|
|
documentId={document.id}
|
|
documentData={document.documentData}
|
|
recipient={recipient}
|
|
fields={fields}
|
|
metadata={document.documentMeta}
|
|
isCompleted={document.status === DocumentStatus.COMPLETED}
|
|
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy}
|
|
isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument}
|
|
allRecipients={allRecipients}
|
|
/>
|
|
</DocumentSigningAuthProvider>
|
|
</DocumentSigningProvider>
|
|
);
|
|
}
|