Compare commits

...

15 Commits

Author SHA1 Message Date
Thang Vu
186e168da7 formatting the PR 2023-09-16 16:18:33 +07:00
Thang Vu
d2daf63c43 Merge branch 'main' into d1-adapter 2023-09-16 15:45:15 +07:00
Hamir Mahal
ed32236712 docs: don't copy $ in commands (#8609)
Co-authored-by: Thang Vu <hi@thvu.dev>
2023-09-16 09:56:57 +02:00
Thang Vu
307f7b5eb9 chore: Add EdgeDB in sidebar 2023-09-16 12:06:12 +07:00
Thang Vu
120d7a29ee chore: update email 2023-09-16 12:05:57 +07:00
Josh Schlesser
571ffc823c Merge branch 'main' into d1-adapter 2023-03-29 12:10:15 -07:00
Joshua Schlesser
75042bde21 Merge branch 'd1-adapter' of github.com:jschlesser/next-auth into d1-adapter 2023-03-28 09:58:52 -07:00
Joshua Schlesser
29cbd11552 changed to esm module 2023-03-27 10:36:15 -07:00
Nico Domino
030bae3517 Merge branch 'main' into d1-adapter 2023-03-27 19:00:41 +02:00
Nico Domino
46a5e369be Merge branch 'main' into d1-adapter 2023-03-26 18:59:42 +02:00
Joshua Schlesser
e54c195179 cleaning out unused file 2023-03-23 07:44:08 -07:00
Joshua Schlesser
b34aef3758 Merge branch 'main' into adapter-d1-2 2023-03-23 07:37:36 -07:00
Joshua Schlesser
294918e127 fixed up d1 logo in docs 2023-03-23 07:07:07 -07:00
Joshua Schlesser
84b51405ac Added documentation 2023-03-22 09:22:09 -07:00
Joshua Schlesser
8da00c56e4 cleaned everything up 2023-03-22 06:57:56 -07:00
15 changed files with 1056 additions and 190 deletions

View File

