admin: Superforms + Zod validation, three-address-defaults schema, profiles toast fix

- Add Superforms + Zod 4 for all admin form validation
- Replace address isDefault with three separate boolean defaults (Mailing, Billing, Shipping)
- Update profiles page with three-checkbox address form and toast improvements
- Convert all admin page server actions to pass request directly to crud
- Export superFormErrorsToFieldErrors helper for profiles server actions
- Regenerate Prisma client for new schema fields
This commit is contained in:
NeilClack
2026-07-07 13:36:23 -05:00
parent 38347f5f24
commit 38d1752f5b
98 changed files with 3408 additions and 1758 deletions
@@ -18,19 +18,19 @@ description: Configure email verification, implement password reset flows, set p
Configure `emailVerification.sendVerificationEmail` to verify user email addresses.
```ts
import { betterAuth } from "better-auth";
import { sendEmail } from "./email"; // your email sending function
import { betterAuth } from 'better-auth';
import { sendEmail } from './email'; // your email sending function
export const auth = betterAuth({
emailVerification: {
sendVerificationEmail: async ({ user, url, token }, request) => {
await sendEmail({
to: user.email,
subject: "Verify your email address",
text: `Click the link to verify your email: ${url}`,
});
},
},
emailVerification: {
sendVerificationEmail: async ({ user, url, token }, request) => {
await sendEmail({
to: user.email,
subject: 'Verify your email address',
text: `Click the link to verify your email: ${url}`
});
}
}
});
```
@@ -42,9 +42,9 @@ For stricter security, enable `emailAndPassword.requireEmailVerification` to blo
```ts
export const auth = betterAuth({
emailAndPassword: {
requireEmailVerification: true,
},
emailAndPassword: {
requireEmailVerification: true
}
});
```
@@ -60,7 +60,7 @@ Always use absolute URLs (including the origin) for callback URLs in sign-up and
```ts
const { data, error } = await authClient.signUp.email({
callbackURL: "https://example.com/callback", // absolute URL with origin
callbackURL: 'https://example.com/callback' // absolute URL with origin
});
```
@@ -69,26 +69,26 @@ const { data, error } = await authClient.signUp.email({
Provide `sendResetPassword` in the email and password config to enable password resets.
```ts
import { betterAuth } from "better-auth";
import { sendEmail } from "./email"; // your email sending function
import { betterAuth } from 'better-auth';
import { sendEmail } from './email'; // your email sending function
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
// Custom email sending function to send reset-password email
sendResetPassword: async ({ user, url, token }, request) => {
void sendEmail({
to: user.email,
subject: "Reset your password",
text: `Click the link to reset your password: ${url}`,
});
},
// Optional event hook
onPasswordReset: async ({ user }, request) => {
// your logic here
console.log(`Password for user ${user.email} has been reset.`);
},
},
emailAndPassword: {
enabled: true,
// Custom email sending function to send reset-password email
sendResetPassword: async ({ user, url, token }, request) => {
void sendEmail({
to: user.email,
subject: 'Reset your password',
text: `Click the link to reset your password: ${url}`
});
},
// Optional event hook
onPasswordReset: async ({ user }, request) => {
// your logic here
console.log(`Password for user ${user.email} has been reset.`);
}
}
});
```
@@ -100,14 +100,14 @@ On serverless platforms, configure a background task handler:
```ts
export const auth = betterAuth({
advanced: {
backgroundTasks: {
handler: (promise) => {
// Use platform-specific methods like waitUntil
waitUntil(promise);
},
},
},
advanced: {
backgroundTasks: {
handler: (promise) => {
// Use platform-specific methods like waitUntil
waitUntil(promise);
}
}
}
});
```
@@ -117,10 +117,10 @@ Tokens expire after 1 hour by default. Configure with `resetPasswordTokenExpires
```ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes
},
emailAndPassword: {
enabled: true,
resetPasswordTokenExpiresIn: 60 * 30 // 30 minutes
}
});
```
@@ -132,10 +132,10 @@ Enable `revokeSessionsOnPasswordReset` to invalidate all existing sessions on pa
```ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
revokeSessionsOnPasswordReset: true,
},
emailAndPassword: {
enabled: true,
revokeSessionsOnPasswordReset: true
}
});
```
@@ -145,11 +145,11 @@ Password length limits (configurable):
```ts
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
minPasswordLength: 12,
maxPasswordLength: 256,
},
emailAndPassword: {
enabled: true,
minPasswordLength: 12,
maxPasswordLength: 256
}
});
```
@@ -159,10 +159,10 @@ Call `requestPasswordReset` to send the reset link. Triggers the `sendResetPassw
```ts
const data = await auth.api.requestPasswordReset({
body: {
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
},
body: {
email: 'john.doe@example.com', // required
redirectTo: 'https://example.com/reset-password'
}
});
```
@@ -170,8 +170,8 @@ Or authClient:
```ts
const { data, error } = await authClient.requestPasswordReset({
email: "john.doe@example.com", // required
redirectTo: "https://example.com/reset-password",
email: 'john.doe@example.com', // required
redirectTo: 'https://example.com/reset-password'
});
```
@@ -186,26 +186,25 @@ Default: `scrypt` (Node.js native, no external dependencies).
To use Argon2id or another algorithm, provide custom `hash` and `verify` functions:
```ts
import { betterAuth } from "better-auth";
import { hash, verify, type Options } from "@node-rs/argon2";
import { betterAuth } from 'better-auth';
import { hash, verify, type Options } from '@node-rs/argon2';
const argon2Options: Options = {
memoryCost: 65536, // 64 MiB
timeCost: 3, // 3 iterations
parallelism: 4, // 4 parallel lanes
outputLen: 32, // 32 byte output
algorithm: 2, // Argon2id variant
memoryCost: 65536, // 64 MiB
timeCost: 3, // 3 iterations
parallelism: 4, // 4 parallel lanes
outputLen: 32, // 32 byte output
algorithm: 2 // Argon2id variant
};
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
password: {
hash: (password) => hash(password, argon2Options),
verify: ({ password, hash: storedHash }) =>
verify(storedHash, password, argon2Options),
},
},
emailAndPassword: {
enabled: true,
password: {
hash: (password) => hash(password, argon2Options),
verify: ({ password, hash: storedHash }) => verify(storedHash, password, argon2Options)
}
}
});
```