diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx
index bebf6534d..2c3c7f223 100644
--- a/apps/remix/app/root.tsx
+++ b/apps/remix/app/root.tsx
@@ -13,6 +13,7 @@ import {
useLocation,
} from 'react-router';
import { ThemeProvider } from 'remix-themes';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { SessionProvider } from '@documenso/lib/client-only/providers/session';
import { APP_I18N_OPTIONS, type SupportedLanguageCodes } from '@documenso/lib/constants/i18n';
@@ -82,7 +83,9 @@ export const links: Route.LinksFunction = () => [
// };
// }
-export async function loader({ request, context }: Route.LoaderArgs) {
+export async function loader({ request }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { getTheme } = await themeSessionResolver(request);
let lang: SupportedLanguageCodes = await langCookie.parse(request.headers.get('cookie') ?? '');
@@ -95,7 +98,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
{
lang,
theme: getTheme(),
- session: context.session,
+ session,
__ENV__: Object.fromEntries(
Object.entries(process.env).filter(([key]) => key.startsWith('NEXT_')), // Todo: I'm pretty sure this will leak?
),
diff --git a/apps/remix/app/routes/_authenticated+/_layout.tsx b/apps/remix/app/routes/_authenticated+/_layout.tsx
index cea725807..9646a7c88 100644
--- a/apps/remix/app/routes/_authenticated+/_layout.tsx
+++ b/apps/remix/app/routes/_authenticated+/_layout.tsx
@@ -1,4 +1,5 @@
-import { Outlet, redirect } from 'react-router';
+import { Outlet } from 'react-router';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { getLimits } from '@documenso/ee/server-only/limits/client';
import { LimitsProvider } from '@documenso/ee/server-only/limits/provider/client';
@@ -11,29 +12,25 @@ import { VerifyEmailBanner } from '~/components/general/verify-email-banner';
import type { Route } from './+types/_layout';
-export const loader = async ({ request, context }: Route.LoaderArgs) => {
- const { session } = context;
-
- if (!session) {
- throw redirect('/signin');
- }
+export const loader = async ({ request }: Route.LoaderArgs) => {
+ const { user, teams, currentTeam } = getLoaderSession();
const requestHeaders = Object.fromEntries(request.headers.entries());
// Todo: Should only load this on first render.
const [limits, banner] = await Promise.all([
- getLimits({ headers: requestHeaders, teamId: session.currentTeam?.id }),
+ getLimits({ headers: requestHeaders, teamId: currentTeam?.id }),
getSiteSettings().then((settings) =>
settings.find((setting) => setting.id === SITE_SETTINGS_BANNER_ID),
),
]);
return {
- user: session.user,
- teams: session.teams,
+ user,
+ teams,
banner,
limits,
- teamId: session.currentTeam?.id,
+ teamId: currentTeam?.id,
};
};
diff --git a/apps/remix/app/routes/_authenticated+/admin+/_layout.tsx b/apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
index 3b64519a4..8d4a407e7 100644
--- a/apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
+++ b/apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
@@ -1,16 +1,14 @@
import { Trans } from '@lingui/macro';
import { BarChart3, FileStack, Settings, Trophy, Users, Wallet2 } from 'lucide-react';
import { Link, Outlet, redirect, useLocation } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
-import type { Route } from './+types/_layout';
-
-export function loader({ context }: Route.LoaderArgs) {
- const { user } = getRequiredLoaderSession(context);
+export function loader() {
+ const { user } = getLoaderSession();
if (!user || !isAdmin(user)) {
throw redirect('/documents');
diff --git a/apps/remix/app/routes/_authenticated+/documents+/$id._index.tsx b/apps/remix/app/routes/_authenticated+/documents+/$id._index.tsx
index c4a006984..2bb84d6f2 100644
--- a/apps/remix/app/routes/_authenticated+/documents+/$id._index.tsx
+++ b/apps/remix/app/routes/_authenticated+/documents+/$id._index.tsx
@@ -3,7 +3,7 @@ import { useLingui } from '@lingui/react';
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
import { ChevronLeft, Clock9, Users2 } from 'lucide-react';
import { Link, redirect } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import { useSession } from '@documenso/lib/client-only/providers/session';
@@ -34,8 +34,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/$id._index';
-export async function loader({ params, context }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderSession(context);
+export async function loader({ params }: Route.LoaderArgs) {
+ const { user, currentTeam: team } = getLoaderSession();
const { id } = params;
diff --git a/apps/remix/app/routes/_authenticated+/documents+/$id.edit.tsx b/apps/remix/app/routes/_authenticated+/documents+/$id.edit.tsx
index 77b15bb27..9bf123aae 100644
--- a/apps/remix/app/routes/_authenticated+/documents+/$id.edit.tsx
+++ b/apps/remix/app/routes/_authenticated+/documents+/$id.edit.tsx
@@ -2,7 +2,7 @@ import { Plural, Trans } from '@lingui/macro';
import { DocumentStatus as InternalDocumentStatus, TeamMemberRole } from '@prisma/client';
import { ChevronLeft, Users2 } from 'lucide-react';
import { Link, redirect } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
@@ -17,8 +17,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/$id.edit';
-export async function loader({ params, context }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderSession(context);
+export async function loader({ params }: Route.LoaderArgs) {
+ const { user, currentTeam: team } = getLoaderSession();
const { id } = params;
diff --git a/apps/remix/app/routes/_authenticated+/documents+/$id.logs.tsx b/apps/remix/app/routes/_authenticated+/documents+/$id.logs.tsx
index 5dbc00a69..7f887679d 100644
--- a/apps/remix/app/routes/_authenticated+/documents+/$id.logs.tsx
+++ b/apps/remix/app/routes/_authenticated+/documents+/$id.logs.tsx
@@ -5,7 +5,7 @@ import type { Recipient } from '@prisma/client';
import { ChevronLeft } from 'lucide-react';
import { DateTime } from 'luxon';
import { Link, redirect } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
@@ -22,10 +22,10 @@ import { DocumentLogsTable } from '~/components/tables/document-logs-table';
import type { Route } from './+types/$id.logs';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
const { id } = params;
- const { user, currentTeam: team } = getRequiredLoaderSession(context);
+ const { user, currentTeam: team } = getLoaderSession();
const documentId = Number(id);
diff --git a/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx b/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx
index de5eb9375..1276b37d5 100644
--- a/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx
+++ b/apps/remix/app/routes/_authenticated+/settings+/public-profile+/index.tsx
@@ -4,7 +4,7 @@ import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import type { TemplateDirectLink } from '@prisma/client';
import { TemplateType } from '@prisma/client';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { getUserPublicProfile } from '@documenso/lib/server-only/user/get-user-public-profile';
@@ -43,8 +43,8 @@ const teamProfileText = {
templatesSubtitle: msg`Show templates in your team public profile for your audience to sign and get started quickly`,
};
-export async function loader({ context }: Route.LoaderArgs) {
- const { user } = getRequiredLoaderSession(context);
+export async function loader() {
+ const { user } = getLoaderSession();
const { profile } = await getUserPublicProfile({
userId: user.id,
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_index.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_index.tsx
index de665bd2d..0683f5066 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_index.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_index.tsx
@@ -1,13 +1,13 @@
import { redirect } from 'react-router';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
-import type { Route } from './+types/_index';
-
-export function loader({ context }: Route.LoaderArgs) {
- if (!context.session?.currentTeam) {
+export function loader() {
+ const { currentTeam } = getLoaderSession();
+ if (!currentTeam) {
throw redirect('/documents');
}
- throw redirect(formatDocumentsPath(context.session.currentTeam.url));
+ throw redirect(formatDocumentsPath(currentTeam.url));
}
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
index e7abffcfe..a52a32b29 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
@@ -3,7 +3,7 @@ import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { ChevronLeft } from 'lucide-react';
import { Link, Outlet, isRouteErrorResponse, redirect, useNavigate } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import { AppErrorCode } from '@documenso/lib/errors/app-error';
@@ -14,8 +14,8 @@ import { TeamProvider } from '~/providers/team';
import type { Route } from './+types/_layout';
-export const loader = ({ context }: Route.LoaderArgs) => {
- const { currentTeam } = getRequiredLoaderSession(context);
+export const loader = () => {
+ const { currentTeam } = getLoaderSession();
if (!currentTeam) {
throw redirect('/documents');
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx
index ea28f85d7..bef091059 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx
@@ -1,16 +1,14 @@
import { Trans } from '@lingui/macro';
import { Outlet } from 'react-router';
-import { getRequiredLoaderTeamSession } from 'server/utils/get-loader-session';
+import { getLoaderTeamSession } from 'server/utils/get-loader-session';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { TeamSettingsNavDesktop } from '~/components/general/teams/team-settings-nav-desktop';
import { TeamSettingsNavMobile } from '~/components/general/teams/team-settings-nav-mobile';
-import type { Route } from '../+types/_layout';
-
-export function loader({ context }: Route.LoaderArgs) {
- const { currentTeam: team } = getRequiredLoaderTeamSession(context);
+export function loader() {
+ const { currentTeam: team } = getLoaderTeamSession();
if (!team || !canExecuteTeamAction('MANAGE_TEAM', team.currentTeamMember.role)) {
throw new Response(null, { status: 401 }); // Unauthorized.
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/billing.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/billing.tsx
index 5886af1a0..2d3c07fef 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/billing.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/billing.tsx
@@ -1,7 +1,7 @@
import { Plural, Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { DateTime } from 'luxon';
-import { getRequiredLoaderTeamSession } from 'server/utils/get-loader-session';
+import { getLoaderTeamSession } from 'server/utils/get-loader-session';
import type Stripe from 'stripe';
import { match } from 'ts-pattern';
@@ -15,8 +15,8 @@ import { TeamSettingsBillingInvoicesTable } from '~/components/tables/team-setti
import type { Route } from './+types/billing';
-export async function loader({ context }: Route.LoaderArgs) {
- const { currentTeam: team } = getRequiredLoaderTeamSession(context);
+export async function loader() {
+ const { currentTeam: team } = getLoaderTeamSession();
let teamSubscription: Stripe.Subscription | null = null;
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/public-profile.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/public-profile.tsx
index d1954b8ea..dd1c7d5c7 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/public-profile.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/public-profile.tsx
@@ -1,13 +1,11 @@
-import { getRequiredLoaderTeamSession } from 'server/utils/get-loader-session';
+import { getLoaderTeamSession } from 'server/utils/get-loader-session';
import { getTeamPublicProfile } from '@documenso/lib/server-only/team/get-team-public-profile';
import PublicProfilePage from '~/routes/_authenticated+/settings+/public-profile+/index';
-import type { Route } from './+types/public-profile';
-
-export async function loader({ context }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderTeamSession(context);
+export async function loader() {
+ const { user, currentTeam: team } = getLoaderTeamSession();
const { profile } = await getTeamPublicProfile({
userId: user.id,
diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/tokens.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/tokens.tsx
index 05021959d..ae637162f 100644
--- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/tokens.tsx
+++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/tokens.tsx
@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { DateTime } from 'luxon';
-import { getRequiredLoaderTeamSession } from 'server/utils/get-loader-session';
+import { getLoaderTeamSession } from 'server/utils/get-loader-session';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { getTeamTokens } from '@documenso/lib/server-only/public-api/get-all-team-tokens';
@@ -12,8 +12,8 @@ import { ApiTokenForm } from '~/components/forms/token';
import type { Route } from './+types/tokens';
-export async function loader({ context }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderTeamSession(context);
+export async function loader() {
+ const { user, currentTeam: team } = getLoaderTeamSession();
const tokens = await getTeamTokens({ userId: user.id, teamId: team.id }).catch(() => null);
diff --git a/apps/remix/app/routes/_authenticated+/templates+/$id._index.tsx b/apps/remix/app/routes/_authenticated+/templates+/$id._index.tsx
index 52505040a..a99a1cb20 100644
--- a/apps/remix/app/routes/_authenticated+/templates+/$id._index.tsx
+++ b/apps/remix/app/routes/_authenticated+/templates+/$id._index.tsx
@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro';
import { DocumentSigningOrder, SigningStatus } from '@prisma/client';
import { ChevronLeft, LucideEdit } from 'lucide-react';
import { Link, redirect, useNavigate } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
@@ -24,8 +24,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/$id._index';
-export async function loader({ params, context }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderSession(context);
+export async function loader({ params }: Route.LoaderArgs) {
+ const { user, currentTeam: team } = getLoaderSession();
const { id } = params;
diff --git a/apps/remix/app/routes/_authenticated+/templates+/$id.edit.tsx b/apps/remix/app/routes/_authenticated+/templates+/$id.edit.tsx
index 74d75b794..9fb00e6d4 100644
--- a/apps/remix/app/routes/_authenticated+/templates+/$id.edit.tsx
+++ b/apps/remix/app/routes/_authenticated+/templates+/$id.edit.tsx
@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro';
import { ChevronLeft } from 'lucide-react';
import { Link, redirect } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
@@ -15,8 +15,8 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import { TemplateDirectLinkDialogWrapper } from '../../../components/dialogs/template-direct-link-dialog-wrapper';
import type { Route } from './+types/$id.edit';
-export async function loader({ context, params }: Route.LoaderArgs) {
- const { user, currentTeam: team } = getRequiredLoaderSession(context);
+export async function loader({ params }: Route.LoaderArgs) {
+ const { user, currentTeam: team } = getLoaderSession();
const { id } = params;
diff --git a/apps/remix/app/routes/_index.tsx b/apps/remix/app/routes/_index.tsx
index 6bc005462..13a7291e3 100644
--- a/apps/remix/app/routes/_index.tsx
+++ b/apps/remix/app/routes/_index.tsx
@@ -1,9 +1,10 @@
import { redirect } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
-import type { Route } from './+types/_index';
+export function loader() {
+ const session = getOptionalLoaderSession();
-export function loader({ context }: Route.LoaderArgs) {
- if (context.session) {
+ if (session) {
throw redirect('/documents');
}
diff --git a/apps/remix/app/routes/_profile+/_layout.tsx b/apps/remix/app/routes/_profile+/_layout.tsx
index 9c9d883c1..92492a01b 100644
--- a/apps/remix/app/routes/_profile+/_layout.tsx
+++ b/apps/remix/app/routes/_profile+/_layout.tsx
@@ -6,24 +6,15 @@ import { ChevronLeft } from 'lucide-react';
import { Link, Outlet } from 'react-router';
import LogoIcon from '@documenso/assets/logo_icon.png';
+import { useSession } from '@documenso/lib/client-only/providers/session';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Header as AuthenticatedHeader } from '~/components/general/app-header';
-import { Logo } from '~/components/general/branding-logo';
+import { BrandingLogo } from '~/components/general/branding-logo';
-import type { Route } from './+types/_layout';
-
-export function loader({ context }: Route.LoaderArgs) {
- const { session } = context;
-
- return {
- session,
- };
-}
-
-export default function PublicProfileLayout({ loaderData }: Route.ComponentProps) {
- const { session } = loaderData;
+export default function PublicProfileLayout() {
+ const session = useSession();
const [scrollY, setScrollY] = useState(0);
@@ -53,7 +44,7 @@ export default function PublicProfileLayout({ loaderData }: Route.ComponentProps
to="/"
className="focus-visible:ring-ring ring-offset-background rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 md:inline"
>
-
+
context.session?.user !== null)
+ .with(DocumentAccessAuth.ACCOUNT, () => session?.user !== null)
.with(null, () => true)
.exhaustive();
@@ -67,6 +70,7 @@ export default function DirectTemplatePage() {
const data = useSuperLoaderData();
+ // Should not be possible for directLink to be null.
if (!data.isAccessAuthValid) {
return ;
}
diff --git a/apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx b/apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
index 8a21da5b4..c0a12bcdc 100644
--- a/apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
+++ b/apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import { DocumentStatus, SigningStatus } from '@prisma/client';
import { Clock8 } from 'lucide-react';
import { Link, redirect } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import signingCelebration from '@documenso/assets/images/signing-celebration.png';
import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
@@ -25,14 +26,16 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/_index';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
throw new Response('Not Found', { status: 404 });
}
- const user = context.session?.user;
+ const user = session?.user;
const [document, fields, recipient, completedFields] = await Promise.all([
getDocumentAndSenderByToken({
diff --git a/apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx b/apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx
index f5e0b5db3..9acbd3fe2 100644
--- a/apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx
+++ b/apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx
@@ -3,6 +3,7 @@ import { useLingui } from '@lingui/react';
import { DocumentStatus, FieldType, RecipientRole } from '@prisma/client';
import { CheckCircle2, Clock8, FileSearch } from 'lucide-react';
import { Link } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import signingCelebration from '@documenso/assets/images/signing-celebration.png';
@@ -27,14 +28,16 @@ import { DocumentSigningAuthPageView } from '~/components/general/document-signi
import type { Route } from './+types/complete';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
throw new Response('Not Found', { status: 404 });
}
- const user = context.session?.user;
+ const user = session?.user;
const document = await getDocumentAndSenderByToken({
token,
diff --git a/apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx b/apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
index ff5b853e8..1ffc34a1b 100644
--- a/apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
+++ b/apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import { FieldType } from '@prisma/client';
import { XCircle } from 'lucide-react';
import { Link } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
@@ -16,14 +17,16 @@ import { truncateTitle } from '~/utils/truncate-title';
import type { Route } from './+types/rejected';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
throw new Response('Not Found', { status: 404 });
}
- const user = context.session?.user;
+ const user = session?.user;
const document = await getDocumentAndSenderByToken({
token,
diff --git a/apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx b/apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx
index c29011238..675888272 100644
--- a/apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx
+++ b/apps/remix/app/routes/_recipient+/sign.$token+/waiting.tsx
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import type { Team } from '@prisma/client';
import { DocumentStatus } from '@prisma/client';
import { Link, redirect } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
@@ -12,7 +13,9 @@ import { Button } from '@documenso/ui/primitives/button';
import type { Route } from './+types/waiting';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
@@ -34,7 +37,7 @@ export async function loader({ params, context }: Route.LoaderArgs) {
let isOwnerOrTeamMember = false;
- const user = context.session?.user;
+ const user = session?.user;
let team: Team | null = null;
if (user) {
diff --git a/apps/remix/app/routes/_unauthenticated+/signin.tsx b/apps/remix/app/routes/_unauthenticated+/signin.tsx
index 202718dd2..891bbc58c 100644
--- a/apps/remix/app/routes/_unauthenticated+/signin.tsx
+++ b/apps/remix/app/routes/_unauthenticated+/signin.tsx
@@ -1,5 +1,6 @@
import { Trans } from '@lingui/macro';
import { Link, redirect } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import {
IS_GOOGLE_SSO_ENABLED,
@@ -10,14 +11,14 @@ import { env } from '@documenso/lib/utils/env';
import { SignInForm } from '~/components/forms/signin';
-import type { Route } from './+types/signin';
-
export function meta() {
return [{ title: 'Sign In' }];
}
-export function loader({ context }: Route.LoaderArgs) {
- if (context.session) {
+export function loader() {
+ const session = getOptionalLoaderSession();
+
+ if (session) {
throw redirect('/documents');
}
}
diff --git a/apps/remix/app/routes/_unauthenticated+/team.decline.$token.tsx b/apps/remix/app/routes/_unauthenticated+/team.decline.$token.tsx
index ad3363ded..0af2fd569 100644
--- a/apps/remix/app/routes/_unauthenticated+/team.decline.$token.tsx
+++ b/apps/remix/app/routes/_unauthenticated+/team.decline.$token.tsx
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import { TeamMemberInviteStatus } from '@prisma/client';
import { DateTime } from 'luxon';
import { Link } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { declineTeamInvitation } from '@documenso/lib/server-only/team/decline-team-invitation';
@@ -11,7 +12,9 @@ import { Button } from '@documenso/ui/primitives/button';
import type { Route } from './+types/team.decline.$token';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
@@ -71,7 +74,7 @@ export async function loader({ params, context }: Route.LoaderArgs) {
} as const;
}
- const isSessionUserTheInvitedUser = user.id === context.session?.user.id;
+ const isSessionUserTheInvitedUser = user.id === session?.user.id;
return {
state: 'Success',
diff --git a/apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx b/apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
index a06f98dcb..27064a9e2 100644
--- a/apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
+++ b/apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import { TeamMemberInviteStatus } from '@prisma/client';
import { DateTime } from 'luxon';
import { Link } from 'react-router';
+import { getOptionalLoaderSession } from 'server/utils/get-loader-session';
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
import { acceptTeamInvitation } from '@documenso/lib/server-only/team/accept-team-invitation';
@@ -11,7 +12,9 @@ import { Button } from '@documenso/ui/primitives/button';
import type { Route } from './+types/team.invite.$token';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
+ const session = getOptionalLoaderSession();
+
const { token } = params;
if (!token) {
@@ -74,7 +77,7 @@ export async function loader({ params, context }: Route.LoaderArgs) {
} as const;
}
- const isSessionUserTheInvitedUser = user.id === context.session?.user.id;
+ const isSessionUserTheInvitedUser = user.id === session?.user.id;
return {
state: 'Success',
diff --git a/apps/remix/app/routes/embed+/direct.$url.tsx b/apps/remix/app/routes/embed+/direct.$url.tsx
index 2f5387233..dec8b0584 100644
--- a/apps/remix/app/routes/embed+/direct.$url.tsx
+++ b/apps/remix/app/routes/embed+/direct.$url.tsx
@@ -1,5 +1,5 @@
import { data } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
@@ -17,7 +17,7 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/direct.$url';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
if (!params.url) {
throw new Response('Not found', { status: 404 });
}
@@ -48,7 +48,7 @@ export async function loader({ params, context }: Route.LoaderArgs) {
);
}
- const { user } = getRequiredLoaderSession(context);
+ const { user } = getLoaderSession();
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
documentAuth: template.authOptions,
diff --git a/apps/remix/app/routes/embed+/sign.$url.tsx b/apps/remix/app/routes/embed+/sign.$url.tsx
index 432f85a3b..ef11dcfef 100644
--- a/apps/remix/app/routes/embed+/sign.$url.tsx
+++ b/apps/remix/app/routes/embed+/sign.$url.tsx
@@ -1,6 +1,6 @@
import { DocumentStatus } from '@prisma/client';
import { data } from 'react-router';
-import { getRequiredLoaderSession } from 'server/utils/get-loader-session';
+import { getLoaderSession } from 'server/utils/get-loader-session';
import { match } from 'ts-pattern';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
@@ -20,14 +20,14 @@ import { superLoaderJson, useSuperLoaderData } from '~/utils/super-json-loader';
import type { Route } from './+types/sign.$url';
-export async function loader({ params, context }: Route.LoaderArgs) {
+export async function loader({ params }: Route.LoaderArgs) {
if (!params.url) {
throw new Response('Not found', { status: 404 });
}
const token = params.url;
- const { user } = getRequiredLoaderSession(context);
+ const { user } = getLoaderSession();
const [document, fields, recipient] = await Promise.all([
getDocumentAndSenderByToken({
diff --git a/apps/remix/server/load-context.ts b/apps/remix/server/context.ts
similarity index 75%
rename from apps/remix/server/load-context.ts
rename to apps/remix/server/context.ts
index ff1726384..dbc1c48de 100644
--- a/apps/remix/server/load-context.ts
+++ b/apps/remix/server/context.ts
@@ -1,24 +1,27 @@
+import type { Context, Next } from 'hono';
+
import { extractSessionCookieFromHeaders } from '@documenso/auth/server/lib/session/session-cookies';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
+import type { AppSession } from '@documenso/lib/client-only/providers/session';
import { type TGetTeamByUrlResponse, getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { type TGetTeamsResponse, getTeams } from '@documenso/lib/server-only/team/get-teams';
-import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
+import {
+ type RequestMetadata,
+ extractRequestMetadata,
+} from '@documenso/lib/universal/extract-request-metadata';
import { AppLogger } from '@documenso/lib/utils/debugger';
-type GetLoadContextArgs = {
- request: Request;
+const logger = new AppLogger('Middleware');
+
+export type AppContext = {
+ requestMetadata: RequestMetadata;
+ session: AppSession | null;
};
-declare module 'react-router' {
- interface AppLoadContext extends Awaited> {}
-}
-
-const logger = new AppLogger('Context');
-
-export async function getLoadContext(args: GetLoadContextArgs) {
+export const appContext = async (c: Context, next: Next) => {
const initTime = Date.now();
- const request = args.request;
+ const request = c.req.raw;
const url = new URL(request.url);
const noSessionCookie = extractSessionCookieFromHeaders(request.headers) === null;
@@ -26,10 +29,12 @@ export async function getLoadContext(args: GetLoadContextArgs) {
if (!isPageRequest(request) || noSessionCookie || blacklistedPathsRegex.test(url.pathname)) {
logger.log('Pathname ignored', url.pathname);
- return {
+ setAppContext(c, {
requestMetadata: extractRequestMetadata(request),
session: null,
- };
+ });
+
+ return next();
}
const splitUrl = url.pathname.replace('.data', '').split('/');
@@ -37,7 +42,7 @@ export async function getLoadContext(args: GetLoadContextArgs) {
let team: TGetTeamByUrlResponse | null = null;
let teams: TGetTeamsResponse = [];
- const session = await getSession(args.request);
+ const session = await getSession(c);
if (session.isAuthenticated) {
let teamUrl = null;
@@ -58,7 +63,7 @@ export async function getLoadContext(args: GetLoadContextArgs) {
const endTime = Date.now();
logger.log(`Pathname accepted in ${endTime - initTime}ms`, url.pathname);
- return {
+ setAppContext(c, {
requestMetadata: extractRequestMetadata(request),
session: session.isAuthenticated
? {
@@ -68,8 +73,14 @@ export async function getLoadContext(args: GetLoadContextArgs) {
teams,
}
: null,
- };
-}
+ });
+
+ return next();
+};
+
+const setAppContext = (c: Context, context: AppContext) => {
+ c.set('context', context);
+};
const isPageRequest = (request: Request) => {
const url = new URL(request.url);
diff --git a/apps/remix/server/index.ts b/apps/remix/server/index.ts
index 421968034..a3a99f0ef 100644
--- a/apps/remix/server/index.ts
+++ b/apps/remix/server/index.ts
@@ -1,4 +1,5 @@
import { Hono } from 'hono';
+import { contextStorage } from 'hono/context-storage';
import { PDFDocument } from 'pdf-lib';
import { tsRestHonoApp } from '@documenso/api/hono';
@@ -11,10 +12,21 @@ import { putFile } from '@documenso/lib/universal/upload/put-file';
import { getPresignGetUrl } from '@documenso/lib/universal/upload/server-actions';
import { openApiDocument } from '@documenso/trpc/server/open-api';
+import { type AppContext, appContext } from './context';
import { openApiTrpcServerHandler } from './trpc/hono-trpc-open-api';
import { reactRouterTrpcServer } from './trpc/hono-trpc-remix';
-const app = new Hono();
+export interface HonoEnv {
+ Variables: {
+ context: AppContext;
+ };
+}
+
+const app = new Hono();
+
+app.use(contextStorage());
+
+app.use(appContext);
// App middleware.
// app.use('*', appMiddleware);
diff --git a/apps/remix/server/trpc/hono-trpc-open-api.ts b/apps/remix/server/trpc/hono-trpc-open-api.ts
index b5f3b1adc..95cd4c1c8 100644
--- a/apps/remix/server/trpc/hono-trpc-open-api.ts
+++ b/apps/remix/server/trpc/hono-trpc-open-api.ts
@@ -12,6 +12,7 @@ export const openApiTrpcServerHandler = async (c: Context) => {
endpoint: API_V2_BETA_URL,
router: appRouter,
// Todo: Test this, since it's not using the createContext params.
+ // Todo: Reduce calls since we fetch on most request? maybe
createContext: async () => createTrpcContext({ c, requestSource: 'apiV2' }),
req: c.req.raw,
onError: (opts) => handleTrpcRouterError(opts, 'apiV2'),
diff --git a/apps/remix/server/utils/get-loader-session.ts b/apps/remix/server/utils/get-loader-session.ts
index ad4345f8e..8a1fdc347 100644
--- a/apps/remix/server/utils/get-loader-session.ts
+++ b/apps/remix/server/utils/get-loader-session.ts
@@ -1,31 +1,43 @@
-import type { AppLoadContext } from 'react-router';
+import { getContext } from 'hono/context-storage';
import { redirect } from 'react-router';
+import type { HonoEnv } from 'server';
+
+import type { AppSession } from '@documenso/lib/client-only/providers/session';
/**
* Returns the session context or throws a redirect to signin if it is not present.
*/
-export const getRequiredLoaderSession = (context: AppLoadContext) => {
- if (!context.session) {
+export const getLoaderSession = (): AppSession => {
+ const session = getOptionalLoaderSession();
+
+ if (!session) {
throw redirect('/signin'); // Todo: Maybe add a redirect cookie to come back?
}
+ return session;
+};
+
+export const getOptionalLoaderSession = (): AppSession | null => {
+ const { context } = getContext().var;
return context.session;
};
/**
* Returns the team session context or throws a redirect to signin if it is not present.
*/
-export const getRequiredLoaderTeamSession = (context: AppLoadContext) => {
- if (!context.session) {
+export const getLoaderTeamSession = () => {
+ const session = getOptionalLoaderSession();
+
+ if (!session) {
throw redirect('/signin'); // Todo: Maybe add a redirect cookie to come back?
}
- if (!context.session.currentTeam) {
+ if (!session.currentTeam) {
throw new Response(null, { status: 404 }); // Todo: Test that 404 page shows up.
}
return {
- ...context.session,
- currentTeam: context.session.currentTeam,
+ ...session,
+ currentTeam: session.currentTeam,
};
};
diff --git a/apps/remix/vite.config.ts b/apps/remix/vite.config.ts
index 01ece6b11..b5f6572c4 100644
--- a/apps/remix/vite.config.ts
+++ b/apps/remix/vite.config.ts
@@ -8,8 +8,6 @@ import { defineConfig, loadEnv } from 'vite';
import macrosPlugin from 'vite-plugin-babel-macros';
import tsconfigPaths from 'vite-tsconfig-paths';
-import { getLoadContext } from './server/load-context';
-
export default defineConfig({
envDir: path.join(__dirname, '../../'),
envPrefix: '__DO_NOT_USE_OR_YOU_WILL_BE_FIRED__',
@@ -54,7 +52,6 @@ export default defineConfig({
lingui(),
macrosPlugin(),
serverAdapter({
- getLoadContext,
entry: 'server/index.ts',
}),
tsconfigPaths(),
diff --git a/packages/lib/client-only/providers/session.tsx b/packages/lib/client-only/providers/session.tsx
index cc0539c2f..cb65bdaaa 100644
--- a/packages/lib/client-only/providers/session.tsx
+++ b/packages/lib/client-only/providers/session.tsx
@@ -1,25 +1,30 @@
import { createContext, useContext } from 'react';
import React from 'react';
-import type { Session, User } from '@prisma/client';
+import type { Session, User } from '@documenso/prisma/client';
-interface AuthProviderProps {
- children: React.ReactNode;
- session: DocumensoSession | null;
-}
+import type { TGetTeamByUrlResponse } from '../../server-only/team/get-team';
+import type { TGetTeamsResponse } from '../../server-only/team/get-teams';
-export type DocumensoSession = {
- user: User; // Todo: Exclude password
+export type AppSession = {
session: Session;
+ user: User; // Todo: Remove password, and redundant fields.
+ currentTeam: TGetTeamByUrlResponse | null;
+ teams: TGetTeamsResponse;
};
-const SessionContext = createContext(null);
+interface SessionProviderProps {
+ children: React.ReactNode;
+ session: AppSession | null;
+}
+
+const SessionContext = createContext(null);
export const useSession = () => {
const context = useContext(SessionContext);
if (!context) {
- throw new Error('useAuth must be used within a AuthProvider');
+ throw new Error('useSession must be used within a SessionProvider');
}
return context;
@@ -34,6 +39,6 @@ export const useOptionalSession = () => {
);
};
-export const SessionProvider = ({ children, session }: AuthProviderProps) => {
+export const SessionProvider = ({ children, session }: SessionProviderProps) => {
return {children};
};