formatting the PR

This commit is contained in:
Thang Vu
2023-09-16 16:18:33 +07:00
parent d2daf63c43
commit 186e168da7
8 changed files with 724 additions and 394 deletions

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

@@ -37,18 +37,18 @@
"test": "jest"
},
"peerDependencies": {
"next-auth": "^4"
"@auth/core": "workspace:*"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230321.0",
"@miniflare/d1": "^2.12.2",
"@next-auth/adapter-test": "workspace:*",
"@next-auth/tsconfig": "workspace:*",
"@auth/adapter-test": "workspace:*",
"@auth/tsconfig": "workspace:*",
"better-sqlite3": "^7.0.0",
"jest": "^27.0.3",
"next-auth": "workspace:*"
},
"jest": {
"preset": "@next-auth/adapter-test/jest"
"preset": "@auth/adapter-test/jest"
}
}
}

View File

@@ -5,89 +5,81 @@
* <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 @next-auth/d1-adapter
* npm install next-auth @auth/d1-adapter
* ```
*
* @module @next-auth/d1-adapter
* @module @auth/d1-adapter
*/
import { D1Database } from "@cloudflare/workers-types";
import { D1Database as MiniflareD1Database } from "@miniflare/d1";
import * as crypto from 'crypto';
import {
import type { D1Database as WorkerDatabase } from "@cloudflare/workers-types"
import type { D1Database as MiniflareD1Database } from "@miniflare/d1"
import type {
Adapter,
AdapterSession,
AdapterUser,
AdapterAccount,
} from "next-auth/adapters";
VerificationToken as AdapterVerificationToken,
} from "@auth/core/adapters"
export { up } from "./migrations";
// some handy internal type aliases
export { up } from "./migrations"
/**
* @type @cloudflare/workers-types.D1Database | @miniflare/d1.D1Database
*/
export type BothDB = D1Database | MiniflareD1Database;
interface AdapterVerificationToken {
expires: Date;
identifier: string;
token: string;
}
export type D1Database = WorkerDatabase | MiniflareD1Database
// all the sqls
// USER
export const CREATE_USER_SQL = `INSERT INTO authjs_users (id, name, email, emailVerified, image) VALUES (?, ?, ?, ?, ?)`;
export const GET_USER_BY_ID_SQL = `SELECT * FROM authjs_users WHERE id = ?`;
export const GET_USER_BY_EMAIL_SQL = `SELECT * FROM authjs_users WHERE email = ?`;
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 authjs_users u JOIN authjs_accounts a ON a.userId = u.id
WHERE a.providerAccountId = ? AND a.provider = ?`;
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 authjs_users
UPDATE users
SET name = ?, email = ?, emailVerified = ?, image = ?
WHERE id = ? `;
export const DELETE_USER_SQL = `DELETE FROM authjs_users WHERE id = ?`;
WHERE id = ? `
export const DELETE_USER_SQL = `DELETE FROM users WHERE id = ?`
// SESSION
export const CREATE_SESSION_SQL = 'INSERT INTO authjs_sessions (id, sessionToken, userId, expires) VALUES (?,?,?,?)';
// 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 authjs_sessions
WHERE sessionToken = ?`;
export const UPDATE_SESSION_BY_SESSION_TOKEN_SQL = `UPDATE authjs_sessions SET expires = ? WHERE sessionToken = ?`;
export const DELETE_SESSION_SQL = `DELETE FROM authjs_sessions WHERE sessionToken = ?`;
export const DELETE_SESSION_BY_USER_ID_SQL = `DELETE FROM authjs_sessions WHERE userId = ?`;
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 authjs_accounts (
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 authjs_accounts WHERE id = ? `
export const GET_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `SELECT * FROM authjs_accounts WHERE provider = ? AND providerAccountId = ?`;
export const DELETE_ACCOUNT_BY_PROVIDER_AND_PROVIDER_ACCOUNT_ID_SQL = `DELETE FROM authjs_accounts WHERE provider = ? AND providerAccountId = ?`;
export const DELETE_ACCOUNT_BY_USER_ID_SQL = `DELETE FROM authjs_accounts WHERE userId = ?`;
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 authjs_verification_tokens WHERE identifier = ? AND token = ?`;
export const CREATE_VERIFICATION_TOKEN_SQL = `INSERT INTO authjs_verification_tokens (identifier, expires, token) VALUES (?,?,?)`;
export const DELETE_VERIFICATION_TOKEN_SQL = `DELETE FROM authjs_verification_tokens WHERE identifier = ? and 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
@@ -104,11 +96,11 @@ 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];
delete obj[key]
}
if (isDate(value)) {
obj[key] = new Date(value);
obj[key] = new Date(value)
}
}
@@ -117,56 +109,82 @@ function format<T>(obj: Record<string, any>): T {
// D1 doesnt like undefined, it wants null when calling bind
function cleanBindings(bindings: any[]) {
return bindings.map(e=> e === undefined ? null : e)
return bindings.map((e) => (e === undefined ? null : e))
}
export async function createRecord<RecordType>(db:BothDB, CREATE_SQL:string, bindings: any[], GET_SQL:string, getBindings: any[]):Promise<RecordType | null> {
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()
await db
.prepare(CREATE_SQL)
.bind(...bindings)
.run()
return await getRecord<RecordType>(db, GET_SQL, getBindings)
} catch(e:any) {
console.log(`Error creating record with ${CREATE_SQL} and bindings ${bindings}`)
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function getRecord<RecordType>(db:BothDB, SQL:string, bindings:any[]):Promise<RecordType | null> {
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) {
const res: any = await db
.prepare(SQL)
.bind(...bindings)
.first()
if (res) {
return format<RecordType>(res)
} else {
return null
}
} catch(e:any) {
console.log(`Error getting record with ${SQL} and bindings ${bindings}`)
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
export async function updateRecord(db:BothDB, SQL:string, bindings:any[]) {
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.log(`Error updating record with ${SQL} and bindings ${bindings}`)
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
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:BothDB, SQL:string, bindings:any[]) {
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.log(`Error deleting record with ${SQL} and bindings ${bindings}`)
console.log(`D1 ERROR MESSAGE`, e.message, e.cause?.message)
await db
.prepare(SQL)
.bind(...bindings)
.run()
} catch (e: any) {
console.error(e.message, e.cause?.message)
throw e
}
}
@@ -181,7 +199,7 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
*
* ```javascript title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import { D1Adapter, up } from "@next-auth/d1-adapter"
* import { D1Adapter, up } from "@auth/d1-adapter"
*
*
* // For more information on each option (and a full list of options) go to
@@ -198,14 +216,14 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
*
* 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:
* `authjs_accounts`, `authjs_sessions`, `authjs_users`, `authjs_verification_tokens`.
*
* The table prefix "authjs_" is not configurable at this time.
*
* `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 "@next-auth/d1-adapter"
*
* import { up } from "@auth/d1-adapter"
*
* let migrated = false;
* async function migrationHandle({event, resolve}) {
* if(!migrated) {
@@ -219,118 +237,173 @@ export async function deleteRecord(db:BothDB, SQL:string, bindings:any[]) {
* 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:BothDB): Adapter {
export function D1Adapter(db: D1Database): Adapter {
// we need to run migrations if we dont have the right tables
return {
async createUser(user) {
console.log('Creating User:', user)
const id: string = crypto.randomUUID();
const createBindings = [id, user.name , user.email, user.emailVerified?.toISOString(), user.image];
const getBindings = [id];
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
);
// this method cant return null, gotta throw
if(newUser) return newUser;
throw new Error(`Couldn't create a new user`);
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]);
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]);
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]);
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) {
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(`couldnt find user after update with id ${params.id}`);
}
throw new Error(`couldnt update user with data ${JSON.stringify(user)}`);
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(`couldnt find user to update from id ${user.id}`);
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;
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];
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
);
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]);
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>(
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`);
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]);
const session: any = await getRecord<AdapterSession>(
db,
GET_SESSION_BY_TOKEN_SQL,
[sessionToken]
)
// no session? no user!
if(session === null) return null;
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;
const user = await getRecord<AdapterUser>(db, GET_USER_BY_ID_SQL, [
session.userId,
])
if (user === null) return null
return {session, user};
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) {
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()
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])
@@ -338,14 +411,20 @@ export function D1Adapter(db:BothDB): Adapter {
},
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]
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
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

@@ -1,5 +1,7 @@
export const up = [
`CREATE TABLE IF NOT EXISTS "authjs_accounts" (
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,
@@ -16,14 +18,14 @@ export const up = [
"oauth_token" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "authjs_sessions" (
`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 "authjs_users" (
`CREATE TABLE IF NOT EXISTS "users" (
"id" text NOT NULL DEFAULT '',
"name" text DEFAULT NULL,
"email" text DEFAULT NULL,
@@ -31,23 +33,36 @@ export const up = [
"image" text DEFAULT NULL,
PRIMARY KEY (id)
);`,
`CREATE TABLE IF NOT EXISTS "authjs_verification_tokens" (
`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 "authjs_accounts";`,
`DROP TABLE IF EXISTS "authjs_sessions";`,
`DROP TABLE IF EXISTS "authjs_users";`,
`DROP TABLE IF EXISTS "authjs_verification_token";`
];
`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

@@ -1,24 +0,0 @@
import {up as upSQLStatements} from "./init";
// @ts-ignore
import { D1Database, D1PreparedStatement } from "@cloudflare/workers-types";
import type { BothDB } from "..";
/**
*
* @param db
*/
async function up(db: BothDB) {
// 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

@@ -1,33 +1,46 @@
import {
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";
GET_VERIFICATION_TOKEN_BY_IDENTIFIER_AND_TOKEN_SQL,
} from "../src"
import {
AdapterSession,
AdapterUser,
AdapterAccount
} from "next-auth/adapters";
import { D1Database, D1DatabaseAPI } from "@miniflare/d1";
import { runBasicTests } from "@next-auth/adapter-test";
import Database from "better-sqlite3";
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));
let adapter = D1Adapter(db);
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))
beforeAll(async () => await up(db))
runBasicTests({
adapter: adapter,
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])
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

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

572
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff