Start working on the profile refactoring.

This commit is contained in:
charlesgauthereau
2025-12-21 16:55:12 +01:00
parent 428782e8a4
commit 116229a29e
77 changed files with 5076 additions and 509 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
"use client"
import {createAuthClient} from "better-auth/react";
import {adminClient, inferAdditionalFields, organizationClient} from "better-auth/client/plugins";
import {adminClient, inferAdditionalFields, organizationClient, twoFactorClient} from "better-auth/client/plugins";
import {ac, user, admin as adminRole, pending, superadmin, orgAdmin, orgMember, orgOwner} from "./permissions";
import {auth} from "@/lib/auth/auth";
import {getServerUrl} from "@/utils/get-server-url";
@@ -12,6 +12,7 @@ const {PROJECT_URL} = await res.json();
export const authClient = createAuthClient({
baseURL: PROJECT_URL,
plugins: [
twoFactorClient(),
organizationClient({
ac,
roles: {
@@ -34,4 +35,4 @@ export const authClient = createAuthClient({
});
export const {signIn, signOut, signUp, useSession, listAccounts, admin} = authClient;
export const {signIn, signOut, signUp, useSession, listAccounts, admin, requestPasswordReset} = authClient;
+59 -14
View File
@@ -4,7 +4,7 @@ import * as drizzleDb from "@/db";
import {db} from "@/db";
import {env} from "@/env.mjs";
import {nextCookies} from "better-auth/next-js";
import {admin as adminPlugin, openAPI, Organization, organization} from "better-auth/plugins";
import {admin as adminPlugin, openAPI, Organization, organization, twoFactor} from "better-auth/plugins";
import {ac, admin, orgAdmin, orgMember, orgOwner, pending, superadmin, user} from "@/lib/auth/permissions";
import {headers} from "next/headers";
import {count, eq} from "drizzle-orm";
@@ -12,6 +12,9 @@ import {MemberWithUser, OrganizationWithMembersAndUsers} from "@/db/schema/03_or
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";
export const auth = betterAuth({
database: drizzleAdapter(db, {
@@ -23,34 +26,73 @@ export const auth = betterAuth({
enabled: true,
requireEmailVerification: false,
sendResetPassword: async ({user, url, token}, request) => {
const [updatedUser] = await db.update(drizzleDb.schemas.user).set(withUpdatedAt({
emailVerified: true,
})).where(eq(drizzleDb.schemas.user.id, user.id)).returning();
await sendEmail({
to: user.email,
subject: "Reset your password",
html: await render(EmailResetPassword({url: url})),
});
},
onPasswordReset: async ({user}, request) => {
console.log(`Password for user ${user.email} has been reset.`);
},
/*
async sendVerificationEmail(data, request) {
// Send an email to the user with a link to verify their email
},
async verifyEmail(data, request) {
// Verify the email address
},*/
},
socialProviders: {
google: {
clientId: env.AUTH_GOOGLE_ID!,
clientSecret: env.AUTH_GOOGLE_SECRET!,
emailVerification: {
async sendVerificationEmail({user, token, url}) {
await sendEmail({
to: user.email,
subject: "Portabase Email Verification",
html: await render(EmailVerification({
firstname: user.name,
url: url
})),
});
await (
await auth.$context
).internalAdapter.updateUser(user.id, {
emailVerified: false,
});
},
async afterEmailVerification(user) {
await (
await auth.$context
).internalAdapter.updateUser(user.id, {
emailVerified: true,
});
},
},
socialProviders: SUPPORTED_PROVIDERS.reduce((acc: any, provider: any) => {
if (provider.id === "credential") return acc;
if (provider.id === "google") {
acc.google = {
clientId: env.AUTH_GOOGLE_ID! as string,
clientSecret: env.AUTH_GOOGLE_SECRET! as string,
};
}
return acc;
}, {}),
account: {
accountLinking: {
enabled: true,
},
},
plugins: [
openAPI(),
nextCookies(),
twoFactor(),
organization({
ac,
roles: {
@@ -80,6 +122,9 @@ export const auth = betterAuth({
deleteUser: {
enabled: true,
},
changeEmail: {
enabled: true,
},
additionalFields: {
deletedAt: {
type: "number",
+24
View File
@@ -0,0 +1,24 @@
import { z } from "zod";
export const zString = () =>
z.string();
export const zEnum = <T extends [string, ...string[]]>(values: T) => z.enum(values, { message: "Field required" });
export const zEmail = () => z.string().email({ message: "Invalid email" });
const passwordRegex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-])/;
export const zPassword = () => zString().min(8, { message: "New Password too short" }).regex(passwordRegex, { message: "New password too weak" });
export const zDate = () =>
z.preprocess(
(arg) => {
if (typeof arg === "string" || arg instanceof Date) {
const date = new Date(arg);
return isNaN(date.getTime()) ? undefined : date;
}
return undefined;
},
z.date()
);