mirror of
https://github.com/documenso/documenso.git
synced 2025-11-20 11:41:44 +10:00
fix: wip
This commit is contained in:
@ -8,6 +8,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { env } from '@documenso/lib/utils/env';
|
import { env } from '@documenso/lib/utils/env';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { AuthenticationErrorCode } from '../lib/errors/error-codes';
|
import { AuthenticationErrorCode } from '../lib/errors/error-codes';
|
||||||
import { sessionCookieOptions } from '../lib/session/session-cookies';
|
import { sessionCookieOptions } from '../lib/session/session-cookies';
|
||||||
@ -71,6 +72,8 @@ export const googleRoute = new Hono<HonoAuthContext>()
|
|||||||
* Google callback verification.
|
* Google callback verification.
|
||||||
*/
|
*/
|
||||||
.get('/callback', async (c) => {
|
.get('/callback', async (c) => {
|
||||||
|
const requestMeta = c.get('requestMetadata');
|
||||||
|
|
||||||
const code = c.req.query('code');
|
const code = c.req.query('code');
|
||||||
const state = c.req.query('state');
|
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 claims = decodeIdToken(tokens.idToken()) as Record<string, unknown>;
|
||||||
|
|
||||||
const googleEmail = claims.email;
|
const googleEmail = claims.email;
|
||||||
|
const googleEmailVerified = claims.email_verified;
|
||||||
const googleName = claims.name;
|
const googleName = claims.name;
|
||||||
const googleSub = claims.sub;
|
const googleSub = claims.sub;
|
||||||
|
|
||||||
@ -145,7 +149,8 @@ export const googleRoute = new Hono<HonoAuthContext>()
|
|||||||
|
|
||||||
// Handle existing user but no account.
|
// Handle existing user but no account.
|
||||||
if (userWithSameEmail) {
|
if (userWithSameEmail) {
|
||||||
await prisma.account.create({
|
await prisma.$transaction(async (tx) => {
|
||||||
|
await tx.account.create({
|
||||||
data: {
|
data: {
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
provider: 'google',
|
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);
|
await onAuthorize({ userId: userWithSameEmail.id }, c);
|
||||||
|
|
||||||
return c.redirect(redirectPath, 302);
|
return c.redirect(redirectPath, 302);
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
|
|||||||
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
||||||
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
||||||
|
|
||||||
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
||||||
import {
|
import {
|
||||||
ZCheckboxFieldMeta,
|
ZCheckboxFieldMeta,
|
||||||
ZDateFieldMeta,
|
ZDateFieldMeta,
|
||||||
@ -26,14 +27,12 @@ import {
|
|||||||
ZTextFieldMeta,
|
ZTextFieldMeta,
|
||||||
} from '../../types/field-meta';
|
} 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) => {
|
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);
|
const isSignatureField = isSignatureFieldType(field.type);
|
||||||
|
|
||||||
pdf.registerFontkit(fontkit);
|
pdf.registerFontkit(fontkit);
|
||||||
|
|||||||
@ -19,8 +19,6 @@ import { cn } from '../../lib/utils';
|
|||||||
type FieldIconProps = {
|
type FieldIconProps = {
|
||||||
fieldMeta: FieldMetaType;
|
fieldMeta: FieldMetaType;
|
||||||
type: FieldType;
|
type: FieldType;
|
||||||
signerEmail?: string;
|
|
||||||
fontCaveatClassName?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldIcons = {
|
const fieldIcons = {
|
||||||
@ -35,18 +33,12 @@ const fieldIcons = {
|
|||||||
[FieldType.DROPDOWN]: { icon: ChevronDown, label: 'Select' },
|
[FieldType.DROPDOWN]: { icon: ChevronDown, label: 'Select' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FieldIcon = ({
|
export const FieldIcon = ({ fieldMeta, type }: FieldIconProps) => {
|
||||||
fieldMeta,
|
|
||||||
type,
|
|
||||||
signerEmail,
|
|
||||||
fontCaveatClassName,
|
|
||||||
}: FieldIconProps) => {
|
|
||||||
if (type === 'SIGNATURE' || type === 'FREE_SIGNATURE') {
|
if (type === 'SIGNATURE' || type === 'FREE_SIGNATURE') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-field-card-foreground flex items-center justify-center gap-x-1 text-[clamp(0.575rem,25cqw,1.2rem)]',
|
'text-field-card-foreground font-signature flex items-center justify-center gap-x-1 text-[clamp(0.575rem,25cqw,1.2rem)]',
|
||||||
fontCaveatClassName,
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Trans>Signature</Trans>
|
<Trans>Signature</Trans>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
// Todo
|
|
||||||
// import { Caveat } from 'next/font/google';
|
|
||||||
import { CopyPlus, Settings2, Trash } from 'lucide-react';
|
import { CopyPlus, Settings2, Trash } from 'lucide-react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { Rnd } from 'react-rnd';
|
import { Rnd } from 'react-rnd';
|
||||||
@ -20,13 +18,6 @@ import type { TDocumentFlowFormSchema } from './types';
|
|||||||
|
|
||||||
type Field = TDocumentFlowFormSchema['fields'][0];
|
type Field = TDocumentFlowFormSchema['fields'][0];
|
||||||
|
|
||||||
// const fontCaveat = Caveat({
|
|
||||||
// weight: ['500'],
|
|
||||||
// subsets: ['latin'],
|
|
||||||
// display: 'swap',
|
|
||||||
// variable: '--font-caveat',
|
|
||||||
// });
|
|
||||||
|
|
||||||
export type FieldItemProps = {
|
export type FieldItemProps = {
|
||||||
field: Field;
|
field: Field;
|
||||||
passive?: boolean;
|
passive?: boolean;
|
||||||
@ -252,12 +243,7 @@ export const FieldItem = ({
|
|||||||
.with('CHECKBOX', () => <CheckboxField field={field} />)
|
.with('CHECKBOX', () => <CheckboxField field={field} />)
|
||||||
.with('RADIO', () => <RadioField field={field} />)
|
.with('RADIO', () => <RadioField field={field} />)
|
||||||
.otherwise(() => (
|
.otherwise(() => (
|
||||||
<FieldIcon
|
<FieldIcon fieldMeta={field.fieldMeta} type={field.type} />
|
||||||
fieldMeta={field.fieldMeta}
|
|
||||||
type={field.type}
|
|
||||||
signerEmail={field.signerEmail}
|
|
||||||
// fontCaveatClassName={fontCaveat.className}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{!hideRecipients && (
|
{!hideRecipients && (
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
// import { Caveat } from 'next/font/google';
|
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import type { Prisma } from '@prisma/client';
|
import { FieldType, type Prisma } from '@prisma/client';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
|
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 { Card, CardContent } from '../card';
|
||||||
import { FRIENDLY_FIELD_TYPE } from './types';
|
import { FRIENDLY_FIELD_TYPE } from './types';
|
||||||
|
|
||||||
// const fontCaveat = Caveat({
|
|
||||||
// weight: ['500'],
|
|
||||||
// subsets: ['latin'],
|
|
||||||
// display: 'swap',
|
|
||||||
// variable: '--font-caveat',
|
|
||||||
// });
|
|
||||||
|
|
||||||
export type ShowFieldItemProps = {
|
export type ShowFieldItemProps = {
|
||||||
field: Prisma.FieldGetPayload<null>;
|
field: Prisma.FieldGetPayload<null>;
|
||||||
recipients: Prisma.RecipientGetPayload<null>[];
|
recipients: Prisma.RecipientGetPayload<null>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
|
export const ShowFieldItem = ({ field }: ShowFieldItemProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const coords = useFieldPageCoords(field);
|
const coords = useFieldPageCoords(field);
|
||||||
|
|
||||||
const signerEmail =
|
|
||||||
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '';
|
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div
|
<div
|
||||||
className={cn('pointer-events-none absolute z-10 opacity-75')}
|
className={cn('pointer-events-none absolute z-10 opacity-75')}
|
||||||
@ -44,7 +33,7 @@ export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
|
|||||||
<CardContent
|
<CardContent
|
||||||
className={cn(
|
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',
|
'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])}
|
{parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[field.type])}
|
||||||
|
|||||||
Reference in New Issue
Block a user