From 15922d447b69f62f0fecb211f713744eda2b632d Mon Sep 17 00:00:00 2001
From: David Nguyen
Date: Wed, 12 Feb 2025 16:41:35 +1100
Subject: [PATCH] fix: wip
---
...y-dialog.tsx => passkey-create-dialog.tsx} | 4 +-
.../dialogs/template-create-dialog.tsx | 24 +-
.../dialogs/template-use-dialog.tsx | 18 +-
apps/remix/app/components/forms/password.tsx | 4 +-
apps/remix/app/components/forms/signin.tsx | 6 +-
.../forms/team-branding-preferences-form.tsx | 29 +-
.../app/components/general/app-header.tsx | 4 +-
.../document-signing-auth-2fa.tsx | 14 +-
.../document-signing-auth-account.tsx | 2 +-
.../document-signing-auth-page.tsx | 2 +-
.../document-signing-auth-passkey.tsx | 4 +-
.../document-signing-auth-provider.tsx | 7 +-
.../general/document/document-status.tsx | 2 +-
.../general/document/document-upload.tsx | 24 +-
.../app/components/general/menu-switcher.tsx | 4 +-
.../general/upcoming-profile-claim-teaser.tsx | 55 ----
.../general/verify-email-banner.tsx | 18 +-
.../documents-table-action-dropdown.tsx | 2 +-
apps/remix/app/root.tsx | 6 +
.../_authenticated+/admin+/subscriptions.tsx | 2 +-
.../_authenticated+/admin+/users._index.tsx | 2 +-
.../_authenticated+/documents+/$id._index.tsx | 3 +-
.../settings+/public-profile+/index.tsx | 2 +-
.../settings+/security+/index.tsx | 34 ++-
.../settings+/security+/passkeys+/index.tsx | 4 +-
.../routes/_recipient+/d.$token+/_index.tsx | 4 +-
apps/remix/package.json | 8 +-
apps/remix/rollup.config.mjs | 6 +-
apps/remix/server/api/files.ts | 110 +++++++
apps/remix/server/api/files.types.ts | 38 +++
apps/remix/server/middleware.ts | 110 +++----
apps/remix/server/router.ts | 85 +-----
apps/remix/tsconfig.json | 3 +-
package-lock.json | 284 ++++++++++++++----
packages/api/v1/implementation.ts | 12 +-
packages/auth/package.json | 4 +-
packages/auth/server/lib/session/session.ts | 57 ++--
packages/auth/server/routes/email-password.ts | 23 +-
packages/auth/server/routes/google.ts | 4 +-
packages/auth/server/routes/passkey.ts | 6 +-
packages/email/ambient.d.ts | 1 -
packages/email/render.tsx | 7 +-
packages/email/templates/document-invite.tsx | 2 +-
.../lib/client-only/providers/session.tsx | 5 +-
.../emails/send-signing-email.handler.ts | 4 +-
.../internal/seal-document.handler.ts | 8 +-
packages/lib/package.json | 2 +-
packages/lib/server-only/2fa/disable-2fa.ts | 5 +-
.../document/create-document-v2.ts | 8 +-
.../server-only/document/create-document.ts | 8 +-
.../lib/server-only/document/seal-document.ts | 8 +-
.../document/send-completed-email.ts | 4 +-
.../server-only/document/send-document.tsx | 8 +-
.../get-template-by-direct-link-token.ts | 12 +-
.../lib/universal/upload/get-file.server.ts | 50 +++
packages/lib/universal/upload/get-file.ts | 18 +-
.../lib/universal/upload/put-file.server.ts | 85 ++++++
packages/lib/universal/upload/put-file.ts | 60 ++--
.../lib/universal/upload/server-actions.ts | 47 +--
packages/prisma/package.json | 2 +-
packages/tailwind-config/index.d.ts | 4 +
packages/tailwind-config/package.json | 3 +-
packages/trpc/server/context.ts | 5 +-
.../primitives/document-flow/add-fields.tsx | 16 +-
.../advanced-fields/checkbox.tsx | 6 +-
.../document-flow/advanced-fields/radio.tsx | 6 +-
packages/ui/primitives/password-input.tsx | 3 +-
packages/ui/primitives/signature-pad/point.ts | 2 +-
.../signature-pad/signature-pad.tsx | 11 +-
.../template-flow/add-template-fields.tsx | 10 +-
70 files changed, 889 insertions(+), 551 deletions(-)
rename apps/remix/app/components/dialogs/{create-passkey-dialog.tsx => passkey-create-dialog.tsx} (98%)
delete mode 100644 apps/remix/app/components/general/upcoming-profile-claim-teaser.tsx
create mode 100644 apps/remix/server/api/files.ts
create mode 100644 apps/remix/server/api/files.types.ts
delete mode 100644 packages/email/ambient.d.ts
create mode 100644 packages/lib/universal/upload/get-file.server.ts
create mode 100644 packages/lib/universal/upload/put-file.server.ts
create mode 100644 packages/tailwind-config/index.d.ts
diff --git a/apps/remix/app/components/dialogs/create-passkey-dialog.tsx b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx
similarity index 98%
rename from apps/remix/app/components/dialogs/create-passkey-dialog.tsx
rename to apps/remix/app/components/dialogs/passkey-create-dialog.tsx
index d454ac834..6a21895d3 100644
--- a/apps/remix/app/components/dialogs/create-passkey-dialog.tsx
+++ b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx
@@ -37,7 +37,7 @@ import {
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
-export type CreatePasskeyDialogProps = {
+export type PasskeyCreateDialogProps = {
trigger?: React.ReactNode;
onSuccess?: () => void;
} & Omit;
@@ -50,7 +50,7 @@ type TCreatePasskeyFormSchema = z.infer;
const parser = new UAParser();
-export const CreatePasskeyDialog = ({ trigger, onSuccess, ...props }: CreatePasskeyDialogProps) => {
+export const PasskeyCreateDialog = ({ trigger, onSuccess, ...props }: PasskeyCreateDialogProps) => {
const [open, setOpen] = useState(false);
const [formError, setFormError] = useState(null);
diff --git a/apps/remix/app/components/dialogs/template-create-dialog.tsx b/apps/remix/app/components/dialogs/template-create-dialog.tsx
index 01f7a573e..85038ba3d 100644
--- a/apps/remix/app/components/dialogs/template-create-dialog.tsx
+++ b/apps/remix/app/components/dialogs/template-create-dialog.tsx
@@ -7,7 +7,7 @@ import { FilePlus, Loader } from 'lucide-react';
import { useNavigate } from 'react-router';
import { useSession } from '@documenso/lib/client-only/providers/session';
-import { AppError } from '@documenso/lib/errors/app-error';
+import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import {
@@ -48,27 +48,7 @@ export const TemplateCreateDialog = ({ templateRootPath }: TemplateCreateDialogP
setIsUploadingFile(true);
try {
- // Todo
- // const { type, data } = await putPdfFile(file);
-
- const formData = new FormData();
- formData.append('file', file);
-
- const response = await fetch('/api/file', {
- method: 'POST',
- body: formData,
- })
- .then(async (res) => await res.json())
- .catch((e) => {
- console.error('Upload failed:', e);
- throw new AppError('UPLOAD_FAILED');
- });
-
- // Why do we run this twice?
- // const { id: templateDocumentDataId } = await createDocumentData({
- // type: response.type,
- // data: response.data,
- // });
+ const response = await putPdfFile(file);
const { id } = await createTemplate({
title: file.name,
diff --git a/apps/remix/app/components/dialogs/template-use-dialog.tsx b/apps/remix/app/components/dialogs/template-use-dialog.tsx
index 8225afc84..2f8266ef1 100644
--- a/apps/remix/app/components/dialogs/template-use-dialog.tsx
+++ b/apps/remix/app/components/dialogs/template-use-dialog.tsx
@@ -17,6 +17,7 @@ import {
TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX,
} from '@documenso/lib/constants/template';
import { AppError } from '@documenso/lib/errors/app-error';
+import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
@@ -150,22 +151,7 @@ export function TemplateUseDialog({
let customDocumentDataId: string | undefined = undefined;
if (data.useCustomDocument && data.customDocumentData) {
- // const customDocumentData = await putPdfFile(data.customDocumentData);
- // Todo
-
- const formData = new FormData();
- formData.append('file', data.customDocumentData);
-
- const customDocumentData = await fetch('/api/file', {
- method: 'POST',
- body: formData,
- })
- .then(async (res) => await res.json())
- .catch((e) => {
- console.error('Upload failed:', e);
- throw new AppError('UPLOAD_FAILED');
- });
-
+ const customDocumentData = await putPdfFile(data.customDocumentData);
customDocumentDataId = customDocumentData.id;
}
diff --git a/apps/remix/app/components/forms/password.tsx b/apps/remix/app/components/forms/password.tsx
index 23ae901ab..d397cdcaf 100644
--- a/apps/remix/app/components/forms/password.tsx
+++ b/apps/remix/app/components/forms/password.tsx
@@ -2,12 +2,12 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
-import type { User } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { z } from 'zod';
import { authClient } from '@documenso/auth/client';
+import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { AppError } from '@documenso/lib/errors/app-error';
import { ZCurrentPasswordSchema, ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
import { cn } from '@documenso/ui/lib/utils';
@@ -38,7 +38,7 @@ export type TPasswordFormSchema = z.infer;
export type PasswordFormProps = {
className?: string;
- user: User;
+ user: SessionUser;
};
export const PasswordForm = ({ className }: PasswordFormProps) => {
diff --git a/apps/remix/app/components/forms/signin.tsx b/apps/remix/app/components/forms/signin.tsx
index 143455bd1..9154aeb92 100644
--- a/apps/remix/app/components/forms/signin.tsx
+++ b/apps/remix/app/components/forms/signin.tsx
@@ -97,7 +97,7 @@ export const SignInForm = ({
const [isPasskeyLoading, setIsPasskeyLoading] = useState(false);
- const redirectUrl = useMemo(() => {
+ const redirectPath = useMemo(() => {
// Handle SSR
if (typeof window === 'undefined') {
return LOGIN_REDIRECT_PATH;
@@ -171,7 +171,7 @@ export const SignInForm = ({
await authClient.passkey.signIn({
credential: JSON.stringify(credential),
csrfToken: sessionId,
- redirectUrl,
+ redirectPath,
});
} catch (err) {
setIsPasskeyLoading(false);
@@ -211,7 +211,7 @@ export const SignInForm = ({
password,
totpCode,
backupCode,
- redirectUrl,
+ redirectPath,
});
} catch (err) {
console.log(err);
diff --git a/apps/remix/app/components/forms/team-branding-preferences-form.tsx b/apps/remix/app/components/forms/team-branding-preferences-form.tsx
index 8136314c4..f33345d3b 100644
--- a/apps/remix/app/components/forms/team-branding-preferences-form.tsx
+++ b/apps/remix/app/components/forms/team-branding-preferences-form.tsx
@@ -9,6 +9,8 @@ import { Loader } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
+import { getFile } from '@documenso/lib/universal/upload/get-file';
+import { putFile } from '@documenso/lib/universal/upload/put-file';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
@@ -78,8 +80,7 @@ export function TeamBrandingPreferencesForm({ team, settings }: TeamBrandingPref
let uploadedBrandingLogo = settings?.brandingLogo;
if (brandingLogo) {
- // Todo
- // uploadedBrandingLogo = JSON.stringify(await putFile(brandingLogo));
+ uploadedBrandingLogo = JSON.stringify(await putFile(brandingLogo));
}
if (brandingLogo === null) {
@@ -116,26 +117,12 @@ export function TeamBrandingPreferencesForm({ team, settings }: TeamBrandingPref
const file = JSON.parse(settings.brandingLogo);
if ('type' in file && 'data' in file) {
- // Todo
- // Todo
- // Todo
- void fetch(`/api/file?key=${file.key}`, {
- method: 'GET',
- })
- .then(async (res) => await res.json())
- .then((data) => {
- const objectUrl = URL.createObjectURL(new Blob([data.binaryData]));
+ void getFile(file).then((binaryData) => {
+ const objectUrl = URL.createObjectURL(new Blob([binaryData]));
- setPreviewUrl(objectUrl);
- setHasLoadedPreview(true);
- });
-
- // void getFile(file).then((binaryData) => {
- // const objectUrl = URL.createObjectURL(new Blob([binaryData]));
-
- // setPreviewUrl(objectUrl);
- // setHasLoadedPreview(true);
- // });
+ setPreviewUrl(objectUrl);
+ setHasLoadedPreview(true);
+ });
return;
}
diff --git a/apps/remix/app/components/general/app-header.tsx b/apps/remix/app/components/general/app-header.tsx
index edc2d1f6b..4d8361e69 100644
--- a/apps/remix/app/components/general/app-header.tsx
+++ b/apps/remix/app/components/general/app-header.tsx
@@ -1,9 +1,9 @@
import { type HTMLAttributes, useEffect, useState } from 'react';
-import type { User } from '@prisma/client';
import { MenuIcon, SearchIcon } from 'lucide-react';
import { Link, useLocation, useParams } from 'react-router';
+import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import type { TGetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
import { getRootHref } from '@documenso/lib/utils/params';
import { cn } from '@documenso/ui/lib/utils';
@@ -16,7 +16,7 @@ import { AppNavMobile } from './app-nav-mobile';
import { MenuSwitcher } from './menu-switcher';
export type HeaderProps = HTMLAttributes & {
- user: User;
+ user: SessionUser;
teams: TGetTeamsResponse;
};
diff --git a/apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx b/apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
index 4f8be2bba..e0b5ca2b1 100644
--- a/apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
+++ b/apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
@@ -109,14 +109,12 @@ export const DocumentSigningAuth2FA = ({
)}
- {user?.identityProvider === 'DOCUMENSO' && (
-
-
- By enabling 2FA, you will be required to enter a code from your authenticator app
- every time you sign in.
-
-
- )}
+
+
+ By enabling 2FA, you will be required to enter a code from your authenticator app
+ every time you sign in using email password.
+
+
diff --git a/apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx b/apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
index 9f93144d1..b2877f2e6 100644
--- a/apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
+++ b/apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
@@ -35,7 +35,7 @@ export const DocumentSigningAuthAccount = ({
setIsSigningOut(true);
await authClient.signOut({
- redirectUrl: `/signin#email=${email}`,
+ redirectPath: `/signin#email=${email}`,
});
} catch {
setIsSigningOut(false);
diff --git a/apps/remix/app/components/general/document-signing/document-signing-auth-page.tsx b/apps/remix/app/components/general/document-signing/document-signing-auth-page.tsx
index 98fcb8b9a..21b1be6ca 100644
--- a/apps/remix/app/components/general/document-signing/document-signing-auth-page.tsx
+++ b/apps/remix/app/components/general/document-signing/document-signing-auth-page.tsx
@@ -27,7 +27,7 @@ export const DocumentSigningAuthPageView = ({
setIsSigningOut(true);
await authClient.signOut({
- redirectUrl: emailHasAccount ? `/signin#email=${email}` : `/signup#email=${email}`,
+ redirectPath: emailHasAccount ? `/signin#email=${email}` : `/signup#email=${email}`,
});
} catch {
toast({
diff --git a/apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx b/apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
index 523ef9c8e..22e641713 100644
--- a/apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
+++ b/apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
@@ -32,7 +32,7 @@ import {
SelectValue,
} from '@documenso/ui/primitives/select';
-import { CreatePasskeyDialog } from '~/components/dialogs/create-passkey-dialog';
+import { PasskeyCreateDialog } from '~/components/dialogs/passkey-create-dialog';
import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
@@ -190,7 +190,7 @@ export const DocumentSigningAuthPasskey = ({
Cancel
- refetchPasskeys()}
trigger={