@auth/drizzle-adapter 0.0.0-manual.2501f898
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/LICENSE +15 -0
- package/README.md +28 -0
- package/package.json +65 -0
- package/src/index.ts +265 -0
- package/src/mysql/index.ts +261 -0
- package/src/pg/index.ts +231 -0
- package/src/sqlite/index.ts +209 -0
- package/src/utils.ts +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2023, Balázs Orbán
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<br/>
|
|
3
|
+
<a href="https://authjs.dev" target="_blank">
|
|
4
|
+
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
|
5
|
+
</a>
|
|
6
|
+
<a href="https://github.com/drizzle-team/drizzle-orm" target="_blank">
|
|
7
|
+
<img height="64px" src="https://pbs.twimg.com/profile_images/1598308842391179266/CtXrfLnk_400x400.jpg"/>
|
|
8
|
+
</a>
|
|
9
|
+
<h3 align="center"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
|
10
|
+
<p align="center" style="align: center;">
|
|
11
|
+
<a href="https://npm.im/@auth/drizzle-adapter">
|
|
12
|
+
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://npm.im/@auth/drizzle-adapter">
|
|
15
|
+
<img alt="npm" src="https://img.shields.io/npm/v/@auth/drizzle-adapter?color=green&label=@auth/drizzle-adapter&style=flat-square">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.npmtrends.com/@auth/drizzle-adapter">
|
|
18
|
+
<img src="https://img.shields.io/npm/dm/@auth/drizzle-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://github.com/nextauthjs/next-auth/stargazers">
|
|
21
|
+
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/drizzle).
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@auth/drizzle-adapter",
|
|
3
|
+
"version": "0.0.0-manual.2501f898",
|
|
4
|
+
"description": "Drizzle adapter for Auth.js.",
|
|
5
|
+
"homepage": "https://authjs.dev",
|
|
6
|
+
"repository": "https://github.com/nextauthjs/next-auth",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/nextauthjs/next-auth/issues"
|
|
9
|
+
},
|
|
10
|
+
"author": "Anthony Shew",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"types": "./index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"*.js",
|
|
15
|
+
"*.d.ts*",
|
|
16
|
+
"lib",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./index.d.ts",
|
|
22
|
+
"import": "./index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"next-auth",
|
|
28
|
+
"@auth",
|
|
29
|
+
"Auth.js",
|
|
30
|
+
"next.js",
|
|
31
|
+
"oauth",
|
|
32
|
+
"drizzle"
|
|
33
|
+
],
|
|
34
|
+
"private": false,
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@auth/core": "0.10.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/better-sqlite3": "^7.6.4",
|
|
43
|
+
"@types/uuid": "^8.3.3",
|
|
44
|
+
"better-sqlite3": "^8.4.0",
|
|
45
|
+
"drizzle-kit": "^0.19.5",
|
|
46
|
+
"drizzle-orm": "^0.27.0",
|
|
47
|
+
"jest": "^27.4.3",
|
|
48
|
+
"mysql2": "^3.2.0",
|
|
49
|
+
"postgres": "^3.3.4",
|
|
50
|
+
"@next-auth/tsconfig": "0.0.0",
|
|
51
|
+
"@next-auth/adapter-test": "0.0.0"
|
|
52
|
+
},
|
|
53
|
+
"jest": {
|
|
54
|
+
"preset": "@next-auth/adapter-test/jest"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"clean": "find . -type d -name \".drizzle\" | xargs rm -rf",
|
|
58
|
+
"test": "pnpm test:mysql && pnpm test:sqlite && pnpm test:pg",
|
|
59
|
+
"test:mysql": "pnpm clean && ./tests/mysql/test.sh",
|
|
60
|
+
"test:sqlite": "pnpm clean && ./tests/sqlite/test.sh",
|
|
61
|
+
"test:pg": "pnpm clean && ./tests/pg/test.sh",
|
|
62
|
+
"build": "tsc",
|
|
63
|
+
"dev": "drizzle-kit generate:mysql --schema=src/schema.ts --out=.drizzle && tsc -w"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
|
|
3
|
+
* <p style={{fontWeight: "normal"}}>Official <a href="https://orm.drizzle.team">Drizzle ORM</a> adapter for Auth.js / NextAuth.js.</p>
|
|
4
|
+
* <a href="https://orm.drizzle.team">
|
|
5
|
+
* <img style={{display: "block"}} src="/img/adapters/drizzle-orm.png" width="38" />
|
|
6
|
+
* </a>
|
|
7
|
+
* </div>
|
|
8
|
+
*
|
|
9
|
+
* ## Installation
|
|
10
|
+
*
|
|
11
|
+
* ```bash npm2yarn2pnpm
|
|
12
|
+
* npm install drizzle-orm @auth/drizzle-adapter
|
|
13
|
+
* npm install drizzle-kit --save-dev
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @module @auth/drizzle-adapter
|
|
17
|
+
*/
|
|
18
|
+
import {
|
|
19
|
+
SqlFlavorOptions,
|
|
20
|
+
isMySqlDatabase,
|
|
21
|
+
isPgDatabase,
|
|
22
|
+
isSQLiteDatabase,
|
|
23
|
+
} from "./utils"
|
|
24
|
+
import type { Adapter } from "@auth/core/adapters"
|
|
25
|
+
import { mySqlDrizzleAdapter } from "./mysql"
|
|
26
|
+
import { pgDrizzleAdapter } from "./pg"
|
|
27
|
+
import { SQLiteDrizzleAdapter } from "./sqlite"
|
|
28
|
+
/**
|
|
29
|
+
* Add the adapter to your `app/api/[...nextauth]/route.js` next-auth configuration object.
|
|
30
|
+
*
|
|
31
|
+
* ```ts title="pages/api/auth/[...nextauth].ts"
|
|
32
|
+
* import NextAuth from "next-auth"
|
|
33
|
+
* import GoogleProvider from "next-auth/providers/google"
|
|
34
|
+
* import { DrizzleAdapter } from "@auth/drizzle-adapter"
|
|
35
|
+
* import { db } from "./schema"
|
|
36
|
+
*
|
|
37
|
+
* export default NextAuth({
|
|
38
|
+
* adapter: DrizzleAdapter(db),
|
|
39
|
+
* providers: [
|
|
40
|
+
* GoogleProvider({
|
|
41
|
+
* clientId: process.env.GOOGLE_CLIENT_ID,
|
|
42
|
+
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
43
|
+
* }),
|
|
44
|
+
* ],
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* ## Setup
|
|
49
|
+
*
|
|
50
|
+
* First, create a schema that includes [the minimum requirements for a `next-auth` adapter](/reference/adapters#models). You can select your favorite SQL flavor below and copy it.
|
|
51
|
+
* Additionally, you may extend the schema from the minimum requirements to suit your needs.
|
|
52
|
+
*
|
|
53
|
+
* - [Postgres](#postgres)
|
|
54
|
+
* - [MySQL](#mysql)
|
|
55
|
+
* - [SQLite](#sqlite)
|
|
56
|
+
*
|
|
57
|
+
* ### Postgres
|
|
58
|
+
|
|
59
|
+
* ```ts title="schema.ts"
|
|
60
|
+
* import {
|
|
61
|
+
* timestamp,
|
|
62
|
+
* pgTable,
|
|
63
|
+
* text,
|
|
64
|
+
* primaryKey,
|
|
65
|
+
* integer
|
|
66
|
+
* } from "drizzle-orm/pg-core"
|
|
67
|
+
* import type { AdapterAccount } from '@auth/core/adapters'
|
|
68
|
+
*
|
|
69
|
+
* export const users = pgTable("users", {
|
|
70
|
+
* id: text("id").notNull().primaryKey(),
|
|
71
|
+
* name: text("name"),
|
|
72
|
+
* email: text("email").notNull(),
|
|
73
|
+
* emailVerified: timestamp("emailVerified", { mode: "date" }),
|
|
74
|
+
* image: text("image"),
|
|
75
|
+
* })
|
|
76
|
+
*
|
|
77
|
+
* export const accounts = pgTable(
|
|
78
|
+
* "accounts",
|
|
79
|
+
* {
|
|
80
|
+
* userId: text("userId")
|
|
81
|
+
* .notNull()
|
|
82
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
83
|
+
* type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
84
|
+
* provider: text("provider").notNull(),
|
|
85
|
+
* providerAccountId: text("providerAccountId").notNull(),
|
|
86
|
+
* refresh_token: text("refresh_token"),
|
|
87
|
+
* access_token: text("access_token"),
|
|
88
|
+
* expires_at: integer("expires_at"),
|
|
89
|
+
* token_type: text("token_type"),
|
|
90
|
+
* scope: text("scope"),
|
|
91
|
+
* id_token: text("id_token"),
|
|
92
|
+
* session_state: text("session_state"),
|
|
93
|
+
* },
|
|
94
|
+
* (account) => ({
|
|
95
|
+
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
96
|
+
* })
|
|
97
|
+
* )
|
|
98
|
+
*
|
|
99
|
+
* export const sessions = pgTable("sessions", {
|
|
100
|
+
* sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
101
|
+
* userId: text("userId")
|
|
102
|
+
* .notNull()
|
|
103
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
104
|
+
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
105
|
+
* })
|
|
106
|
+
*
|
|
107
|
+
* export const verificationTokens = pgTable(
|
|
108
|
+
* "verificationToken",
|
|
109
|
+
* {
|
|
110
|
+
* identifier: text("identifier").notNull(),
|
|
111
|
+
* token: text("token").notNull(),
|
|
112
|
+
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
113
|
+
* },
|
|
114
|
+
* (vt) => ({
|
|
115
|
+
* compoundKey: primaryKey(vt.identifier, vt.token),
|
|
116
|
+
* })
|
|
117
|
+
* )
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* ### MySQL
|
|
121
|
+
*
|
|
122
|
+
* ```ts title="schema.ts"
|
|
123
|
+
* import {
|
|
124
|
+
* int,
|
|
125
|
+
* timestamp,
|
|
126
|
+
* mysqlTable,
|
|
127
|
+
* primaryKey,
|
|
128
|
+
* varchar,
|
|
129
|
+
* } from "drizzle-orm/mysql-core"
|
|
130
|
+
* import type { AdapterAccount } from "@auth/core/adapters"
|
|
131
|
+
*
|
|
132
|
+
* export const users = mysqlTable("users", {
|
|
133
|
+
* id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
|
134
|
+
* name: varchar("name", { length: 255 }),
|
|
135
|
+
* email: varchar("email", { length: 255 }).notNull(),
|
|
136
|
+
* emailVerified: timestamp("emailVerified", { mode: "date", fsp: 3 }).defaultNow(),
|
|
137
|
+
* image: varchar("image", { length: 255 }),
|
|
138
|
+
* })
|
|
139
|
+
*
|
|
140
|
+
* export const accounts = mysqlTable(
|
|
141
|
+
* "accounts",
|
|
142
|
+
* {
|
|
143
|
+
* userId: varchar("userId", { length: 255 })
|
|
144
|
+
* .notNull()
|
|
145
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
146
|
+
* type: varchar("type", { length: 255 }).$type<AdapterAccount["type"]>().notNull(),
|
|
147
|
+
* provider: varchar("provider", { length: 255 }).notNull(),
|
|
148
|
+
* providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
|
|
149
|
+
* refresh_token: varchar("refresh_token", { length: 255 }),
|
|
150
|
+
* access_token: varchar("access_token", { length: 255 }),
|
|
151
|
+
* expires_at: int("expires_at"),
|
|
152
|
+
* token_type: varchar("token_type", { length: 255 }),
|
|
153
|
+
* scope: varchar("scope", { length: 255 }),
|
|
154
|
+
* id_token: varchar("id_token", { length: 255 }),
|
|
155
|
+
* session_state: varchar("session_state", { length: 255 }),
|
|
156
|
+
* },
|
|
157
|
+
* (account) => ({
|
|
158
|
+
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
159
|
+
* })
|
|
160
|
+
* )
|
|
161
|
+
*
|
|
162
|
+
* export const sessions = mysqlTable("sessions", {
|
|
163
|
+
* sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(),
|
|
164
|
+
* userId: varchar("userId", { length: 255 })
|
|
165
|
+
* .notNull()
|
|
166
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
167
|
+
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
168
|
+
* })
|
|
169
|
+
*
|
|
170
|
+
* export const verificationTokens = mysqlTable(
|
|
171
|
+
* "verificationToken",
|
|
172
|
+
* {
|
|
173
|
+
* identifier: varchar("identifier", { length: 255 }).notNull(),
|
|
174
|
+
* token: varchar("token", { length: 255 }).notNull(),
|
|
175
|
+
* expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
176
|
+
* },
|
|
177
|
+
* (vt) => ({
|
|
178
|
+
* compoundKey: primaryKey(vt.identifier, vt.token),
|
|
179
|
+
* })
|
|
180
|
+
* )
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* ### SQLite
|
|
184
|
+
*
|
|
185
|
+
* ```ts title="schema.ts"
|
|
186
|
+
* import { integer, sqliteTable, text, primaryKey } from "drizzle-orm/sqlite-core"
|
|
187
|
+
* import type { AdapterAccount } from "@auth/core/adapters"
|
|
188
|
+
*
|
|
189
|
+
* export const users = sqliteTable("users", {
|
|
190
|
+
* id: text("id").notNull().primaryKey(),
|
|
191
|
+
* name: text("name"),
|
|
192
|
+
* email: text("email").notNull(),
|
|
193
|
+
* emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
|
194
|
+
* image: text("image"),
|
|
195
|
+
* })
|
|
196
|
+
*
|
|
197
|
+
* export const accounts = sqliteTable(
|
|
198
|
+
* "accounts",
|
|
199
|
+
* {
|
|
200
|
+
* userId: text("userId")
|
|
201
|
+
* .notNull()
|
|
202
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
203
|
+
* type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
204
|
+
* provider: text("provider").notNull(),
|
|
205
|
+
* providerAccountId: text("providerAccountId").notNull(),
|
|
206
|
+
* refresh_token: text("refresh_token"),
|
|
207
|
+
* access_token: text("access_token"),
|
|
208
|
+
* expires_at: integer("expires_at"),
|
|
209
|
+
* token_type: text("token_type"),
|
|
210
|
+
* scope: text("scope"),
|
|
211
|
+
* id_token: text("id_token"),
|
|
212
|
+
* session_state: text("session_state"),
|
|
213
|
+
* },
|
|
214
|
+
* (account) => ({
|
|
215
|
+
* compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
216
|
+
* })
|
|
217
|
+
* )
|
|
218
|
+
*
|
|
219
|
+
* export const sessions = sqliteTable("sessions", {
|
|
220
|
+
* sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
221
|
+
* userId: text("userId")
|
|
222
|
+
* .notNull()
|
|
223
|
+
* .references(() => users.id, { onDelete: "cascade" }),
|
|
224
|
+
* expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
|
225
|
+
* })
|
|
226
|
+
*
|
|
227
|
+
* export const verificationTokens = sqliteTable(
|
|
228
|
+
* "verificationToken",
|
|
229
|
+
* {
|
|
230
|
+
* identifier: text("identifier").notNull(),
|
|
231
|
+
* token: text("token").notNull(),
|
|
232
|
+
* expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
|
233
|
+
* },
|
|
234
|
+
* (vt) => ({
|
|
235
|
+
* compoundKey: primaryKey(vt.identifier, vt.token),
|
|
236
|
+
* })
|
|
237
|
+
* )
|
|
238
|
+
* ```
|
|
239
|
+
*
|
|
240
|
+
* ## Migrating your database
|
|
241
|
+
* With your schema now described in your code, you'll need to migrate your database to your schema.
|
|
242
|
+
*
|
|
243
|
+
* For full documentation on how to run migrations with Drizzle, [visit the Drizzle documentation](https://orm.drizzle.team/kit-docs/overview#running-migrations).
|
|
244
|
+
*
|
|
245
|
+
* ---
|
|
246
|
+
*
|
|
247
|
+
**/
|
|
248
|
+
export function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(
|
|
249
|
+
db: SqlFlavor
|
|
250
|
+
): Adapter {
|
|
251
|
+
if (isMySqlDatabase(db)) {
|
|
252
|
+
// We need to cast to unknown since the type overlaps (PScale is MySQL based)
|
|
253
|
+
return mySqlDrizzleAdapter(db)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (isPgDatabase(db)) {
|
|
257
|
+
return pgDrizzleAdapter(db)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (isSQLiteDatabase(db)) {
|
|
261
|
+
return SQLiteDrizzleAdapter(db)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
throw new Error("Unsupported database type in Auth.js Drizzle adapter.")
|
|
265
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm"
|
|
2
|
+
import crypto from "node:crypto"
|
|
3
|
+
import {
|
|
4
|
+
int,
|
|
5
|
+
timestamp,
|
|
6
|
+
mysqlTable,
|
|
7
|
+
primaryKey,
|
|
8
|
+
varchar,
|
|
9
|
+
} from "drizzle-orm/mysql-core"
|
|
10
|
+
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
|
11
|
+
import { MySql2Database } from "drizzle-orm/mysql2"
|
|
12
|
+
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const users = mysqlTable("users", {
|
|
15
|
+
id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
|
16
|
+
name: varchar("name", { length: 255 }),
|
|
17
|
+
email: varchar("email", { length: 255 }).notNull(),
|
|
18
|
+
emailVerified: timestamp("emailVerified", {
|
|
19
|
+
mode: "date",
|
|
20
|
+
fsp: 3,
|
|
21
|
+
}).defaultNow(),
|
|
22
|
+
image: varchar("image", { length: 255 }),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
export const accounts = mysqlTable(
|
|
27
|
+
"accounts",
|
|
28
|
+
{
|
|
29
|
+
userId: varchar("userId", { length: 255 })
|
|
30
|
+
.notNull()
|
|
31
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
32
|
+
type: varchar("type", { length: 255 })
|
|
33
|
+
.$type<AdapterAccount["type"]>()
|
|
34
|
+
.notNull(),
|
|
35
|
+
provider: varchar("provider", { length: 255 }).notNull(),
|
|
36
|
+
providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
|
|
37
|
+
refresh_token: varchar("refresh_token", { length: 255 }),
|
|
38
|
+
access_token: varchar("access_token", { length: 255 }),
|
|
39
|
+
expires_at: int("expires_at"),
|
|
40
|
+
token_type: varchar("token_type", { length: 255 }),
|
|
41
|
+
scope: varchar("scope", { length: 255 }),
|
|
42
|
+
id_token: varchar("id_token", { length: 255 }),
|
|
43
|
+
session_state: varchar("session_state", { length: 255 }),
|
|
44
|
+
},
|
|
45
|
+
(account) => ({
|
|
46
|
+
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
/** @internal */
|
|
51
|
+
export const sessions = mysqlTable("sessions", {
|
|
52
|
+
sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(),
|
|
53
|
+
userId: varchar("userId", { length: 255 })
|
|
54
|
+
.notNull()
|
|
55
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
56
|
+
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
/** @internal */
|
|
60
|
+
export const verificationTokens = mysqlTable(
|
|
61
|
+
"verificationToken",
|
|
62
|
+
{
|
|
63
|
+
identifier: varchar("identifier", { length: 255 }).notNull(),
|
|
64
|
+
token: varchar("token", { length: 255 }).notNull(),
|
|
65
|
+
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
66
|
+
},
|
|
67
|
+
(vt) => ({
|
|
68
|
+
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
69
|
+
})
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
/** @internal */
|
|
73
|
+
export const schema = { users, accounts, sessions, verificationTokens }
|
|
74
|
+
export type DefaultSchema = typeof schema
|
|
75
|
+
|
|
76
|
+
/** @internal */
|
|
77
|
+
export function mySqlDrizzleAdapter(
|
|
78
|
+
client: MySql2Database<Record<string, never>>
|
|
79
|
+
): Adapter {
|
|
80
|
+
return {
|
|
81
|
+
createUser: async (data) => {
|
|
82
|
+
const id = crypto.randomUUID()
|
|
83
|
+
|
|
84
|
+
await client.insert(users).values({ ...data, id })
|
|
85
|
+
|
|
86
|
+
return await client
|
|
87
|
+
.select()
|
|
88
|
+
.from(users)
|
|
89
|
+
.where(eq(users.id, id))
|
|
90
|
+
.then((res) => res[0])
|
|
91
|
+
},
|
|
92
|
+
getUser: async (data) => {
|
|
93
|
+
const thing =
|
|
94
|
+
(await client
|
|
95
|
+
.select()
|
|
96
|
+
.from(users)
|
|
97
|
+
.where(eq(users.id, data))
|
|
98
|
+
.then((res) => res[0])) ?? null
|
|
99
|
+
|
|
100
|
+
return thing
|
|
101
|
+
},
|
|
102
|
+
getUserByEmail: async (data) => {
|
|
103
|
+
const user =
|
|
104
|
+
(await client
|
|
105
|
+
.select()
|
|
106
|
+
.from(users)
|
|
107
|
+
.where(eq(users.email, data))
|
|
108
|
+
.then((res) => res[0])) ?? null
|
|
109
|
+
|
|
110
|
+
return user
|
|
111
|
+
},
|
|
112
|
+
createSession: async (data) => {
|
|
113
|
+
await client.insert(sessions).values(data)
|
|
114
|
+
|
|
115
|
+
return await client
|
|
116
|
+
.select()
|
|
117
|
+
.from(sessions)
|
|
118
|
+
.where(eq(sessions.sessionToken, data.sessionToken))
|
|
119
|
+
.then((res) => res[0])
|
|
120
|
+
},
|
|
121
|
+
getSessionAndUser: async (data) => {
|
|
122
|
+
const sessionAndUser =
|
|
123
|
+
(await client
|
|
124
|
+
.select({
|
|
125
|
+
session: sessions,
|
|
126
|
+
user: users,
|
|
127
|
+
})
|
|
128
|
+
.from(sessions)
|
|
129
|
+
.where(eq(sessions.sessionToken, data))
|
|
130
|
+
.innerJoin(users, eq(users.id, sessions.userId))
|
|
131
|
+
.then((res) => res[0])) ?? null
|
|
132
|
+
|
|
133
|
+
return sessionAndUser
|
|
134
|
+
},
|
|
135
|
+
updateUser: async (data) => {
|
|
136
|
+
if (!data.id) {
|
|
137
|
+
throw new Error("No user id.")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await client.update(users).set(data).where(eq(users.id, data.id))
|
|
141
|
+
|
|
142
|
+
return await client
|
|
143
|
+
.select()
|
|
144
|
+
.from(users)
|
|
145
|
+
.where(eq(users.id, data.id))
|
|
146
|
+
.then((res) => res[0])
|
|
147
|
+
},
|
|
148
|
+
updateSession: async (data) => {
|
|
149
|
+
await client
|
|
150
|
+
.update(sessions)
|
|
151
|
+
.set(data)
|
|
152
|
+
.where(eq(sessions.sessionToken, data.sessionToken))
|
|
153
|
+
|
|
154
|
+
return await client
|
|
155
|
+
.select()
|
|
156
|
+
.from(sessions)
|
|
157
|
+
.where(eq(sessions.sessionToken, data.sessionToken))
|
|
158
|
+
.then((res) => res[0])
|
|
159
|
+
},
|
|
160
|
+
linkAccount: async (rawAccount) => {
|
|
161
|
+
await client
|
|
162
|
+
.insert(accounts)
|
|
163
|
+
.values(rawAccount)
|
|
164
|
+
.then((res) => res[0])
|
|
165
|
+
},
|
|
166
|
+
getUserByAccount: async (account) => {
|
|
167
|
+
const dbAccount =
|
|
168
|
+
(await client
|
|
169
|
+
.select()
|
|
170
|
+
.from(accounts)
|
|
171
|
+
.where(
|
|
172
|
+
and(
|
|
173
|
+
eq(accounts.providerAccountId, account.providerAccountId),
|
|
174
|
+
eq(accounts.provider, account.provider)
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
.leftJoin(users, eq(accounts.userId, users.id))
|
|
178
|
+
.then((res) => res[0])) ?? null
|
|
179
|
+
|
|
180
|
+
if (!dbAccount) {
|
|
181
|
+
return null
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return dbAccount.users
|
|
185
|
+
},
|
|
186
|
+
deleteSession: async (sessionToken) => {
|
|
187
|
+
const session =
|
|
188
|
+
(await client
|
|
189
|
+
.select()
|
|
190
|
+
.from(sessions)
|
|
191
|
+
.where(eq(sessions.sessionToken, sessionToken))
|
|
192
|
+
.then((res) => res[0])) ?? null
|
|
193
|
+
|
|
194
|
+
await client
|
|
195
|
+
.delete(sessions)
|
|
196
|
+
.where(eq(sessions.sessionToken, sessionToken))
|
|
197
|
+
|
|
198
|
+
return session
|
|
199
|
+
},
|
|
200
|
+
createVerificationToken: async (token) => {
|
|
201
|
+
await client.insert(verificationTokens).values(token)
|
|
202
|
+
|
|
203
|
+
return await client
|
|
204
|
+
.select()
|
|
205
|
+
.from(verificationTokens)
|
|
206
|
+
.where(eq(verificationTokens.identifier, token.identifier))
|
|
207
|
+
.then((res) => res[0])
|
|
208
|
+
},
|
|
209
|
+
useVerificationToken: async (token) => {
|
|
210
|
+
try {
|
|
211
|
+
const deletedToken =
|
|
212
|
+
(await client
|
|
213
|
+
.select()
|
|
214
|
+
.from(verificationTokens)
|
|
215
|
+
.where(
|
|
216
|
+
and(
|
|
217
|
+
eq(verificationTokens.identifier, token.identifier),
|
|
218
|
+
eq(verificationTokens.token, token.token)
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
.then((res) => res[0])) ?? null
|
|
222
|
+
|
|
223
|
+
await client
|
|
224
|
+
.delete(verificationTokens)
|
|
225
|
+
.where(
|
|
226
|
+
and(
|
|
227
|
+
eq(verificationTokens.identifier, token.identifier),
|
|
228
|
+
eq(verificationTokens.token, token.token)
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
return deletedToken
|
|
233
|
+
} catch (err) {
|
|
234
|
+
throw new Error("No verification token found.")
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
deleteUser: async (id) => {
|
|
238
|
+
const user = await client
|
|
239
|
+
.select()
|
|
240
|
+
.from(users)
|
|
241
|
+
.where(eq(users.id, id))
|
|
242
|
+
.then((res) => res[0] ?? null)
|
|
243
|
+
|
|
244
|
+
await client.delete(users).where(eq(users.id, id))
|
|
245
|
+
|
|
246
|
+
return user
|
|
247
|
+
},
|
|
248
|
+
unlinkAccount: async (account) => {
|
|
249
|
+
await client
|
|
250
|
+
.delete(accounts)
|
|
251
|
+
.where(
|
|
252
|
+
and(
|
|
253
|
+
eq(accounts.providerAccountId, account.providerAccountId),
|
|
254
|
+
eq(accounts.provider, account.provider)
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
return undefined
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
}
|
package/src/pg/index.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import {
|
|
2
|
+
timestamp,
|
|
3
|
+
pgTable,
|
|
4
|
+
text,
|
|
5
|
+
primaryKey,
|
|
6
|
+
integer,
|
|
7
|
+
} from "drizzle-orm/pg-core"
|
|
8
|
+
import { PostgresJsDatabase } from "drizzle-orm/postgres-js"
|
|
9
|
+
import { Adapter, AdapterAccount } from "@auth/core/adapters"
|
|
10
|
+
import { and, eq } from "drizzle-orm"
|
|
11
|
+
import crypto from "node:crypto"
|
|
12
|
+
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const users = pgTable("users", {
|
|
15
|
+
id: text("id").notNull().primaryKey(),
|
|
16
|
+
name: text("name"),
|
|
17
|
+
email: text("email").notNull(),
|
|
18
|
+
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
|
19
|
+
image: text("image"),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
/** @internal */
|
|
23
|
+
export const accounts = pgTable(
|
|
24
|
+
"accounts",
|
|
25
|
+
{
|
|
26
|
+
userId: text("userId")
|
|
27
|
+
.notNull()
|
|
28
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
29
|
+
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
30
|
+
provider: text("provider").notNull(),
|
|
31
|
+
providerAccountId: text("providerAccountId").notNull(),
|
|
32
|
+
refresh_token: text("refresh_token"),
|
|
33
|
+
access_token: text("access_token"),
|
|
34
|
+
expires_at: integer("expires_at"),
|
|
35
|
+
token_type: text("token_type"),
|
|
36
|
+
scope: text("scope"),
|
|
37
|
+
id_token: text("id_token"),
|
|
38
|
+
session_state: text("session_state"),
|
|
39
|
+
},
|
|
40
|
+
(account) => ({
|
|
41
|
+
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
42
|
+
})
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
/** @internal */
|
|
46
|
+
export const sessions = pgTable("sessions", {
|
|
47
|
+
sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
48
|
+
userId: text("userId")
|
|
49
|
+
.notNull()
|
|
50
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
51
|
+
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
/** @internal */
|
|
55
|
+
export const verificationTokens = pgTable(
|
|
56
|
+
"verificationToken",
|
|
57
|
+
{
|
|
58
|
+
identifier: text("identifier").notNull(),
|
|
59
|
+
token: text("token").notNull(),
|
|
60
|
+
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
61
|
+
},
|
|
62
|
+
(vt) => ({
|
|
63
|
+
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
/** @internal */
|
|
68
|
+
export const schema = { users, accounts, sessions, verificationTokens }
|
|
69
|
+
export type DefaultSchema = typeof schema
|
|
70
|
+
|
|
71
|
+
/** @internal */
|
|
72
|
+
export function pgDrizzleAdapter(
|
|
73
|
+
client: PostgresJsDatabase<Record<string, never>>
|
|
74
|
+
): Adapter {
|
|
75
|
+
return {
|
|
76
|
+
createUser: async (data) => {
|
|
77
|
+
return await client
|
|
78
|
+
.insert(users)
|
|
79
|
+
.values({ ...data, id: crypto.randomUUID() })
|
|
80
|
+
.returning()
|
|
81
|
+
.then((res) => res[0] ?? null)
|
|
82
|
+
},
|
|
83
|
+
getUser: async (data) => {
|
|
84
|
+
return await client
|
|
85
|
+
.select()
|
|
86
|
+
.from(users)
|
|
87
|
+
.where(eq(users.id, data))
|
|
88
|
+
.then((res) => res[0] ?? null)
|
|
89
|
+
},
|
|
90
|
+
getUserByEmail: async (data) => {
|
|
91
|
+
return await client
|
|
92
|
+
.select()
|
|
93
|
+
.from(users)
|
|
94
|
+
.where(eq(users.email, data))
|
|
95
|
+
.then((res) => res[0] ?? null)
|
|
96
|
+
},
|
|
97
|
+
createSession: async (data) => {
|
|
98
|
+
return await client
|
|
99
|
+
.insert(sessions)
|
|
100
|
+
.values(data)
|
|
101
|
+
.returning()
|
|
102
|
+
.then((res) => res[0])
|
|
103
|
+
},
|
|
104
|
+
getSessionAndUser: async (data) => {
|
|
105
|
+
return await client
|
|
106
|
+
.select({
|
|
107
|
+
session: sessions,
|
|
108
|
+
user: users,
|
|
109
|
+
})
|
|
110
|
+
.from(sessions)
|
|
111
|
+
.where(eq(sessions.sessionToken, data))
|
|
112
|
+
.innerJoin(users, eq(users.id, sessions.userId))
|
|
113
|
+
.then((res) => res[0] ?? null)
|
|
114
|
+
},
|
|
115
|
+
updateUser: async (data) => {
|
|
116
|
+
if (!data.id) {
|
|
117
|
+
throw new Error("No user id.")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return await client
|
|
121
|
+
.update(users)
|
|
122
|
+
.set(data)
|
|
123
|
+
.where(eq(users.id, data.id))
|
|
124
|
+
.returning()
|
|
125
|
+
.then((res) => res[0])
|
|
126
|
+
},
|
|
127
|
+
updateSession: async (data) => {
|
|
128
|
+
return await client
|
|
129
|
+
.update(sessions)
|
|
130
|
+
.set(data)
|
|
131
|
+
.where(eq(sessions.sessionToken, data.sessionToken))
|
|
132
|
+
.returning()
|
|
133
|
+
.then((res) => res[0])
|
|
134
|
+
},
|
|
135
|
+
linkAccount: async (rawAccount) => {
|
|
136
|
+
const updatedAccount = await client
|
|
137
|
+
.insert(accounts)
|
|
138
|
+
.values(rawAccount)
|
|
139
|
+
.returning()
|
|
140
|
+
.then((res) => res[0])
|
|
141
|
+
|
|
142
|
+
// Drizzle will return `null` for fields that are not defined.
|
|
143
|
+
// However, the return type is expecting `undefined`.
|
|
144
|
+
const account = {
|
|
145
|
+
...updatedAccount,
|
|
146
|
+
access_token: updatedAccount.access_token ?? undefined,
|
|
147
|
+
token_type: updatedAccount.token_type ?? undefined,
|
|
148
|
+
id_token: updatedAccount.id_token ?? undefined,
|
|
149
|
+
refresh_token: updatedAccount.refresh_token ?? undefined,
|
|
150
|
+
scope: updatedAccount.scope ?? undefined,
|
|
151
|
+
expires_at: updatedAccount.expires_at ?? undefined,
|
|
152
|
+
session_state: updatedAccount.session_state ?? undefined,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return account
|
|
156
|
+
},
|
|
157
|
+
getUserByAccount: async (account) => {
|
|
158
|
+
const dbAccount =
|
|
159
|
+
(await client
|
|
160
|
+
.select()
|
|
161
|
+
.from(accounts)
|
|
162
|
+
.where(
|
|
163
|
+
and(
|
|
164
|
+
eq(accounts.providerAccountId, account.providerAccountId),
|
|
165
|
+
eq(accounts.provider, account.provider)
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
.leftJoin(users, eq(accounts.userId, users.id))
|
|
169
|
+
.then((res) => res[0])) ?? null
|
|
170
|
+
|
|
171
|
+
if (!dbAccount) {
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return dbAccount.users
|
|
176
|
+
},
|
|
177
|
+
deleteSession: async (sessionToken) => {
|
|
178
|
+
const session = await client
|
|
179
|
+
.delete(sessions)
|
|
180
|
+
.where(eq(sessions.sessionToken, sessionToken))
|
|
181
|
+
.returning()
|
|
182
|
+
.then((res) => res[0] ?? null)
|
|
183
|
+
|
|
184
|
+
return session
|
|
185
|
+
},
|
|
186
|
+
createVerificationToken: async (token) => {
|
|
187
|
+
return await client
|
|
188
|
+
.insert(verificationTokens)
|
|
189
|
+
.values(token)
|
|
190
|
+
.returning()
|
|
191
|
+
.then((res) => res[0])
|
|
192
|
+
},
|
|
193
|
+
useVerificationToken: async (token) => {
|
|
194
|
+
try {
|
|
195
|
+
return await client
|
|
196
|
+
.delete(verificationTokens)
|
|
197
|
+
.where(
|
|
198
|
+
and(
|
|
199
|
+
eq(verificationTokens.identifier, token.identifier),
|
|
200
|
+
eq(verificationTokens.token, token.token)
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
.returning()
|
|
204
|
+
.then((res) => res[0] ?? null)
|
|
205
|
+
} catch (err) {
|
|
206
|
+
throw new Error("No verification token found.")
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
deleteUser: async (id) => {
|
|
210
|
+
await client
|
|
211
|
+
.delete(users)
|
|
212
|
+
.where(eq(users.id, id))
|
|
213
|
+
.returning()
|
|
214
|
+
.then((res) => res[0] ?? null)
|
|
215
|
+
},
|
|
216
|
+
unlinkAccount: async (account) => {
|
|
217
|
+
const { type, provider, providerAccountId, userId } = await client
|
|
218
|
+
.delete(accounts)
|
|
219
|
+
.where(
|
|
220
|
+
and(
|
|
221
|
+
eq(accounts.providerAccountId, account.providerAccountId),
|
|
222
|
+
eq(accounts.provider, account.provider)
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
.returning()
|
|
226
|
+
.then((res) => res[0] ?? null)
|
|
227
|
+
|
|
228
|
+
return { provider, type, providerAccountId, userId }
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
integer,
|
|
3
|
+
sqliteTable,
|
|
4
|
+
text,
|
|
5
|
+
primaryKey,
|
|
6
|
+
BaseSQLiteDatabase,
|
|
7
|
+
} from "drizzle-orm/sqlite-core"
|
|
8
|
+
import crypto from "node:crypto"
|
|
9
|
+
import { Adapter, AdapterAccount } from "@auth/core/adapters"
|
|
10
|
+
import { eq, and } from "drizzle-orm"
|
|
11
|
+
|
|
12
|
+
/** @internal */
|
|
13
|
+
export const users = sqliteTable("users", {
|
|
14
|
+
id: text("id").notNull().primaryKey(),
|
|
15
|
+
name: text("name"),
|
|
16
|
+
email: text("email").notNull(),
|
|
17
|
+
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
|
18
|
+
image: text("image"),
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
/** @internal */
|
|
22
|
+
export const accounts = sqliteTable(
|
|
23
|
+
"accounts",
|
|
24
|
+
{
|
|
25
|
+
userId: text("userId")
|
|
26
|
+
.notNull()
|
|
27
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
28
|
+
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
29
|
+
provider: text("provider").notNull(),
|
|
30
|
+
providerAccountId: text("providerAccountId").notNull(),
|
|
31
|
+
refresh_token: text("refresh_token"),
|
|
32
|
+
access_token: text("access_token"),
|
|
33
|
+
expires_at: integer("expires_at"),
|
|
34
|
+
token_type: text("token_type"),
|
|
35
|
+
scope: text("scope"),
|
|
36
|
+
id_token: text("id_token"),
|
|
37
|
+
session_state: text("session_state"),
|
|
38
|
+
},
|
|
39
|
+
(account) => ({
|
|
40
|
+
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
41
|
+
})
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/** @internal */
|
|
45
|
+
export const sessions = sqliteTable("sessions", {
|
|
46
|
+
sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
47
|
+
userId: text("userId")
|
|
48
|
+
.notNull()
|
|
49
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
50
|
+
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
/** @internal */
|
|
54
|
+
export const verificationTokens = sqliteTable(
|
|
55
|
+
"verificationToken",
|
|
56
|
+
{
|
|
57
|
+
identifier: text("identifier").notNull(),
|
|
58
|
+
token: text("token").notNull(),
|
|
59
|
+
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
|
60
|
+
},
|
|
61
|
+
(vt) => ({
|
|
62
|
+
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
/** @internal */
|
|
67
|
+
export const schema = { users, accounts, sessions, verificationTokens }
|
|
68
|
+
export type DefaultSchema = typeof schema
|
|
69
|
+
|
|
70
|
+
/** @internal */
|
|
71
|
+
export function SQLiteDrizzleAdapter(
|
|
72
|
+
client: BaseSQLiteDatabase<any, any>
|
|
73
|
+
): Adapter {
|
|
74
|
+
return {
|
|
75
|
+
createUser: (data) => {
|
|
76
|
+
return client
|
|
77
|
+
.insert(users)
|
|
78
|
+
.values({ ...data, id: crypto.randomUUID() })
|
|
79
|
+
.returning()
|
|
80
|
+
.get()
|
|
81
|
+
},
|
|
82
|
+
getUser: (data) => {
|
|
83
|
+
return client.select().from(users).where(eq(users.id, data)).get() ?? null
|
|
84
|
+
},
|
|
85
|
+
getUserByEmail: (data) => {
|
|
86
|
+
return (
|
|
87
|
+
client.select().from(users).where(eq(users.email, data)).get() ?? null
|
|
88
|
+
)
|
|
89
|
+
},
|
|
90
|
+
createSession: (data) => {
|
|
91
|
+
return client.insert(sessions).values(data).returning().get()
|
|
92
|
+
},
|
|
93
|
+
getSessionAndUser: (data) => {
|
|
94
|
+
return (
|
|
95
|
+
client
|
|
96
|
+
.select({
|
|
97
|
+
session: sessions,
|
|
98
|
+
user: users,
|
|
99
|
+
})
|
|
100
|
+
.from(sessions)
|
|
101
|
+
.where(eq(sessions.sessionToken, data))
|
|
102
|
+
.innerJoin(users, eq(users.id, sessions.userId))
|
|
103
|
+
.get() ?? null
|
|
104
|
+
)
|
|
105
|
+
},
|
|
106
|
+
updateUser: (data) => {
|
|
107
|
+
if (!data.id) {
|
|
108
|
+
throw new Error("No user id.")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return client
|
|
112
|
+
.update(users)
|
|
113
|
+
.set(data)
|
|
114
|
+
.where(eq(users.id, data.id))
|
|
115
|
+
.returning()
|
|
116
|
+
.get()
|
|
117
|
+
},
|
|
118
|
+
updateSession: (data) => {
|
|
119
|
+
return client
|
|
120
|
+
.update(sessions)
|
|
121
|
+
.set(data)
|
|
122
|
+
.where(eq(sessions.sessionToken, data.sessionToken))
|
|
123
|
+
.returning()
|
|
124
|
+
.get()
|
|
125
|
+
},
|
|
126
|
+
linkAccount: (rawAccount) => {
|
|
127
|
+
const updatedAccount = client
|
|
128
|
+
.insert(accounts)
|
|
129
|
+
.values(rawAccount)
|
|
130
|
+
.returning()
|
|
131
|
+
.get()
|
|
132
|
+
|
|
133
|
+
const account: AdapterAccount = {
|
|
134
|
+
...updatedAccount,
|
|
135
|
+
type: updatedAccount.type,
|
|
136
|
+
access_token: updatedAccount.access_token ?? undefined,
|
|
137
|
+
token_type: updatedAccount.token_type ?? undefined,
|
|
138
|
+
id_token: updatedAccount.id_token ?? undefined,
|
|
139
|
+
refresh_token: updatedAccount.refresh_token ?? undefined,
|
|
140
|
+
scope: updatedAccount.scope ?? undefined,
|
|
141
|
+
expires_at: updatedAccount.expires_at ?? undefined,
|
|
142
|
+
session_state: updatedAccount.session_state ?? undefined,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return account
|
|
146
|
+
},
|
|
147
|
+
getUserByAccount: (account) => {
|
|
148
|
+
const results = client
|
|
149
|
+
.select()
|
|
150
|
+
.from(accounts)
|
|
151
|
+
.leftJoin(users, eq(users.id, accounts.userId))
|
|
152
|
+
.where(
|
|
153
|
+
and(
|
|
154
|
+
eq(accounts.provider, account.provider),
|
|
155
|
+
eq(accounts.providerAccountId, account.providerAccountId)
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
.get()
|
|
159
|
+
|
|
160
|
+
return results?.users ?? null
|
|
161
|
+
},
|
|
162
|
+
deleteSession: (sessionToken) => {
|
|
163
|
+
return (
|
|
164
|
+
client
|
|
165
|
+
.delete(sessions)
|
|
166
|
+
.where(eq(sessions.sessionToken, sessionToken))
|
|
167
|
+
.returning()
|
|
168
|
+
.get() ?? null
|
|
169
|
+
)
|
|
170
|
+
},
|
|
171
|
+
createVerificationToken: (token) => {
|
|
172
|
+
return client.insert(verificationTokens).values(token).returning().get()
|
|
173
|
+
},
|
|
174
|
+
useVerificationToken: (token) => {
|
|
175
|
+
try {
|
|
176
|
+
return (
|
|
177
|
+
client
|
|
178
|
+
.delete(verificationTokens)
|
|
179
|
+
.where(
|
|
180
|
+
and(
|
|
181
|
+
eq(verificationTokens.identifier, token.identifier),
|
|
182
|
+
eq(verificationTokens.token, token.token)
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
.returning()
|
|
186
|
+
.get() ?? null
|
|
187
|
+
)
|
|
188
|
+
} catch (err) {
|
|
189
|
+
throw new Error("No verification token found.")
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
deleteUser: (id) => {
|
|
193
|
+
return client.delete(users).where(eq(users.id, id)).returning().get()
|
|
194
|
+
},
|
|
195
|
+
unlinkAccount: (account) => {
|
|
196
|
+
client
|
|
197
|
+
.delete(accounts)
|
|
198
|
+
.where(
|
|
199
|
+
and(
|
|
200
|
+
eq(accounts.providerAccountId, account.providerAccountId),
|
|
201
|
+
eq(accounts.provider, account.provider)
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
.run()
|
|
205
|
+
|
|
206
|
+
return undefined
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AnyMySqlTable, MySqlDatabase } from "drizzle-orm/mysql-core"
|
|
2
|
+
import { AnyPgTable, PgDatabase } from "drizzle-orm/pg-core"
|
|
3
|
+
import { AnySQLiteTable, BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
|
|
4
|
+
import { DefaultSchema as PgSchema } from "./pg"
|
|
5
|
+
import { DefaultSchema as MySqlSchema } from "./mysql"
|
|
6
|
+
import { DefaultSchema as SQLiteSchema } from "./sqlite"
|
|
7
|
+
|
|
8
|
+
export type AnyMySqlDatabase = MySqlDatabase<any, any>
|
|
9
|
+
export type AnyPgDatabase = PgDatabase<any, any, any>
|
|
10
|
+
export type AnySQLiteDatabase = BaseSQLiteDatabase<any, any, any, any>
|
|
11
|
+
|
|
12
|
+
export interface MinimumSchema {
|
|
13
|
+
mysql: MySqlSchema & Record<string, AnyMySqlTable>
|
|
14
|
+
pg: PgSchema & Record<string, AnyPgTable>
|
|
15
|
+
sqlite: SQLiteSchema & Record<string, AnySQLiteTable>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type SqlFlavorOptions =
|
|
19
|
+
| AnyMySqlDatabase
|
|
20
|
+
| AnyPgDatabase
|
|
21
|
+
| AnySQLiteDatabase
|
|
22
|
+
|
|
23
|
+
export type ClientFlavors<Flavor> = Flavor extends AnyMySqlDatabase
|
|
24
|
+
? MinimumSchema["mysql"]
|
|
25
|
+
: Flavor extends AnyPgDatabase
|
|
26
|
+
? MinimumSchema["pg"]
|
|
27
|
+
: Flavor extends AnySQLiteDatabase
|
|
28
|
+
? MinimumSchema["sqlite"]
|
|
29
|
+
: never
|
|
30
|
+
|
|
31
|
+
export function isMySqlDatabase(
|
|
32
|
+
db: any
|
|
33
|
+
): db is MySqlDatabase<any, any, any, any> {
|
|
34
|
+
return db instanceof MySqlDatabase
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isPgDatabase(db: any): db is PgDatabase<any, any, any> {
|
|
38
|
+
return db instanceof PgDatabase
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isSQLiteDatabase(db: any): db is AnySQLiteDatabase {
|
|
42
|
+
return db instanceof BaseSQLiteDatabase
|
|
43
|
+
}
|