diff --git a/app/(auth)/reset-password/page.tsx b/app/(auth)/reset-password/page.tsx index e25bcb88..a804d27a 100644 --- a/app/(auth)/reset-password/page.tsx +++ b/app/(auth)/reset-password/page.tsx @@ -1,15 +1,59 @@ -import {PageParams} from "@/types/next"; -import {Metadata} from "next"; -import {ResetPasswordSection} from "@/components/wrappers/auth/reset-password/reset-password-section"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { ResetPasswordForm } from "@/components/wrappers/auth/login/reset-password-form/reset-password-form"; +import { redirect } from "next/navigation"; +import { auth } from "@/lib/auth/auth"; +import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar"; -export const metadata: Metadata = { - title: "Reset Password", -}; +export default async function RoutePage(props: { searchParams: Promise<{ token: string | undefined }> }) { + + const { token } = await props.searchParams; + + if (!token) { + return redirect(`/login?error=invalid_or_expired_token`); + } + + const verification = await (await auth.$context).internalAdapter.findVerificationValue(`reset-password:${token}`); + + if (!verification || verification.expiresAt < new Date()) { + return redirect(`/login?error=invalid_or_expired_token`); + } + + const user = await (await auth.$context).internalAdapter.findUserById(verification.value); -export default async function RoutePage(props: PageParams<{}>) { return ( -
Please enter your new password below.
+Level: ${payload.level}
-//${payload.message.replace(/\n/g, '
')}
${JSON.stringify(payload.data, null, 2)}` : ''}
-// `;
-//
-// const info = await transporter.sendMail({
-// from: config.from,
-// to: Array.isArray(config.to) ? config.to.join(', ') : config.to,
-// // to: config.from,
-// subject: `[${payload.level.toUpperCase()}] ${payload.title}`,
-// html,
-// // subject: "Portabase",
-// // html: await render(TestEmailSettings(), {}),
-// });
-//
-// console.log(info);
-//
-// return {
-// success: true,
-// provider: 'smtp',
-// // message: `Email sent: ${info.messageId}`,
-// message: `Email sent: ${config.to}`,
-// response: info,
-// };
-// }
-
-
"use server";
import { render } from "@react-email/render";
import nodemailer from "nodemailer";
import type { EventPayload, DispatchResult } from '../types';
import EmailNotification from "@/components/emails/email-notification";
-import EmailResetPassword from "@/components/emails/email-reset-password";
export async function sendSmtp(
config: {
diff --git a/src/lib/auth/auth.ts b/src/lib/auth/auth.ts
index ea066832..6a393eb7 100644
--- a/src/lib/auth/auth.ts
+++ b/src/lib/auth/auth.ts
@@ -11,10 +11,12 @@ import {count, eq} from "drizzle-orm";
import {MemberWithUser, OrganizationWithMembersAndUsers} from "@/db/schema/03_organization";
import {sendEmail} from "@/lib/email/email-helper";
import {render} from "@react-email/render";
-import EmailResetPassword from "@/components/emails/email-reset-password";
import {SUPPORTED_PROVIDERS} from "../../../portabase.config";
import {withUpdatedAt} from "@/db/utils";
-import EmailVerification from "@/components/emails/email-verification";
+import EmailVerification from "@/components/emails/auth/email-verification";
+import EmailForgotPassword from "@/components/emails/auth/email-forgot-password";
+import {getDeviceDetails} from "@/utils/detection";
+import EmailNewLogin from "@/components/emails/auth/email-new-login";
export const auth = betterAuth({
database: drizzleAdapter(db, {
@@ -27,7 +29,7 @@ export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
- sendResetPassword: async ({user, url, token}, request) => {
+ sendResetPassword: async ({user, token}, request) => {
const [updatedUser] = await db.update(drizzleDb.schemas.user).set(withUpdatedAt({
emailVerified: true,
@@ -36,7 +38,13 @@ export const auth = betterAuth({
await sendEmail({
to: user.email,
subject: "Reset your password",
- html: await render(EmailResetPassword({url: url})),
+ html: await render(
+ EmailForgotPassword({
+ firstname: user.name!,
+ token,
+ }),
+ {}
+ ),
});
},
@@ -71,8 +79,6 @@ export const auth = betterAuth({
});
},
},
-
-
socialProviders: SUPPORTED_PROVIDERS.reduce((acc: any, provider: any) => {
if (provider.id === "credential") return acc;
if (provider.id === "google") {
@@ -89,7 +95,6 @@ export const auth = betterAuth({
},
},
-
plugins: [
openAPI(),
nextCookies(),
@@ -176,7 +181,6 @@ export const auth = betterAuth({
} else {
console.warn("Default organization not found. Cannot assign member.");
}
-
},
},
},
@@ -185,38 +189,42 @@ export const auth = betterAuth({
before: async (session, context) => {
const userId = session.userId;
-
let memberships = await db.query.member.findMany({
where: eq(drizzleDb.schemas.member.userId, userId),
});
- // if (!memberships.length) {
- // const defaultOrgSlug = "default";
- // const defaultOrg = await db.query.organization.findFirst({
- // where: eq(drizzleDb.schemas.organization.slug, defaultOrgSlug),
- // });
- //
- // if (!defaultOrg) {
- // throw new Error("No organization found. Cannot assign member.");
- // }
- //
- // await db.insert(drizzleDb.schemas.member).values({
- // userId,
- // organizationId: defaultOrg.id,
- // role: "member",
- // });
- //
- // memberships = await db.query.member.findMany({
- // where: eq(drizzleDb.schemas.member.userId, userId),
- // });
- // }
-
return {
data: {
activeOrganizationId: memberships[0].organizationId,
},
};
},
+ after: async (session) => {
+ const user = await db.query.user.findFirst({
+ where: eq(drizzleDb.schemas.user.id, session.userId),
+ });
+
+ if (user && user.role != "pending") {
+ const deviceInfo = getDeviceDetails(session.userAgent);
+ await sendEmail({
+ to: user.email,
+ subject: "New login to your account",
+ html: await render(
+ EmailNewLogin({
+ firstname: user.name!,
+ os: deviceInfo.os,
+ browser: deviceInfo.browser,
+ ipAddress: session.ipAddress!,
+ }),
+ {}
+ ),
+ });
+
+ (await auth.$context).internalAdapter.updateUser(user.id, {
+ lastConnectedAt: new Date(),
+ });
+ }
+ },
},
},