@@ -61,7 +61,7 @@ pnpm install
4. Start the development server
```bash
$ pnpm dev:docs
pnpm dev:docs
```
And thats all! Now you should have a local copy of this docs site running at [localhost:3000](http://localhost:3000)!

View File

@@ -89,7 +89,7 @@ NEXTAUTH_SECRET="This is an example"
`NEXTAUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
$ openssl rand -base64 32
openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
@@ -242,7 +242,7 @@ AUTH_SECRET="This is an example"
`AUTH_SECRET` is a random string used by the library to encrypt tokens and email verification hashes, and **it's mandatory to keep things secure**! 🔥 🔐 . You can use:
```
$ openssl rand -base64 32
openssl rand -base64 32
```
or https://generate-secret.vercel.app/32 to generate a random value for it.
@@ -288,7 +288,7 @@ To protect your API Routes (blocking unauthorized access to resources), you can
```ts title="routes/api/movies/+server.ts"
import { json, error } from "@sveltejs/kit";
import type { RequestEvent } from "./$types";
export async function GET({ locals }: RequestEvent) {
const session = await locals.getSession()
if (!session?.user) {
@@ -429,7 +429,7 @@ export default NextAuth({
Great! We're now ready to run our application locally. Start the Next.js app by running on your terminal the following command and navigating to [`http://localhost:3000`](http://localhost:3000):
```
$ npm run next dev
npm run next dev
```
</TabItem>
@@ -448,7 +448,7 @@ export const handle = SvelteKitAuth({
Great! We're now ready to run our application locally. Start the Svelte app by running on your terminal the following command and navigating to [`http://localhost:5173`](http://localhost:5173):
```
$ npm run vite dev
npm run vite dev
```

View File

@@ -580,7 +580,7 @@ Auth.js used to generate a secret for convenience, when the user did not define
You can generate a secret to be placed in the `secret` configuration option via the following command:
```bash
$ openssl rand -base64 32
openssl rand -base64 32
```
Therefore, your Auth.js config should look something like this:

View File

@@ -5,6 +5,9 @@ title: Overview
Using an Auth.js / NextAuth.js adapter you can connect to any database service or even several different services at the same time. The following listed official adapters are created and maintained by the community:
<div class="adapter-card-list">
<a href="/reference/adapter/d1" class="adapter-card">
<img src="/img/adapters/d1.svg" width="40" />
<h4 class="adapter-card__title">D1 Adapter</h4>
<a href="/reference/adapter/edgedb" class="adapter-card">
<img src="/img/adapters/edgedb.svg" width="30" />
<h4 class="adapter-card__title">EdgeDB Adapter</h4>

View File

@@ -282,6 +282,7 @@ const docusaurusConfig = {
...(process.env.TYPEDOC_SKIP_ADAPTERS
? []
: [
typedocAdapter("D1"),
typedocAdapter("EdgeDb"),
typedocAdapter("Dgraph"),
typedocAdapter("Drizzle"),

View File

@@ -46,6 +46,7 @@ module.exports = {
label: "Database Adapters",
link: { type: "doc", id: "reference/adapters/index" },
items: [
{ type: "doc", id: "reference/adapter/edgedb/index" },
{ type: "doc", id: "reference/adapter/dgraph/index" },
{ type: "doc", id: "reference/adapter/drizzle/index" },
{ type: "doc", id: "reference/adapter/dynamodb/index" },

5
docs/static/img/adapters/d1.svg vendored Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="49" viewBox="0 0 48 49">
<path d="m18.63 37.418-9.645-12.9 9.592-12.533-1.852-2.527L5.917 23.595l-.015 1.808 10.86 14.542 1.868-2.527z" fill="rgb(243, 128, 32)"></path>
<path d="M21.997 6.503h-3.712l13.387 18.3-13.072 17.7h3.735L35.4 24.81 21.997 6.503z" fill="rgb(243, 128, 32)"></path>
<path d="M29.175 6.503h-3.758l13.598 18.082-13.598 17.918h3.765l12.908-17.01v-1.808L29.175 6.503z" fill="rgb(243, 128, 32)"></path>
</svg>

After

Width:  |  Height:  |  Size: 497 B

View File

@@ -0,0 +1,28 @@
<p align="center">
<br/>
<a href="https://authjs.dev" target="_blank">
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
</a>
<a href="https://developers.cloudflare.com/d1/" target="_blank">
<img height="64px" src="https://authjs.dev/img/adapters/d1.svg"/>
</a>
<h3 align="center"><b>Cloudflare D1 Adapter</b> - NextAuth.js / Auth.js</a></h3>
<p align="center" style="align: center;">
<a href="https://npm.im/@auth/drizzle-adapter">
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
</a>
<a href="https://npm.im/@auth/d1-adapter">
<img alt="npm" src="https://img.shields.io/npm/v/@auth/d1-adapter?color=green&label=@auth/d1-adapter&style=flat-square">
</a>
<a href="https://www.npmtrends.com/@auth/d1-adapter">
<img src="https://img.shields.io/npm/dm/@auth/d1-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
</a>
<a href="https://github.com/nextauthjs/next-auth/stargazers">
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
</a>
</p>
</p>
---
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/d1).

View File

@@ -0,0 +1,54 @@
{
"name": "@auth/d1-adapter",
"version": "0.1.2",
"description": "A Cloudflare D1 adapter for Auth.js",
"homepage": "https://authjs.dev",
"repository": "https://github.com/nextauthjs/next-auth",
"bugs": {
"url": "https://github.com/nextauthjs/next-auth/issues"
},
"author": "Josh Schlesser <josh@schlesser.dev>",
"contributors": [],
"license": "ISC",
"keywords": [
"authjs",
"authjs.dev",
"cloudflare",
"d1"
],
"type": "module",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"private": false,
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc",
"clean": "rm -rf dist",
"test": "jest"
},
"peerDependencies": {
"@auth/core": "workspace:*"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230321.0",
"@miniflare/d1": "^2.12.2",
"@auth/adapter-test": "workspace:*",
"@auth/tsconfig": "workspace:*",
"better-sqlite3": "^7.0.0",
"jest": "^27.0.3",
"next-auth": "workspace:*"
},
"jest": {
"preset": "@auth/adapter-test/jest"
}
}

View File

@@ -0,0 +1,432 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>An unofficial <a href="https://developers.cloudflare.com/d1/">Cloudflare D1</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://developers.cloudflare.com/d1/">
* <img style={{display: "block"}} src="/img/adapters/d1.svg" width="48" />
* </a>
* </div>
*
* ## Warning
* This adapter is not developed or maintained by Clouflare and they haven't declared the D1 api stable. The author will make an effort to keep this adapter up to date.
* The adapter is compatible with the D1 api as of March 22, 2023.
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth @auth/d1-adapter
* ```
*
* @module @auth/d1-adapter
*/
import type { D1Database as WorkerDatabase } from "@cloudflare/workers-types"
import type { D1Database as MiniflareD1Database } from "@miniflare/d1"
import type {
Adapter,
AdapterSession,
AdapterUser,
AdapterAccount,
VerificationToken as AdapterVerificationToken,
} from "@auth/core/adapters"
export { up } from "./migrations"
/**
* @type @cloudflare/workers-types.D1Database | @miniflare/d1.D1Database
*/
export type D1Database = WorkerDatabase | MiniflareD1Database
// all the sqls
// USER
export const CREATE_USER_SQL = `INSERT INTO users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`
export const GET_USER_BY_ID_SQL = `SELECT * FROM users WHERE id = ?`
export const GET_USER_BY_EMAIL_SQL = `SELECT * FROM users WHERE email = ?`
export const GET_USER_BY_ACCOUNTL_SQL = `
SELECT u.*
FROM users u JOIN accounts a ON a.userId = u.id
WHERE a.providerAccountId = ? AND a.provider = ?`
export const UPDATE_USER_BY_ID_SQL = `
UPDATE users
SET name = ?, email = ?, emailVerified = ?, image = ?
WHERE id = ? `
export const DELETE_USER_SQL = `DELETE FROM users WHERE id = ?`
// SESSION
export const CREATE_SESSION_SQL =
"INSERT INTO sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)"
export const GET_SESSION_BY_TOKEN_SQL = `
SELECT id, sessionToken, userId, expires
FROM sessions
WHERE sessionToken = ?`
export const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE sessions SET expires = ? WHERE sessionToken = ?`
export const DELETE_SESSION_SQL = `DELETE FROM sessions WHERE sessionToken = ?`
export const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM sessions WHERE userId = ?`
// ACCOUNT
export const CREATE_ACCOUNT_SQL = `
INSERT INTO accounts (
id, userId, type, provider,
providerAccountId, refresh_token, access_token,
expires_at, token_type, scope, id_token, session_state,
oauth_token, oauth_token_secret
)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
export const GET_ACCOUNT_BY_ID_SQL = `SELECT * FROM accounts WHERE id = ? `
export const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM accounts WHERE provider = ? AND providerAccountId = ?`
export const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM accounts WHERE provider = ? AND providerAccountId = ?`
export const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM accounts WHERE userId = ?`
// VERIFICATION_TOKEN
export const GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL = `SELECT * FROM verification_tokens WHERE identifier = ? AND token = ?`
export const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO verification_tokens (identifier, expires, token) VALUES (?,?,?)`
export const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM verification_tokens WHERE identifier = ? and token = ?`
// helper functions
// isDate is borrowed from the supabase adapter, graciously
// depending on error messages ("Invalid Date") is always precarious, but probably fine for a built in native like Date
function isDate(date: any) {
return (
new Date(date).toString() !== "Invalid Date" && !isNaN(Date.parse(date))
)
}
// format is borrowed from the supabase adapter, graciously
function format<T>(obj: Record<string, any>): T {
for (const [key, value] of Object.entries(obj)) {
if (value === null) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete obj[key]
}
if (isDate(value)) {
obj[key] = new Date(value)
}
}
return obj as T
}
// D1 doesnt like undefined, it wants null when calling bind
function cleanBindings(bindings: any[]) {
return bindings.map((e) => (e === undefined ? null : e))
}
export async function createRecord<RecordType>(
db: D1Database,
CREATE_SQL: string,
bindings: any[],
GET_SQL: string,
getBindings: any[]
) {
try {
bindings = cleanBindings(bindings)
await db
.prepare(CREATE_SQL)
.bind(...bindings)
.run()
return await getRecord<RecordType>(db, GET_SQL, getBindings)
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function getRecord<RecordType>(
db: D1Database,
SQL: string,
bindings: any[]
): Promise<RecordType | null> {
try {
bindings = cleanBindings(bindings)
const res: any = await db
.prepare(SQL)
.bind(...bindings)
.first()
if (res) {
return format<RecordType>(res)
} else {
return null
}
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function updateRecord(
db: D1Database,
SQL: string,
bindings: any[]
) {
try {
bindings = cleanBindings(bindings)
return await db
.prepare(SQL)
.bind(...bindings)
.run()
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function deleteRecord(
db: D1Database,
SQL: string,
bindings: any[]
) {
// eslint-disable-next-line no-useless-catch
try {
bindings = cleanBindings(bindings)
await db
.prepare(SQL)
.bind(...bindings)
.run()
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
/**
*
* ## Setup
*
* This is the D1 Adapter for [`next-auth`](https://authjs.dev). This package can only be used in conjunction with the primary `next-auth` package. It is not a standalone package.
*
* ### Configure Auth.js
*
* ```javascript title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import { D1Adapter, up } from "@auth/d1-adapter"
*
*
* // For more information on each option (and a full list of options) go to
* // https://authjs.dev/reference/configuration/auth-options
* export default NextAuth({
* // https://authjs.dev/reference/providers/
* providers: [],
* adapter: D1Adapter(env.db)
* ...
* })
* ```
*
* ### Migrations
*
* Somewhere in the initialization of your application you need to run the `up(env.db)` function to create the tables in D1.
* It will create 4 tables if they don't already exist:
* `accounts`, `sessions`, `users`, `verification_tokens`.
*
* The table prefix "" is not configurable at this time.
*
* You can use something like the following to attempt the migration once each time your worker starts up. Running migrations more than once will not erase your existing tables.
* ```javascript
* import { up } from "@auth/d1-adapter"
*
* let migrated = false;
* async function migrationHandle({event, resolve}) {
* if(!migrated) {
* try {
* await up(event.platform.env.db)
* migrated = true
* } catch(e) {
* console.log(e.cause.message, e.message)
* }
* }
* return resolve(event)
* }
* ```
*
*
* You can also initialize your tables manually. Look in [init.ts](https://github.com/nextauthjs/next-auth/packages/adapter-d1/src/migrations/init.ts) for the relevant sql.
* Paste and run the SQL into your D1 dashboard query tool.
*
**/
export function D1Adapter(db: D1Database): Adapter {
// we need to run migrations if we dont have the right tables
return {
async createUser(user) {
const id: string = crypto.randomUUID()
const createBindings = [
id,
user.name,
user.email,
user.emailVerified?.toISOString(),
user.image,
]
const getBindings = [id]
const newUser = await createRecord<AdapterUser>(
db,
CREATE_USER_SQL,
createBindings,
GET_USER_BY_ID_SQL,
getBindings
)
if (newUser) return newUser
throw new Error("Error creating user: Cannot get user after creation.")
},
async getUser(id) {
return await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id])
},
async getUserByEmail(email) {
return await getRecord<AdapterUser>(db, GET_USER_BY_EMAIL_SQL, [email])
},
async getUserByAccount({ providerAccountId, provider }) {
return await getRecord<AdapterUser>(db, GET_USER_BY_ACCOUNTL_SQL, [
providerAccountId,
provider,
])
},
async updateUser(user) {
const params = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
user.id,
])
if (params) {
// copy any properties not in the update into the existing one and use that for bind params
// covers the scenario where the user arg doesnt have all of the current users properties
Object.assign(params, user)
const res = await updateRecord(db, UPDATE_USER_BY_ID_SQL, [
params.name,
params.email,
params.emailVerified?.toISOString(),
params.image,
params.id,
])
if (res.success) {
// we could probably just return
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
params.id,
])
if (user) return user
throw new Error(
"Error updating user: Cannot get user after updating."
)
}
}
throw new Error("Error updating user: Failed to run the update SQL.")
},
async deleteUser(userId) {
// this should probably be in a db.batch but batch has problems right now in miniflare
// no multi line sql statements
await deleteRecord(db, DELETE_ACCOUNT_BY_USER_ID_SQL, [userId])
await deleteRecord(db, DELETE_SESSION_BY_USER_ID_SQL, [userId])
await deleteRecord(db, DELETE_USER_SQL, [userId])
return null
},
async linkAccount(a) {
// convert user_id to userId and provider_account_id to providerAccountId
const id = crypto.randomUUID()
const createBindings = [
id,
a.userId,
a.type,
a.provider,
a.providerAccountId,
a.refresh_token,
a.access_token,
a.expires_at,
a.token_type,
a.scope,
a.id_token,
a.session_state,
a.oauth_token ?? null,
a.oauth_token_secret ?? null,
]
const getBindings = [id]
return await createRecord<AdapterAccount>(
db,
CREATE_ACCOUNT_SQL,
createBindings,
GET_ACCOUNT_BY_ID_SQL,
getBindings
)
},
async unlinkAccount({ providerAccountId, provider }) {
await deleteRecord(
db,
DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
[provider, providerAccountId]
)
},
async createSession({ sessionToken, userId, expires }) {
const id = crypto.randomUUID()
const createBindings = [id, sessionToken, userId, expires.toISOString()]
const getBindings = [sessionToken]
const session = await createRecord<AdapterSession>(
db,
CREATE_SESSION_SQL,
createBindings,
GET_SESSION_BY_TOKEN_SQL,
getBindings
)
if (session) return session
throw new Error(`Couldn't create session`)
},
async getSessionAndUser(sessionToken) {
const session: any = await getRecord<AdapterSession>(
db,
GET_SESSION_BY_TOKEN_SQL,
[sessionToken]
)
// no session? no user!
if (session === null) return null
// this shouldnt happen, but just in case
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
session.userId,
])
if (user === null) return null
return { session, user }
},
async updateSession({ sessionToken, expires }) {
// kinda strange that we have to deal with an undefined expires,
// we dont have any policy to enforce, lets just expire it now.
if (expires === undefined) {
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
return null
}
const session = await getRecord<AdapterSession>(
db,
GET_SESSION_BY_TOKEN_SQL,
[sessionToken]
)
if (!session) return null
session.expires = expires
await updateRecord(db, UPDATE_SESSION_BY_SESSION_TOKEN_SQL, [
expires?.toISOString(),
sessionToken,
])
return await db
.prepare(UPDATE_SESSION_BY_SESSION_TOKEN_SQL)
.bind(expires?.toISOString(), sessionToken)
.first()
},
async deleteSession(sessionToken) {
await deleteRecord(db, DELETE_SESSION_SQL, [sessionToken])
return null
},
async createVerificationToken({ identifier, expires, token }) {
return await createRecord(
db,
CREATE_VERIFICATION_TOKEN_SQL,
[identifier, expires.toISOString(), token],
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
[identifier, token]
)
},
async useVerificationToken({ identifier, token }) {
const verificationToken = await getRecord<AdapterVerificationToken>(
db,
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
[identifier, token]
)
if (!verificationToken) return null
await deleteRecord(db, DELETE_VERIFICATION_TOKEN_SQL, [identifier, token])
return verificationToken
},
}
}

View File

@@ -0,0 +1,68 @@
import type { D1Database } from "."
export const upSQLStatements = [
`CREATE TABLE IF NOT EXISTS "accounts" (
"id" text NOT NULL,
"userId" text NOT NULL DEFAULT NULL,
"type" text NOT NULL DEFAULT NULL,
"provider" text NOT NULL DEFAULT NULL,
"providerAccountId" text NOT NULL DEFAULT NULL,
"refresh_token" text DEFAULT NULL,
"access_token" text DEFAULT NULL,
"expires_at" number DEFAULT NULL,
"token_type" text DEFAULT NULL,
"scope" text DEFAULT NULL,
"id_token" text DEFAULT NULL,
"session_state" text DEFAULT NULL,
"oauth_token_secret" text DEFAULT NULL,
"oauth_token" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "sessions" (
"id" text NOT NULL,
"sessionToken" text NOT NULL,
"userId" text NOT NULL DEFAULT NULL,
"expires" datetime NOT NULL DEFAULT NULL,
PRIMARY KEY (sessionToken)
);`,
`CREATE TABLE IF NOT EXISTS "users" (
"id" text NOT NULL DEFAULT '',
"name" text DEFAULT NULL,
"email" text DEFAULT NULL,
"emailVerified" datetime DEFAULT NULL,
"image" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "verification_tokens" (
"identifier" text NOT NULL,
"token" text NOT NULL DEFAULT NULL,
"expires" datetime NOT NULL DEFAULT NULL,
PRIMARY KEY (token)
);`,
]
export const down = [
`DROP TABLE IF EXISTS "accounts";`,
`DROP TABLE IF EXISTS "sessions";`,
`DROP TABLE IF EXISTS "users";`,
`DROP TABLE IF EXISTS "verification_token";`,
]
/**
*
* @param db
*/
async function up(db: D1Database) {
// run the migration
upSQLStatements.forEach(async (sql) => {
try {
console.log("applying db migration sql", sql)
const res = await db.prepare(sql).run()
console.log("migration result", res)
} catch (e: any) {
console.log(e.cause?.message, e.message)
}
})
}
export { up }

View File

@@ -0,0 +1,46 @@
import {
D1Adapter,
up,
getRecord,
GET_USER_BY_ID_SQL,
GET_SESSION_BY_TOKEN_SQL,
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
} from "../src"
import {
AdapterSession,
AdapterUser,
AdapterAccount,
} from "@auth/core/adapters"
import { D1Database, D1DatabaseAPI } from "@miniflare/d1"
import { runBasicTests } from "@auth/adapter-test"
import Database from "better-sqlite3"
const sqliteDB = new Database(":memory:")
let db = new D1Database(new D1DatabaseAPI(sqliteDB as any))
let adapter = D1Adapter(db)
// put stuff here if we need some async init
beforeAll(async () => await up(db))
runBasicTests({
adapter,
db: {
user: async (id) =>
await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [id]),
session: async (sessionToken) =>
await getRecord<AdapterSession>(db, GET_SESSION_BY_TOKEN_SQL, [
sessionToken,
]),
account: async ({ provider, providerAccountId }) =>
await getRecord<AdapterAccount>(
db,
GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL,
[provider, providerAccountId]
),
verificationToken: async ({ identifier, token }) =>
await getRecord(db, GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL, [
identifier,
token,
]),
},
})

View File

@@ -0,0 +1,20 @@
{
"extends": "@auth/tsconfig/tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"isolatedModules": true,
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": ".",
"rootDir": "src",
"skipDefaultLibCheck": true,
"strictNullChecks": true,
"stripInternal": true,
"declarationMap": true,
"declaration": true
},
"include": ["src/**/*", "tests/migrations"],
"exclude": ["*.js", "*.d.ts"]
}

View File

@@ -9,7 +9,7 @@
"Balázs Orbán <info@balazsorban.com>",
"Nico Domino <yo@ndo.dev>",
"Lluis Agusti <hi@llu.lu>",
"Thang Huu Vu <thvu@hey.com>"
"Thang Huu Vu <hi@thvu.dev>"
],
"main": "index.js",
"module": "index.js",

572
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff