This commit is contained in:
David Nguyen
2025-02-10 03:33:22 +11:00
parent 5b395fc9ad
commit d24f67d922
5 changed files with 67 additions and 58 deletions

View File

@ -8,6 +8,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { env } from '@documenso/lib/utils/env';
import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import { AuthenticationErrorCode } from '../lib/errors/error-codes';
import { sessionCookieOptions } from '../lib/session/session-cookies';
@ -71,6 +72,8 @@ export const googleRoute = new Hono<HonoAuthContext>()
* Google callback verification.
*/
.get('/callback', async (c) => {
const requestMeta = c.get('requestMetadata');
const code = c.req.query('code');
const state = c.req.query('state');
@ -100,6 +103,7 @@ export const googleRoute = new Hono<HonoAuthContext>()
const claims = decodeIdToken(tokens.idToken()) as Record<string, unknown>;
const googleEmail = claims.email;
const googleEmailVerified = claims.email_verified;
const googleName = claims.name;
const googleSub = claims.sub;
@ -145,7 +149,8 @@ export const googleRoute = new Hono<HonoAuthContext>()
// Handle existing user but no account.
if (userWithSameEmail) {
await prisma.account.create({
await prisma.$transaction(async (tx) => {
await tx.account.create({
data: {
type: 'oauth',
provider: 'google',
@ -158,7 +163,45 @@ export const googleRoute = new Hono<HonoAuthContext>()
},
});
// Todo: Link account
// Log link event.
await tx.userSecurityAuditLog.create({
data: {
userId: userWithSameEmail.id,
ipAddress: requestMeta.ipAddress,
userAgent: requestMeta.userAgent,
type: UserSecurityAuditLogType.ACCOUNT_SSO_LINK,
},
});
// If account already exists in an unverified state, remove the password to ensure
// they cannot sign in since we cannot confirm the password was set by the user.
if (!userWithSameEmail.emailVerified) {
await tx.user.update({
where: {
id: userWithSameEmail.id,
},
data: {
emailVerified: new Date(),
password: null, // Todo: Check this
},
});
}
// Apparently incredibly rare case? So we whole account to unverified.
if (!googleEmailVerified) {
// Todo: Add logging.
await tx.user.update({
where: {
id: userWithSameEmail.id,
},
data: {
emailVerified: null,
},
});
}
});
await onAuthorize({ userId: userWithSameEmail.id }, c);
return c.redirect(redirectPath, 302);

View File

@ -15,6 +15,7 @@ import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import {
ZCheckboxFieldMeta,
ZDateFieldMeta,
@ -26,14 +27,12 @@ import {
ZTextFieldMeta,
} from '../../types/field-meta';
// Todo: Check if this is okay to do compared to the old approach.
// import fontCaveat from '@documenso/assets/fonts/caveat.ttf?inline';
// import fontNoto from '@documenso/assets/fonts/noto-sans.ttf?inline';
const fontCaveat = '';
const fontNoto = '';
export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignature) => {
const [fontCaveat, fontNoto] = await Promise.all([
fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/caveat.ttf`).then(async (res) => res.arrayBuffer()),
fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/noto-sans.ttf`).then(async (res) => res.arrayBuffer()),
]);
const isSignatureField = isSignatureFieldType(field.type);
pdf.registerFontkit(fontkit);

View File

@ -19,8 +19,6 @@ import { cn } from '../../lib/utils';
type FieldIconProps = {
fieldMeta: FieldMetaType;
type: FieldType;
signerEmail?: string;
fontCaveatClassName?: string;
};
const fieldIcons = {
@ -35,18 +33,12 @@ const fieldIcons = {
[FieldType.DROPDOWN]: { icon: ChevronDown, label: 'Select' },
};
export const FieldIcon = ({
fieldMeta,
type,
signerEmail,
fontCaveatClassName,
}: FieldIconProps) => {
export const FieldIcon = ({ fieldMeta, type }: FieldIconProps) => {
if (type === 'SIGNATURE' || type === 'FREE_SIGNATURE') {
return (
<div
className={cn(
'text-field-card-foreground flex items-center justify-center gap-x-1 text-[clamp(0.575rem,25cqw,1.2rem)]',
fontCaveatClassName,
'text-field-card-foreground font-signature flex items-center justify-center gap-x-1 text-[clamp(0.575rem,25cqw,1.2rem)]',
)}
>
<Trans>Signature</Trans>

View File

@ -1,7 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
// Todo
// import { Caveat } from 'next/font/google';
import { CopyPlus, Settings2, Trash } from 'lucide-react';
import { createPortal } from 'react-dom';
import { Rnd } from 'react-rnd';
@ -20,13 +18,6 @@ import type { TDocumentFlowFormSchema } from './types';
type Field = TDocumentFlowFormSchema['fields'][0];
// const fontCaveat = Caveat({
// weight: ['500'],
// subsets: ['latin'],
// display: 'swap',
// variable: '--font-caveat',
// });
export type FieldItemProps = {
field: Field;
passive?: boolean;
@ -252,12 +243,7 @@ export const FieldItem = ({
.with('CHECKBOX', () => <CheckboxField field={field} />)
.with('RADIO', () => <RadioField field={field} />)
.otherwise(() => (
<FieldIcon
fieldMeta={field.fieldMeta}
type={field.type}
signerEmail={field.signerEmail}
// fontCaveatClassName={fontCaveat.className}
/>
<FieldIcon fieldMeta={field.fieldMeta} type={field.type} />
))}
{!hideRecipients && (

View File

@ -1,6 +1,5 @@
// import { Caveat } from 'next/font/google';
import { useLingui } from '@lingui/react';
import type { Prisma } from '@prisma/client';
import { FieldType, type Prisma } from '@prisma/client';
import { createPortal } from 'react-dom';
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
@ -10,26 +9,16 @@ import { cn } from '../../lib/utils';
import { Card, CardContent } from '../card';
import { FRIENDLY_FIELD_TYPE } from './types';
// const fontCaveat = Caveat({
// weight: ['500'],
// subsets: ['latin'],
// display: 'swap',
// variable: '--font-caveat',
// });
export type ShowFieldItemProps = {
field: Prisma.FieldGetPayload<null>;
recipients: Prisma.RecipientGetPayload<null>[];
};
export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
export const ShowFieldItem = ({ field }: ShowFieldItemProps) => {
const { _ } = useLingui();
const coords = useFieldPageCoords(field);
const signerEmail =
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '';
return createPortal(
<div
className={cn('pointer-events-none absolute z-10 opacity-75')}
@ -44,7 +33,7 @@ export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
<CardContent
className={cn(
'text-muted-foreground/50 flex h-full w-full flex-col items-center justify-center p-0 text-[clamp(0.575rem,1.8cqw,1.2rem)] leading-none',
// field.type === FieldType.SIGNATURE && fontCaveat.className,
field.type === FieldType.SIGNATURE && 'font-signature',
)}
>
{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])}