Skip to content

Commit

Permalink
♻️ Make user session optional (#1515)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevella authored Jan 27, 2025
1 parent f6a0bca commit 58d5c42
Show file tree
Hide file tree
Showing 31 changed files with 335 additions and 541 deletions.
3 changes: 2 additions & 1 deletion apps/web/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ SECRET_PASSWORD=abcdef1234567890abcdef1234567890
DATABASE_URL=postgres://postgres:postgres@localhost:5450/rallly
SUPPORT_EMAIL=[email protected]
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_PORT=1025
QUICK_CREATE_ENABLED=true
2 changes: 1 addition & 1 deletion apps/web/declarations/next-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ declare module "next-auth" {
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
user: {
user?: {
id: string;
timeZone?: string | null;
timeFormat?: TimeFormat | null;
Expand Down
171 changes: 63 additions & 108 deletions apps/web/src/app/[locale]/(admin)/settings/profile/profile-page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
"use client";
import { Alert, AlertDescription, AlertTitle } from "@rallly/ui/alert";
import { Button } from "@rallly/ui/button";
import { DialogTrigger } from "@rallly/ui/dialog";
import { Input } from "@rallly/ui/input";
import { Label } from "@rallly/ui/label";
import { InfoIcon, LogOutIcon, TrashIcon, UserXIcon } from "lucide-react";
import { LogOutIcon, TrashIcon } from "lucide-react";
import Head from "next/head";
import Link from "next/link";
import { useTranslation } from "next-i18next";

import { DeleteAccountDialog } from "@/app/[locale]/(admin)/settings/profile/delete-account-dialog";
Expand All @@ -31,112 +27,71 @@ export const ProfilePage = () => {
<Head>
<title>{t("profile")}</title>
</Head>
{user.isGuest ? (
<SettingsContent>
<SettingsSection
title={<Trans i18nKey="profile" />}
description={<Trans i18nKey="profileDescription" />}
>
<Label className="mb-2.5">
<Trans i18nKey="userId" defaults="User ID" />
</Label>
<Input
className="w-full"
value={user.id.substring(0, 10)}
readOnly
disabled
<SettingsContent>
<SettingsSection
title={<Trans i18nKey="profile" defaults="Profile" />}
description={
<Trans
i18nKey="profileDescription"
defaults="Set your public profile information"
/>
<Alert className="mt-4" icon={InfoIcon}>
<AlertTitle>
<Trans i18nKey="aboutGuest" defaults="Guest User" />
</AlertTitle>
<AlertDescription>
}
>
<ProfileSettings />
</SettingsSection>
<SettingsSection
title={
<Trans i18nKey="profileEmailAddress" defaults="Email Address" />
}
description={
<Trans
i18nKey="profileEmailAddressDescription"
defaults="Your email address is used to log in to your account"
/>
}
>
<ProfileEmailAddress />
</SettingsSection>
<hr />

<SettingsSection
title={<Trans i18nKey="logout" />}
description={
<Trans
i18nKey="logoutDescription"
defaults="Sign out of your existing session"
/>
}
>
<LogoutButton>
<LogOutIcon className="size-4" />
<Trans i18nKey="logout" defaults="Logout" />
</LogoutButton>
</SettingsSection>
{user.email ? (
<>
<hr />
<SettingsSection
title={<Trans i18nKey="dangerZone" defaults="Danger Zone" />}
description={
<Trans
i18nKey="aboutGuestDescription"
defaults="Profile settings are not available for guest users. <0>Sign in</0> to your existing account or <1>create a new account</1> to customize your profile."
components={[
<Link className="text-link" key={0} href="/login" />,
<Link className="text-link" key={1} href="/register" />,
]}
i18nKey="dangerZoneAccount"
defaults="Delete your account permanently. This action cannot be undone."
/>
</AlertDescription>
</Alert>
<LogoutButton className="mt-6" variant="destructive">
<UserXIcon className="size-4" />
<Trans i18nKey="forgetMe" />
</LogoutButton>
</SettingsSection>
</SettingsContent>
) : (
<SettingsContent>
<SettingsSection
title={<Trans i18nKey="profile" defaults="Profile" />}
description={
<Trans
i18nKey="profileDescription"
defaults="Set your public profile information"
/>
}
>
<ProfileSettings />
</SettingsSection>
<SettingsSection
title={
<Trans i18nKey="profileEmailAddress" defaults="Email Address" />
}
description={
<Trans
i18nKey="profileEmailAddressDescription"
defaults="Your email address is used to log in to your account"
/>
}
>
<ProfileEmailAddress />
</SettingsSection>
<hr />

<SettingsSection
title={<Trans i18nKey="logout" />}
description={
<Trans
i18nKey="logoutDescription"
defaults="Sign out of your existing session"
/>
}
>
<LogoutButton>
<LogOutIcon className="size-4" />
<Trans i18nKey="logout" defaults="Logout" />
</LogoutButton>
</SettingsSection>
{user.email ? (
<>
<hr />
<SettingsSection
title={<Trans i18nKey="dangerZone" defaults="Danger Zone" />}
description={
<Trans
i18nKey="dangerZoneAccount"
defaults="Delete your account permanently. This action cannot be undone."
/>
}
>
<DeleteAccountDialog email={user.email}>
<DialogTrigger asChild>
<Button className="text-destructive">
<TrashIcon className="size-4" />
<Trans
i18nKey="deleteAccount"
defaults="Delete Account"
/>
</Button>
</DialogTrigger>
</DeleteAccountDialog>
</SettingsSection>
</>
) : null}
</SettingsContent>
)}
}
>
<DeleteAccountDialog email={user.email}>
<DialogTrigger asChild>
<Button className="text-destructive">
<TrashIcon className="size-4" />
<Trans i18nKey="deleteAccount" defaults="Delete Account" />
</Button>
</DialogTrigger>
</DeleteAccountDialog>
</SettingsSection>
</>
) : null}
</SettingsContent>
</Settings>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const LoginPage = ({ magicLink, email }: PageProps) => {
if (!data.url.includes("auth/error")) {
// if login was successful, update the session
const updatedSession = await session.update();
if (updatedSession) {
if (updatedSession?.user) {
// identify the user in posthog
posthog?.identify(updatedSession.user.id, {
email: updatedSession.user.email,
Expand Down
10 changes: 0 additions & 10 deletions apps/web/src/app/api/logout/route.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/web/src/app/api/notifications/unsubscribe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const GET = async (req: NextRequest) => {

const session = await getServerSession();

if (!session || !session.user.email) {
if (!session || !session.user?.email) {
return NextResponse.redirect(new URL("/login", req.url));
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/api/stripe/checkout/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export async function POST(request: NextRequest) {
Object.fromEntries(formData.entries()),
);

if (!userSession || userSession.user.email === null) {
if (!userSession?.user || userSession.user?.email === null) {
// You need to be logged in to subscribe
return NextResponse.redirect(
new URL(
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/api/stripe/portal/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function GET(request: NextRequest) {
}
} else {
const userSession = await getServerSession();
if (!userSession || userSession.user.email === null) {
if (!userSession?.user || userSession.user.email === null) {
Sentry.captureException(new Error("User not logged in"));
return NextResponse.json(
{ error: "User not logged in" },
Expand Down
37 changes: 18 additions & 19 deletions apps/web/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
import * as Sentry from "@sentry/nextjs";
import { TRPCError } from "@trpc/server";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { ipAddress } from "@vercel/functions";
import type { NextRequest } from "next/server";

import { getLocaleFromHeader } from "@/app/guest";
import { getServerSession } from "@/auth";
import type { TRPCContext } from "@/trpc/context";
import { appRouter } from "@/trpc/routers";
import { getEmailClient } from "@/utils/emails";

const handler = (request: Request) => {
const handler = (req: NextRequest) => {
return fetchRequestHandler({
endpoint: "/api/trpc",
req: request,
req,
router: appRouter,
createContext: async () => {
const session = await getServerSession();

if (!session?.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Unauthorized",
});
}
const locale = await getLocaleFromHeader(req);
const user = session?.user
? {
id: session.user.id,
isGuest: !session.user.email,
locale: session.user.locale ?? undefined,
image: session.user.image ?? undefined,
getEmailClient: () =>
getEmailClient(session.user?.locale ?? undefined),
}
: undefined;

return {
user: {
id: session.user.id,
isGuest: session.user.email === null,
locale: session.user.locale ?? undefined,
image: session.user.image ?? undefined,
getEmailClient: () =>
getEmailClient(session.user?.locale ?? undefined),
},
ip: ipAddress(request) ?? undefined,
user,
locale,
ip: ipAddress(req) ?? undefined,
} satisfies TRPCContext;
},
onError({ error }) {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/api/user/verify-email-change/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const GET = async (request: NextRequest) => {

const session = await getServerSession();

if (!session || !session.user.email) {
if (!session?.user || !session.user.email) {
return NextResponse.redirect(
new URL(`/login?callbackUrl=${request.url}`, request.url),
);
Expand Down
Loading

0 comments on commit 58d5c42

Please sign in to comment.