authverse 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/index.cjs +827 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +799 -0
- package/dist/template/api/route.ts +4 -0
- package/dist/template/app-auth-uiDesign/forget/page.tsx +7 -0
- package/dist/template/app-auth-uiDesign/layout.tsx +9 -0
- package/dist/template/app-auth-uiDesign/login/page.tsx +7 -0
- package/dist/template/app-auth-uiDesign/reset-password/page.tsx +7 -0
- package/dist/template/app-auth-uiDesign/signup/page.tsx +7 -0
- package/dist/template/components/ForgetComponent.tsx +121 -0
- package/dist/template/components/GithubProviders.tsx +21 -0
- package/dist/template/components/GoogleProviders.tsx +21 -0
- package/dist/template/components/LoginComponent.tsx +145 -0
- package/dist/template/components/Logout.tsx +21 -0
- package/dist/template/components/ResetComponent.tsx +150 -0
- package/dist/template/components/SingUpComponent.tsx +173 -0
- package/dist/template/config/drizzle.config.ts +13 -0
- package/dist/template/config/prisma.config.ts +12 -0
- package/dist/template/db/drizzle.ts +6 -0
- package/dist/template/db/schema.ts +68 -0
- package/dist/template/email/reset-password.tsx +132 -0
- package/dist/template/lib/Mongodb/auth.ts +20 -0
- package/dist/template/lib/Mysql/auth.ts +27 -0
- package/dist/template/lib/Postgresql/auth.ts +20 -0
- package/dist/template/lib/auth-client.ts +5 -0
- package/dist/template/lib/auth-drizzle.ts +16 -0
- package/dist/template/prisma/mongodb/schema.prisma +70 -0
- package/dist/template/prisma/mysql/schema.prisma +68 -0
- package/dist/template/prisma/postgresql/schema.prisma +68 -0
- package/dist/template/proxy/proxy.ts +15 -0
- package/dist/template/server/user.ts +49 -0
- package/package.json +62 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Controller, useForm } from "react-hook-form";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
7
|
+
|
|
8
|
+
import { Button } from "@/components/ui/button";
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
CardDescription,
|
|
13
|
+
CardFooter,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
} from "@/components/ui/card";
|
|
17
|
+
import {
|
|
18
|
+
Field,
|
|
19
|
+
FieldError,
|
|
20
|
+
FieldGroup,
|
|
21
|
+
FieldLabel,
|
|
22
|
+
} from "@/components/ui/field";
|
|
23
|
+
import { Input } from "@/components/ui/input";
|
|
24
|
+
import Link from "next/link";
|
|
25
|
+
import { useState } from "react";
|
|
26
|
+
import { signUp } from "@/server/user";
|
|
27
|
+
import { useRouter } from "next/navigation";
|
|
28
|
+
|
|
29
|
+
const formSchema = z.object({
|
|
30
|
+
name: z.string().min(3, {
|
|
31
|
+
message: "Name must be at least 3 characters",
|
|
32
|
+
}),
|
|
33
|
+
email: z.string().email(),
|
|
34
|
+
password: z.string().min(8, {
|
|
35
|
+
message: "Password must be at least 8 characters",
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const SingUpComponent = () => {
|
|
40
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
41
|
+
const router = useRouter();
|
|
42
|
+
|
|
43
|
+
const form = useForm<z.infer<typeof formSchema>>({
|
|
44
|
+
resolver: zodResolver(formSchema),
|
|
45
|
+
defaultValues: {
|
|
46
|
+
name: "",
|
|
47
|
+
email: "",
|
|
48
|
+
password: "",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
|
53
|
+
setIsLoading(true);
|
|
54
|
+
try {
|
|
55
|
+
const { success, message } = await signUp(
|
|
56
|
+
data.name,
|
|
57
|
+
data.email,
|
|
58
|
+
data.password
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (success === true) {
|
|
62
|
+
router.push("/");
|
|
63
|
+
toast.success(message);
|
|
64
|
+
} else {
|
|
65
|
+
toast.error(message);
|
|
66
|
+
}
|
|
67
|
+
setIsLoading(false);
|
|
68
|
+
} catch (error: any) {
|
|
69
|
+
toast.error(error.message);
|
|
70
|
+
setIsLoading(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Card className="w-full sm:max-w-md shadow-md">
|
|
76
|
+
<CardHeader>
|
|
77
|
+
<CardTitle className="text-2xl leading-4">Sign Up</CardTitle>
|
|
78
|
+
<CardDescription>Create an account</CardDescription>
|
|
79
|
+
</CardHeader>
|
|
80
|
+
<CardContent>
|
|
81
|
+
<form id="form-login" onSubmit={form.handleSubmit(onSubmit)}>
|
|
82
|
+
<FieldGroup>
|
|
83
|
+
<Controller
|
|
84
|
+
name="name"
|
|
85
|
+
control={form.control}
|
|
86
|
+
render={({ field, fieldState }) => (
|
|
87
|
+
<Field data-invalid={fieldState.invalid}>
|
|
88
|
+
<FieldLabel htmlFor="form-login-name">Name</FieldLabel>
|
|
89
|
+
<Input
|
|
90
|
+
{...field}
|
|
91
|
+
id="form-login-name"
|
|
92
|
+
aria-invalid={fieldState.invalid}
|
|
93
|
+
placeholder="Enter your name"
|
|
94
|
+
autoComplete="off"
|
|
95
|
+
disabled={isLoading}
|
|
96
|
+
/>
|
|
97
|
+
{fieldState.invalid && (
|
|
98
|
+
<FieldError errors={[fieldState.error]} />
|
|
99
|
+
)}
|
|
100
|
+
</Field>
|
|
101
|
+
)}
|
|
102
|
+
/>
|
|
103
|
+
<Controller
|
|
104
|
+
name="email"
|
|
105
|
+
control={form.control}
|
|
106
|
+
render={({ field, fieldState }) => (
|
|
107
|
+
<Field data-invalid={fieldState.invalid}>
|
|
108
|
+
<FieldLabel htmlFor="form-login-email">Email</FieldLabel>
|
|
109
|
+
<Input
|
|
110
|
+
{...field}
|
|
111
|
+
id="form-login-email"
|
|
112
|
+
aria-invalid={fieldState.invalid}
|
|
113
|
+
placeholder="example@domain.com"
|
|
114
|
+
autoComplete="off"
|
|
115
|
+
disabled={isLoading}
|
|
116
|
+
/>
|
|
117
|
+
{fieldState.invalid && (
|
|
118
|
+
<FieldError errors={[fieldState.error]} />
|
|
119
|
+
)}
|
|
120
|
+
</Field>
|
|
121
|
+
)}
|
|
122
|
+
/>
|
|
123
|
+
<Controller
|
|
124
|
+
name="password"
|
|
125
|
+
control={form.control}
|
|
126
|
+
render={({ field, fieldState }) => (
|
|
127
|
+
<Field data-invalid={fieldState.invalid}>
|
|
128
|
+
<FieldLabel htmlFor="form-login-password">
|
|
129
|
+
Password
|
|
130
|
+
</FieldLabel>
|
|
131
|
+
<Input
|
|
132
|
+
{...field}
|
|
133
|
+
id="form-login-password"
|
|
134
|
+
aria-invalid={fieldState.invalid}
|
|
135
|
+
placeholder="********"
|
|
136
|
+
autoComplete="off"
|
|
137
|
+
type="password"
|
|
138
|
+
disabled={isLoading}
|
|
139
|
+
/>
|
|
140
|
+
{fieldState.invalid && (
|
|
141
|
+
<FieldError errors={[fieldState.error]} />
|
|
142
|
+
)}
|
|
143
|
+
</Field>
|
|
144
|
+
)}
|
|
145
|
+
/>
|
|
146
|
+
</FieldGroup>
|
|
147
|
+
</form>
|
|
148
|
+
</CardContent>
|
|
149
|
+
<CardFooter>
|
|
150
|
+
<div className="w-full">
|
|
151
|
+
<Field orientation="horizontal">
|
|
152
|
+
<Button
|
|
153
|
+
type="submit"
|
|
154
|
+
form="form-login"
|
|
155
|
+
className="w-full"
|
|
156
|
+
disabled={isLoading}
|
|
157
|
+
>
|
|
158
|
+
{isLoading ? "Signing up..." : "Sign Up"}
|
|
159
|
+
</Button>
|
|
160
|
+
</Field>
|
|
161
|
+
<div className="w-full text-center pt-4 space-x-2">
|
|
162
|
+
<span>Already have an account?</span>
|
|
163
|
+
<Link href="/auth/login" className="underline">
|
|
164
|
+
Login
|
|
165
|
+
</Link>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</CardFooter>
|
|
169
|
+
</Card>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export default SingUpComponent;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
import { defineConfig } from "drizzle-kit";
|
|
3
|
+
|
|
4
|
+
config({ path: ".env" });
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
schema: "./db/schema.ts",
|
|
8
|
+
out: "./migrations",
|
|
9
|
+
dialect: "postgresql",
|
|
10
|
+
dbCredentials: {
|
|
11
|
+
url: process.env.DATABASE_URL!,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
|
|
2
|
+
|
|
3
|
+
export const user = pgTable("user", {
|
|
4
|
+
id: text("id").primaryKey(),
|
|
5
|
+
name: text("name").notNull(),
|
|
6
|
+
email: text("email").notNull().unique(),
|
|
7
|
+
emailVerified: boolean("email_verified").default(false).notNull(),
|
|
8
|
+
image: text("image"),
|
|
9
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
10
|
+
updatedAt: timestamp("updated_at")
|
|
11
|
+
.defaultNow()
|
|
12
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
13
|
+
.notNull(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const session = pgTable("session", {
|
|
17
|
+
id: text("id").primaryKey(),
|
|
18
|
+
expiresAt: timestamp("expires_at").notNull(),
|
|
19
|
+
token: text("token").notNull().unique(),
|
|
20
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
21
|
+
updatedAt: timestamp("updated_at")
|
|
22
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
23
|
+
.notNull(),
|
|
24
|
+
ipAddress: text("ip_address"),
|
|
25
|
+
userAgent: text("user_agent"),
|
|
26
|
+
userId: text("user_id")
|
|
27
|
+
.notNull()
|
|
28
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const account = pgTable("account", {
|
|
32
|
+
id: text("id").primaryKey(),
|
|
33
|
+
accountId: text("account_id").notNull(),
|
|
34
|
+
providerId: text("provider_id").notNull(),
|
|
35
|
+
userId: text("user_id")
|
|
36
|
+
.notNull()
|
|
37
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
38
|
+
accessToken: text("access_token"),
|
|
39
|
+
refreshToken: text("refresh_token"),
|
|
40
|
+
idToken: text("id_token"),
|
|
41
|
+
accessTokenExpiresAt: timestamp("access_token_expires_at"),
|
|
42
|
+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
|
|
43
|
+
scope: text("scope"),
|
|
44
|
+
password: text("password"),
|
|
45
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
46
|
+
updatedAt: timestamp("updated_at")
|
|
47
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
48
|
+
.notNull(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const verification = pgTable("verification", {
|
|
52
|
+
id: text("id").primaryKey(),
|
|
53
|
+
identifier: text("identifier").notNull(),
|
|
54
|
+
value: text("value").notNull(),
|
|
55
|
+
expiresAt: timestamp("expires_at").notNull(),
|
|
56
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
57
|
+
updatedAt: timestamp("updated_at")
|
|
58
|
+
.defaultNow()
|
|
59
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
60
|
+
.notNull(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const schema = {
|
|
64
|
+
user,
|
|
65
|
+
session,
|
|
66
|
+
account,
|
|
67
|
+
verification,
|
|
68
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Button,
|
|
4
|
+
Container,
|
|
5
|
+
Head,
|
|
6
|
+
Heading,
|
|
7
|
+
Html,
|
|
8
|
+
Link,
|
|
9
|
+
Preview,
|
|
10
|
+
Section,
|
|
11
|
+
Text,
|
|
12
|
+
Tailwind,
|
|
13
|
+
} from "@react-email/components";
|
|
14
|
+
|
|
15
|
+
interface ForgotPasswordEmailProps {
|
|
16
|
+
username: string;
|
|
17
|
+
resetUrl: string;
|
|
18
|
+
userEmail: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ForgotPasswordEmail = (props: ForgotPasswordEmailProps) => {
|
|
22
|
+
const { username, resetUrl, userEmail } = props;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Html lang="en" dir="ltr">
|
|
26
|
+
<Tailwind>
|
|
27
|
+
<Head />
|
|
28
|
+
<Preview>Reset your password - Action required</Preview>
|
|
29
|
+
<Body className="bg-gray-100 font-sans py-[40px]">
|
|
30
|
+
<Container className="bg-white rounded-[8px] shadow-sm max-w-[600px] mx-auto p-[40px]">
|
|
31
|
+
{/* Header */}
|
|
32
|
+
<Section className="text-center mb-[32px]">
|
|
33
|
+
<Heading className="text-[28px] font-bold text-gray-900 m-0 mb-[8px]">
|
|
34
|
+
Reset Your Password
|
|
35
|
+
</Heading>
|
|
36
|
+
<Text className="text-[16px] text-gray-600 m-0">
|
|
37
|
+
We received a request to reset your password
|
|
38
|
+
</Text>
|
|
39
|
+
</Section>
|
|
40
|
+
|
|
41
|
+
{/* Main Content */}
|
|
42
|
+
<Section className="mb-[32px]">
|
|
43
|
+
<Text className="text-[16px] text-gray-700 leading-[24px] m-0 mb-[16px]">
|
|
44
|
+
Hello, {username}
|
|
45
|
+
</Text>
|
|
46
|
+
<Text className="text-[16px] text-gray-700 leading-[24px] m-0 mb-[16px]">
|
|
47
|
+
We received a password reset request for your account associated
|
|
48
|
+
with <strong>{userEmail}</strong>.
|
|
49
|
+
</Text>
|
|
50
|
+
<Text className="text-[16px] text-gray-700 leading-[24px] m-0 mb-[24px]">
|
|
51
|
+
Click the button below to create a new password. This link will
|
|
52
|
+
expire in 24 hours for security reasons.
|
|
53
|
+
</Text>
|
|
54
|
+
</Section>
|
|
55
|
+
|
|
56
|
+
{/* Reset Button */}
|
|
57
|
+
<Section className="text-center mb-[32px]">
|
|
58
|
+
<Button
|
|
59
|
+
href={resetUrl}
|
|
60
|
+
className="bg-blue-600 text-white px-[32px] py-[16px] rounded-[8px] text-[16px] font-semibold no-underline box-border inline-block"
|
|
61
|
+
>
|
|
62
|
+
Reset Password
|
|
63
|
+
</Button>
|
|
64
|
+
</Section>
|
|
65
|
+
|
|
66
|
+
{/* Alternative Link */}
|
|
67
|
+
<Section className="mb-[32px]">
|
|
68
|
+
<Text className="text-[14px] text-gray-600 leading-[20px] m-0 mb-[8px]">
|
|
69
|
+
If the button doesn't work, copy and paste this link into
|
|
70
|
+
your browser:
|
|
71
|
+
</Text>
|
|
72
|
+
<Link
|
|
73
|
+
href={resetUrl}
|
|
74
|
+
className="text-blue-600 text-[14px] break-all"
|
|
75
|
+
>
|
|
76
|
+
{resetUrl}
|
|
77
|
+
</Link>
|
|
78
|
+
</Section>
|
|
79
|
+
|
|
80
|
+
{/* Security Notice */}
|
|
81
|
+
<Section className="bg-gray-50 p-[20px] rounded-[8px] mb-[32px]">
|
|
82
|
+
<Text className="text-[14px] text-gray-700 leading-[20px] m-0 mb-[8px] font-semibold">
|
|
83
|
+
Security Notice:
|
|
84
|
+
</Text>
|
|
85
|
+
<Text className="text-[14px] text-gray-600 leading-[20px] m-0 mb-[8px]">
|
|
86
|
+
• If you didn't request this password reset, please ignore
|
|
87
|
+
this email
|
|
88
|
+
</Text>
|
|
89
|
+
<Text className="text-[14px] text-gray-600 leading-[20px] m-0 mb-[8px]">
|
|
90
|
+
• This link will expire in 24 hours
|
|
91
|
+
</Text>
|
|
92
|
+
<Text className="text-[14px] text-gray-600 leading-[20px] m-0">
|
|
93
|
+
• For security, never share this link with anyone
|
|
94
|
+
</Text>
|
|
95
|
+
</Section>
|
|
96
|
+
|
|
97
|
+
{/* Help Section */}
|
|
98
|
+
<Section className="mb-[32px]">
|
|
99
|
+
<Text className="text-[14px] text-gray-600 leading-[20px] m-0">
|
|
100
|
+
Need help? Contact our support team at{" "}
|
|
101
|
+
<Link
|
|
102
|
+
href="mailto:support@company.com"
|
|
103
|
+
className="text-blue-600"
|
|
104
|
+
>
|
|
105
|
+
support@company.com
|
|
106
|
+
</Link>
|
|
107
|
+
</Text>
|
|
108
|
+
</Section>
|
|
109
|
+
|
|
110
|
+
{/* Footer */}
|
|
111
|
+
<Section className="border-t border-gray-200 pt-[24px]">
|
|
112
|
+
<Text className="text-[12px] text-gray-500 leading-[16px] m-0 mb-[8px]">
|
|
113
|
+
This email was sent to {userEmail}
|
|
114
|
+
</Text>
|
|
115
|
+
<Text className="text-[12px] text-gray-500 leading-[16px] m-0 mb-[8px]">
|
|
116
|
+
Company Name, 123 Business Street, City, State 12345
|
|
117
|
+
</Text>
|
|
118
|
+
<Text className="text-[12px] text-gray-500 leading-[16px] m-0">
|
|
119
|
+
© 2025 Company Name. All rights reserved.{" "}
|
|
120
|
+
<Link href="#" className="text-gray-500">
|
|
121
|
+
Unsubscribe
|
|
122
|
+
</Link>
|
|
123
|
+
</Text>
|
|
124
|
+
</Section>
|
|
125
|
+
</Container>
|
|
126
|
+
</Body>
|
|
127
|
+
</Tailwind>
|
|
128
|
+
</Html>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default ForgotPasswordEmail;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
+
import { PrismaClient } from "@/generated/prisma/client";
|
|
4
|
+
import { nextCookies } from "better-auth/next-js";
|
|
5
|
+
|
|
6
|
+
const prisma = new PrismaClient();
|
|
7
|
+
export const auth = betterAuth({
|
|
8
|
+
database: prismaAdapter(prisma, {
|
|
9
|
+
provider: "mongodb",
|
|
10
|
+
}),
|
|
11
|
+
advanced: {
|
|
12
|
+
database: {
|
|
13
|
+
generateId: false,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
emailAndPassword: {
|
|
17
|
+
enabled: true,
|
|
18
|
+
},
|
|
19
|
+
plugins: [nextCookies()],
|
|
20
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
+
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
|
|
4
|
+
import { PrismaClient } from "@/generated/prisma/client";
|
|
5
|
+
import { nextCookies } from "better-auth/next-js";
|
|
6
|
+
|
|
7
|
+
const url = new URL(process.env.DATABASE_URL!);
|
|
8
|
+
|
|
9
|
+
const adapter = new PrismaMariaDb({
|
|
10
|
+
host: url.hostname,
|
|
11
|
+
user: url.username,
|
|
12
|
+
password: url.password,
|
|
13
|
+
database: url.pathname.replace("/", ""),
|
|
14
|
+
port: Number(url.port),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const prisma = new PrismaClient({ adapter });
|
|
18
|
+
|
|
19
|
+
export const auth = betterAuth({
|
|
20
|
+
database: prismaAdapter(prisma, {
|
|
21
|
+
provider: "sqlite",
|
|
22
|
+
}),
|
|
23
|
+
emailAndPassword: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
},
|
|
26
|
+
plugins: [nextCookies()],
|
|
27
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
3
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
4
|
+
import { PrismaClient } from "@/generated/prisma/client";
|
|
5
|
+
import { nextCookies } from "better-auth/next-js";
|
|
6
|
+
|
|
7
|
+
const connectionString = `${process.env.DATABASE_URL}`;
|
|
8
|
+
|
|
9
|
+
const adapter = new PrismaPg({ connectionString });
|
|
10
|
+
const prisma = new PrismaClient({ adapter });
|
|
11
|
+
|
|
12
|
+
export const auth = betterAuth({
|
|
13
|
+
database: prismaAdapter(prisma, {
|
|
14
|
+
provider: "sqlite",
|
|
15
|
+
}),
|
|
16
|
+
emailAndPassword: {
|
|
17
|
+
enabled: true,
|
|
18
|
+
},
|
|
19
|
+
plugins: [nextCookies()],
|
|
20
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
3
|
+
import { db } from "@/db/drizzle";
|
|
4
|
+
import { schema } from "@/db/schema";
|
|
5
|
+
import { nextCookies } from "better-auth/next-js";
|
|
6
|
+
|
|
7
|
+
export const auth = betterAuth({
|
|
8
|
+
database: drizzleAdapter(db, {
|
|
9
|
+
provider: "pg",
|
|
10
|
+
schema,
|
|
11
|
+
}),
|
|
12
|
+
emailAndPassword: {
|
|
13
|
+
enabled: true,
|
|
14
|
+
},
|
|
15
|
+
plugins: [nextCookies()],
|
|
16
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
output = "../generated/prisma"
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
datasource db {
|
|
7
|
+
provider = "mongodb"
|
|
8
|
+
url = env("DATABASE_URL")
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
model User {
|
|
12
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
13
|
+
name String
|
|
14
|
+
email String
|
|
15
|
+
emailVerified Boolean @default(false)
|
|
16
|
+
image String?
|
|
17
|
+
createdAt DateTime @default(now())
|
|
18
|
+
updatedAt DateTime @updatedAt
|
|
19
|
+
sessions Session[]
|
|
20
|
+
accounts Account[]
|
|
21
|
+
|
|
22
|
+
@@unique([email])
|
|
23
|
+
@@map("users") // Typically plural for collections
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
model Session {
|
|
27
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
28
|
+
expiresAt DateTime
|
|
29
|
+
token String @unique
|
|
30
|
+
createdAt DateTime @default(now())
|
|
31
|
+
updatedAt DateTime @updatedAt
|
|
32
|
+
ipAddress String?
|
|
33
|
+
userAgent String?
|
|
34
|
+
userId String @db.ObjectId
|
|
35
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
36
|
+
|
|
37
|
+
@@map("sessions")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
model Account {
|
|
41
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
42
|
+
accountId String
|
|
43
|
+
providerId String
|
|
44
|
+
userId String @db.ObjectId
|
|
45
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
46
|
+
accessToken String?
|
|
47
|
+
refreshToken String?
|
|
48
|
+
idToken String?
|
|
49
|
+
accessTokenExpiresAt DateTime?
|
|
50
|
+
refreshTokenExpiresAt DateTime?
|
|
51
|
+
scope String?
|
|
52
|
+
password String?
|
|
53
|
+
createdAt DateTime @default(now())
|
|
54
|
+
updatedAt DateTime @updatedAt
|
|
55
|
+
|
|
56
|
+
@@unique([providerId, accountId])
|
|
57
|
+
@@map("accounts")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
model Verification {
|
|
61
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
62
|
+
identifier String
|
|
63
|
+
value String
|
|
64
|
+
expiresAt DateTime
|
|
65
|
+
createdAt DateTime? @default(now())
|
|
66
|
+
updatedAt DateTime? @updatedAt
|
|
67
|
+
|
|
68
|
+
@@unique([identifier, value])
|
|
69
|
+
@@map("verifications")
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client"
|
|
3
|
+
output = "../generated/prisma"
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
datasource db {
|
|
7
|
+
provider = "mysql"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model User {
|
|
11
|
+
id String @id @default(cuid())
|
|
12
|
+
name String
|
|
13
|
+
email String @unique
|
|
14
|
+
emailVerified Boolean @default(false)
|
|
15
|
+
image String?
|
|
16
|
+
createdAt DateTime @default(now())
|
|
17
|
+
updatedAt DateTime @updatedAt
|
|
18
|
+
sessions Session[]
|
|
19
|
+
accounts Account[]
|
|
20
|
+
|
|
21
|
+
@@map("users")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
model Session {
|
|
25
|
+
id String @id @default(cuid())
|
|
26
|
+
expiresAt DateTime
|
|
27
|
+
token String @unique
|
|
28
|
+
createdAt DateTime @default(now())
|
|
29
|
+
updatedAt DateTime @updatedAt
|
|
30
|
+
ipAddress String?
|
|
31
|
+
userAgent String?
|
|
32
|
+
userId String
|
|
33
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
34
|
+
|
|
35
|
+
@@map("sessions")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
model Account {
|
|
39
|
+
id String @id @default(cuid())
|
|
40
|
+
accountId String
|
|
41
|
+
providerId String
|
|
42
|
+
userId String
|
|
43
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
44
|
+
accessToken String?
|
|
45
|
+
refreshToken String?
|
|
46
|
+
idToken String?
|
|
47
|
+
accessTokenExpiresAt DateTime?
|
|
48
|
+
refreshTokenExpiresAt DateTime?
|
|
49
|
+
scope String?
|
|
50
|
+
password String?
|
|
51
|
+
createdAt DateTime @default(now())
|
|
52
|
+
updatedAt DateTime @updatedAt
|
|
53
|
+
|
|
54
|
+
@@unique([providerId, accountId])
|
|
55
|
+
@@map("accounts")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
model Verification {
|
|
59
|
+
id String @id @default(cuid())
|
|
60
|
+
identifier String
|
|
61
|
+
value String
|
|
62
|
+
expiresAt DateTime
|
|
63
|
+
createdAt DateTime? @default(now())
|
|
64
|
+
updatedAt DateTime? @updatedAt
|
|
65
|
+
|
|
66
|
+
@@unique([identifier, value])
|
|
67
|
+
@@map("verifications")
|
|
68
|
+
}
|