Try to use drizzle event though

This commit is contained in:
Jared Palmer 2023-05-22 12:14:37 -04:00
parent f14e73ac8f
commit d88eae7230
14 changed files with 1978 additions and 12 deletions

16
auth.ts
View file

@ -1,11 +1,27 @@
import NextAuth from "@auth/nextjs";
import GitHub from "@auth/nextjs/providers/github";
import { NextResponse } from "next/server";
import {
db,
users,
accounts,
sessions,
verificationTokens,
} from "./lib/db/schema";
import { DrizzleAdapterPg } from "./lib/db";
export const {
handlers: { GET, POST },
auth,
CSRF_experimental,
} = NextAuth({
adapter: DrizzleAdapterPg(db, {
accounts,
users,
sessions,
verificationTokens,
}),
// @ts-ignore
providers: [GitHub],
session: { strategy: "jwt" },

6
drizzle.config.ts Normal file
View file

@ -0,0 +1,6 @@
import type { Config } from "drizzle-kit";
export default {
schema: "./lib/db/schema.ts",
out: "./lib/db/migrations",
} satisfies Config;

4
lib/db/db.ts Normal file
View file

@ -0,0 +1,4 @@
import { drizzle } from "drizzle-orm/vercel-postgres";
import { sql } from "@vercel/postgres";
export const db = drizzle(sql);

270
lib/db/index.ts Normal file
View file

@ -0,0 +1,270 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>Official <a href="https://github.com/drizzle-team/drizzle-orm">Drizzle ORM</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://github.com/drizzle-team/drizzle-orm">
* <img style={{display: "block"}} src="https://pbs.twimg.com/profile_images/1598308842391179266/CtXrfLnk_400x400.jpg" width="38" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth drizzle-orm @next-auth/drizzle-adapter
* npm install drizzle-kit --save-dev
* ```
*
* @module @next-auth/drizzle-adapter
*/
import type { Adapter } from "@auth/nextjs/adapters";
import { and, eq } from "drizzle-orm";
import { v4 as uuid } from "uuid";
import type { DbClient, Schema } from "./schema";
/**
* ## Setup
*
* Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object:
*
* ```js title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { DrizzleAdapter } from "@next-auth/drizzle-adapter"
* import { db } from "./db-schema"
*
* export default NextAuth({
* adapter: DrizzleAdapter(db),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
* ## Advanced usage
*
* ### Create the Drizzle schema from scratch
*
* You'll need to create a database schema that includes the minimal schema for a `next-auth` adapter.
* Be sure to use the Drizzle driver version that you're using for your project.
*
* > This schema is adapted for use in Drizzle and based upon our main [schema](https://authjs.dev/reference/adapters#models)
*
*
* ```json title="db-schema.ts"
*
* import { integer, pgTable, text, primaryKey } from 'drizzle-orm/pg-core';
* import { drizzle } from 'drizzle-orm/node-postgres';
* import { migrate } from 'drizzle-orm/node-postgres/migrator';
* import { Pool } from 'pg'
* import { ProviderType } from 'next-auth/providers';
*
* export const users = pgTable('users', {
* id: text('id').notNull().primaryKey(),
* name: text('name'),
* email: text("email").notNull(),
* emailVerified: integer("emailVerified"),
* image: text("image"),
* });
*
* export const accounts = pgTable("accounts", {
* userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
* type: text("type").$type<ProviderType>().notNull(),
* provider: text("provider").notNull(),
* providerAccountId: text("providerAccountId").notNull(),
* refresh_token: text("refresh_token"),
* access_token: text("access_token"),
* expires_at: integer("expires_at"),
* token_type: text("token_type"),
* scope: text("scope"),
* id_token: text("id_token"),
* session_state: text("session_state"),
* }, (account) => ({
* _: primaryKey(account.provider, account.providerAccountId)
* }))
*
* export const sessions = pgTable("sessions", {
* userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
* sessionToken: text("sessionToken").notNull().primaryKey(),
* expires: integer("expires").notNull(),
* })
*
* export const verificationTokens = pgTable("verificationToken", {
* identifier: text("identifier").notNull(),
* token: text("token").notNull(),
* expires: integer("expires").notNull()
* }, (vt) => ({
* _: primaryKey(vt.identifier, vt.token)
* }))
*
* const pool = new Pool({
* connectionString: "YOUR_CONNECTION_STRING"
* });
*
* export const db = drizzle(pool);
*
* migrate(db, { migrationsFolder: "./drizzle" })
*
* ```
*
**/
export function DrizzleAdapterPg(
client: DbClient,
{ users, sessions, verificationTokens, accounts }: Schema
): Adapter {
return {
createUser: async (data) => {
return client
.insert(users)
.values({ ...data, id: uuid() })
.returning()
.then((res) => res[0]);
},
getUser: async (data) => {
return (
client
.select()
.from(users)
.where(eq(users.id, data))
.then((res) => res[0]) ?? null
);
},
getUserByEmail: async (data) => {
return (
client
.select()
.from(users)
.where(eq(users.email, data))
.then((res) => res[0]) ?? null
);
},
createSession: async (data) => {
return client
.insert(sessions)
.values(data)
.returning()
.then((res) => res[0]);
},
getSessionAndUser: async (data) => {
return (
client
.select({
session: sessions,
user: users,
})
.from(sessions)
.where(eq(sessions.sessionToken, data))
.innerJoin(users, eq(users.id, sessions.userId))
.then((res) => res[0]) ?? null
);
},
updateUser: async (data) => {
if (!data.id) {
throw new Error("No user id.");
}
return client
.update(users)
.set(data)
.where(eq(users.id, data.id))
.returning()
.then((res) => res[0]);
},
updateSession: async (data) => {
return client
.update(sessions)
.set(data)
.where(eq(sessions.sessionToken, data.sessionToken))
.returning()
.then((res) => res[0]);
},
linkAccount: async (rawAccount) => {
const updatedAccount = await client
.insert(accounts)
.values(rawAccount)
.returning()
.then((res) => res[0]);
// Drizzle will return `null` for fields that are not defined.
// However, the return type is expecting `undefined`.
const account: ReturnType<Adapter["linkAccount"]> = {
...updatedAccount,
access_token: updatedAccount.access_token ?? undefined,
token_type: updatedAccount.token_type ?? undefined,
id_token: updatedAccount.id_token ?? undefined,
refresh_token: updatedAccount.refresh_token ?? undefined,
scope: updatedAccount.scope ?? undefined,
expires_at: updatedAccount.expires_at ?? undefined,
session_state: updatedAccount.session_state ?? undefined,
};
return account;
},
getUserByAccount: async (account) => {
const user =
(await client
.select()
.from(users)
.innerJoin(
accounts,
and(
eq(accounts.providerAccountId, account.providerAccountId),
eq(accounts.provider, account.provider)
)
)
.then((res) => res[0])) ?? null;
return user.users;
},
deleteSession: async (sessionToken) => {
await client
.delete(sessions)
.where(eq(sessions.sessionToken, sessionToken));
},
createVerificationToken: async (token) => {
return client
.insert(verificationTokens)
.values(token)
.returning()
.then((res) => res[0]);
},
useVerificationToken: async (token) => {
try {
return (
client
.delete(verificationTokens)
.where(
and(
eq(verificationTokens.identifier, token.identifier),
eq(verificationTokens.token, token.token)
)
)
.returning()
.then((res) => res[0]) ?? null
);
} catch (err) {
throw new Error("No verification token found.");
}
},
deleteUser: async (id) => {
await client
.delete(users)
.where(eq(users.id, id))
.returning()
.then((res) => res[0]);
},
unlinkAccount: async (account) => {
await client
.delete(accounts)
.where(
and(
eq(accounts.providerAccountId, account.providerAccountId),
eq(accounts.provider, account.provider)
)
);
return undefined;
},
};
}

18
lib/db/migrate.ts Normal file
View file

@ -0,0 +1,18 @@
import { migrate } from "drizzle-orm/vercel-postgres/migrator";
import { db } from "./schema";
import * as dotenv from "dotenv";
dotenv.config();
migrate(db, {
migrationsFolder: "lib/db/migrations",
})
.then(() => {
console.log("Migration was succesfull!");
})
.catch((err) => {
console.error("Migration failed:", err);
})
.finally(() => {
console.info("Migration finished");
});

View file

@ -0,0 +1,49 @@
CREATE TABLE IF NOT EXISTS "accounts" (
"userId" text NOT NULL,
"type" text NOT NULL,
"provider" text NOT NULL,
"providerAccountId" text NOT NULL,
"refresh_token" text,
"access_token" text,
"expires_at" integer,
"token_type" text,
"scope" text,
"id_token" text,
"session_state" text
);
--> statement-breakpoint
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_provider_providerAccountId" PRIMARY KEY("provider","providerAccountId");
CREATE TABLE IF NOT EXISTS "sessions" (
"sessionToken" text PRIMARY KEY NOT NULL,
"userId" text NOT NULL,
"expires" timestamp NOT NULL
);
CREATE TABLE IF NOT EXISTS "users" (
"id" text PRIMARY KEY NOT NULL,
"name" text,
"email" text NOT NULL,
"emailVerified" timestamp,
"image" text
);
CREATE TABLE IF NOT EXISTS "verificationToken" (
"identifier" text NOT NULL,
"token" text NOT NULL,
"expires" timestamp NOT NULL
);
--> statement-breakpoint
ALTER TABLE "verificationToken" ADD CONSTRAINT "verificationToken_identifier_token" PRIMARY KEY("identifier","token");
DO $$ BEGIN
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_userId_users_id_fk" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_userId_users_id_fk" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View file

@ -0,0 +1,7 @@
ALTER TABLE "accounts" DROP CONSTRAINT "accounts_provider_providerAccountId";
ALTER TABLE "verificationToken" DROP CONSTRAINT "verificationToken_identifier_token";
CREATE UNIQUE INDEX IF NOT EXISTS "Account_provider_providerAccountId_key" ON "accounts" ("provider","providerAccountId");
CREATE UNIQUE INDEX IF NOT EXISTS "Session_sessionToken_key" ON "sessions" ("sessionToken");
CREATE UNIQUE INDEX IF NOT EXISTS "User_email_key" ON "users" ("email");
CREATE UNIQUE INDEX IF NOT EXISTS "VerificationToken_identifier_token_key" ON "verificationToken" ("identifier","token");
CREATE UNIQUE INDEX IF NOT EXISTS "VerificationToken_token_key" ON "verificationToken" ("token");

View file

@ -0,0 +1,227 @@
{
"version": "5",
"dialect": "pg",
"id": "ea430ef9-8e30-4cb6-9985-d03f43656cf8",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"accounts": {
"name": "accounts",
"schema": "",
"columns": {
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"token_type": {
"name": "token_type",
"type": "text",
"primaryKey": false,
"notNull": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"session_state": {
"name": "session_state",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"accounts_userId_users_id_fk": {
"name": "accounts_userId_users_id_fk",
"tableFrom": "accounts",
"tableTo": "users",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"accounts_provider_providerAccountId": {
"name": "accounts_provider_providerAccountId",
"columns": [
"provider",
"providerAccountId"
]
}
}
},
"sessions": {
"name": "sessions",
"schema": "",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "text",
"primaryKey": true,
"notNull": true
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"sessions_userId_users_id_fk": {
"name": "sessions_userId_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {}
},
"verificationToken": {
"name": "verificationToken",
"schema": "",
"columns": {
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token": {
"name": "verificationToken_identifier_token",
"columns": [
"identifier",
"token"
]
}
}
}
},
"enums": {},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View file

@ -0,0 +1,252 @@
{
"version": "5",
"dialect": "pg",
"id": "34122d24-88e0-430e-862c-2168b0f866cb",
"prevId": "ea430ef9-8e30-4cb6-9985-d03f43656cf8",
"tables": {
"accounts": {
"name": "accounts",
"schema": "",
"columns": {
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"token_type": {
"name": "token_type",
"type": "text",
"primaryKey": false,
"notNull": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"session_state": {
"name": "session_state",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"Account_provider_providerAccountId_key": {
"name": "Account_provider_providerAccountId_key",
"columns": [
"provider",
"providerAccountId"
],
"isUnique": true
}
},
"foreignKeys": {
"accounts_userId_users_id_fk": {
"name": "accounts_userId_users_id_fk",
"tableFrom": "accounts",
"tableTo": "users",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"sessions": {
"name": "sessions",
"schema": "",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "text",
"primaryKey": true,
"notNull": true
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"Session_sessionToken_key": {
"name": "Session_sessionToken_key",
"columns": [
"sessionToken"
],
"isUnique": true
}
},
"foreignKeys": {
"sessions_userId_users_id_fk": {
"name": "sessions_userId_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"User_email_key": {
"name": "User_email_key",
"columns": [
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
},
"verificationToken": {
"name": "verificationToken",
"schema": "",
"columns": {
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"VerificationToken_identifier_token_key": {
"name": "VerificationToken_identifier_token_key",
"columns": [
"identifier",
"token"
],
"isUnique": true
},
"VerificationToken_token_key": {
"name": "VerificationToken_token_key",
"columns": [
"token"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View file

@ -0,0 +1,20 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1684771069602,
"tag": "0000_flawless_moondragon",
"breakpoints": false
},
{
"idx": 1,
"version": "5",
"when": 1684771628481,
"tag": "0001_colorful_spectrum",
"breakpoints": false
}
]
}

353
lib/db/schema.ts Normal file
View file

@ -0,0 +1,353 @@
import {
integer,
timestamp,
pgTable,
text,
primaryKey,
uniqueIndex,
} from "drizzle-orm/pg-core";
import { drizzle } from "drizzle-orm/vercel-postgres";
import { sql } from "@vercel/postgres";
import { ProviderType } from "@auth/nextjs/providers";
export const users = pgTable(
"users",
{
id: text("id").notNull().primaryKey(),
name: text("name"),
email: text("email").notNull(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: text("image"),
},
(user) => ({
emailKey: uniqueIndex("User_email_key").on(user.email),
})
);
export const accounts = pgTable(
"accounts",
{
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").$type<ProviderType>().notNull(),
provider: text("provider").notNull(),
providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
},
(table) => {
return {
providerProviderAccountIdKey: uniqueIndex(
"Account_provider_providerAccountId_key"
).on(table.provider, table.providerAccountId),
};
}
);
export const sessions = pgTable(
"sessions",
{
sessionToken: text("sessionToken").notNull().primaryKey(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(table) => {
return {
sessionTokenKey: uniqueIndex("Session_sessionToken_key").on(
table.sessionToken
),
};
}
);
export const verificationTokens = pgTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(table) => ({
identifierTokenKey: uniqueIndex(
"VerificationToken_identifier_token_key"
).on(table.identifier, table.token),
tokenKey: uniqueIndex("VerificationToken_token_key").on(table.token),
})
);
// Connect to Vercel Postgres
export const db = drizzle(sql);
export type DbClient = typeof db;
export type Schema = {
users: typeof users;
accounts: typeof accounts;
sessions: typeof sessions;
verificationTokens: typeof verificationTokens;
};
// import { sql, eq, and } from "drizzle-orm";
// import {
// PgDatabase,
// QueryResultHKT,
// integer,
// pgTable,
// text,
// timestamp,
// uniqueIndex,
// uuid,
// } from "drizzle-orm/pg-core";
// import { drizzle } from "drizzle-orm/vercel-postgres";
// import { sql as c } from "@vercel/postgres";
// import { Adapter, AdapterUser } from "@auth/nextjs/adapters";
// export const db = drizzle(c);
// export const users = pgTable(
// "users",
// {
// id: uuid("id")
// .notNull()
// .primaryKey()
// .default(sql`gen_random_uuid()`),
// name: text("name"),
// email: text("email"),
// emailVerified: timestamp("emailVerified", { precision: 3 }),
// image: text("image"),
// },
// (table) => {
// return {
// emailKey: uniqueIndex("User_email_key").on(table.email),
// };
// }
// );
// export const verificationTokens = pgTable(
// "verification_tokens",
// {
// identifier: text("identifier").notNull(),
// token: text("token").notNull(),
// expires: timestamp("expires", { precision: 3 }).notNull(),
// },
// (table) => {
// return {
// identifierTokenKey: uniqueIndex(
// "VerificationToken_identifier_token_key"
// ).on(table.identifier, table.token),
// tokenKey: uniqueIndex("VerificationToken_token_key").on(table.token),
// };
// }
// );
// export const sessions = pgTable(
// "sessions",
// {
// id: uuid("id")
// .notNull()
// .primaryKey()
// .default(sql`gen_random_uuid()`),
// sessionToken: text("sessionToken").notNull(),
// userId: uuid("userId")
// .notNull()
// .references(() => users.id, { onDelete: "cascade", onUpdate: "cascade" }),
// expires: timestamp("expires", { precision: 3 }).notNull(),
// },
// (table) => {
// return {
// sessionTokenKey: uniqueIndex("Session_sessionToken_key").on(
// table.sessionToken
// ),
// };
// }
// );
// export const accounts = pgTable(
// "accounts",
// {
// id: uuid("id")
// .notNull()
// .primaryKey()
// .default(sql`gen_random_uuid()`),
// userId: uuid("userId")
// .notNull()
// .references(() => users.id, { onDelete: "cascade", onUpdate: "cascade" }),
// type: text("type").notNull(),
// provider: text("provider").notNull(),
// providerAccountId: text("providerAccountId").notNull(),
// refresh_token: text("refresh_token"),
// access_token: text("access_token"),
// expires_at: integer("expires_at"),
// token_type: text("token_type"),
// scope: text("scope"),
// id_token: text("id_token"),
// session_state: text("session_state"),
// },
// (table) => {
// return {
// providerProviderAccountIdKey: uniqueIndex(
// "Account_provider_providerAccountId_key"
// ).on(table.provider, table.providerAccountId),
// };
// }
// );
// type Users = typeof users;
// type Sessions = typeof sessions;
// type Accounts = typeof accounts;
// type VerificationTokens = typeof verificationTokens;
// export function DrizzleAdapter<
// T extends PgDatabase<U>,
// U extends QueryResultHKT
// >({
// db,
// schema,
// }: {
// db: T;
// schema: {
// users: Users;
// sessions: Sessions;
// accounts: Accounts;
// verificationTokens: VerificationTokens;
// };
// }): Adapter {
// return {
// async createUser(user) {
// const [newUser] = await db.insert(schema.users).values(user).returning();
// return newUser as AdapterUser;
// },
// async getUser(id) {
// const [user] = await db
// .select()
// .from(schema.users)
// .where(eq(schema.users.id, id));
// return (user as AdapterUser) || null;
// },
// async getUserByEmail(email) {
// const [user] = await db
// .select()
// .from(schema.users)
// .where(eq(schema.users.email, email));
// return (user as AdapterUser) || null;
// },
// async getUserByAccount({ providerAccountId, provider }) {
// const results = await db
// .select()
// .from(schema.users)
// .leftJoin(schema.accounts, eq(schema.accounts.userId, schema.users.id))
// .where(
// and(
// eq(schema.accounts.provider, provider),
// eq(schema.accounts.providerAccountId, providerAccountId)
// )
// );
// if (results.length) {
// return results[0].users as AdapterUser;
// }
// return null;
// },
// async updateUser(user) {
// const userId = user.id;
// if (!userId) {
// throw new Error("Cannot update user without id");
// }
// const [updatedUser] = await db
// .update(schema.users)
// .set(user)
// .where(eq(schema.users.id, userId))
// .returning();
// return updatedUser as AdapterUser;
// },
// async deleteUser(userId) {
// await db.delete(schema.users).where(eq(schema.users.id, userId));
// },
// async linkAccount(account) {
// await db.insert(schema.accounts).values(account);
// },
// async unlinkAccount({ providerAccountId, provider }) {
// await db
// .delete(schema.accounts)
// .where(
// and(
// eq(schema.accounts.provider, provider),
// eq(schema.accounts.providerAccountId, providerAccountId)
// )
// );
// },
// async createSession(session) {
// const [newSession] = await db
// .insert(schema.sessions)
// .values(session)
// .returning();
// return newSession;
// },
// async getSessionAndUser(sessionToken) {
// const results = await db
// .select()
// .from(schema.sessions)
// .leftJoin(schema.users, eq(schema.users.id, schema.sessions.userId))
// .where(eq(schema.sessions.sessionToken, sessionToken));
// if (results.length) {
// return {
// user: results[0].users! as AdapterUser,
// session: results[0].sessions,
// };
// }
// return null;
// },
// async updateSession(session) {
// const [updatedSession] = await db
// .update(schema.sessions)
// .set(session)
// .where(eq(schema.sessions.sessionToken, session.sessionToken))
// .returning();
// return updatedSession;
// },
// async deleteSession(sessionToken) {
// await db
// .delete(schema.sessions)
// .where(eq(schema.sessions.sessionToken, sessionToken));
// },
// async createVerificationToken(token) {
// const [newVerificationToken] = await db
// .insert(schema.verificationTokens)
// .values(token)
// .returning();
// return newVerificationToken;
// },
// async useVerificationToken({ identifier, token }) {
// const [deletedVerificationToken] = await db
// .delete(schema.verificationTokens)
// .where(
// and(
// eq(schema.verificationTokens.identifier, identifier),
// eq(schema.verificationTokens.token, token)
// )
// )
// .returning();
// return deletedVerificationToken || null;
// },
// };
// }

View file

@ -5,7 +5,10 @@
"scripts": {
"dev": "prisma generate && next dev",
"build": "prisma generate && next build",
"studio": "prisma studio",
"db:mg:create": "drizzle-kit generate:pg --config=drizzle.config.ts",
"db:mg:apply": "ts-node --project tsconfig.migration.json ./lib/db/migrate --config=drizzle.config.ts",
"db:check": "drizzle-kit check:pg --config=drizzle.config.ts",
"db:up": "drizzle-kit up:pg --config=drizzle.config.ts",
"start": "next start",
"lint": "next lint",
"lint:fix": "next lint --fix",
@ -21,11 +24,13 @@
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@vercel/analytics": "^1.0.0",
"@vercel/kv": "^0.2.1",
"@vercel/postgres": "^0.3.0",
"body-scroll-lock": "4.0.0-beta.0",
"class-variance-authority": "^0.4.0",
"clsx": "^1.2.1",
"common-tags": "^1.8.2",
"cookie": "^0.5.0",
"drizzle-orm": "^0.26.0",
"eventsource-parser": "^1.0.0",
"focus-trap-react": "^10.1.1",
"framer-motion": "^10.12.4",
@ -45,7 +50,8 @@
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"tailwind-merge": "^1.12.0",
"tailwindcss-animate": "^1.0.5"
"tailwindcss-animate": "^1.0.5",
"uuid": "^9.0.0"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^3.7.1",
@ -60,6 +66,8 @@
"@types/react-syntax-highlighter": "^15.5.6",
"@typescript-eslint/parser": "^5.58.0",
"autoprefixer": "^10.4.13",
"dotenv": "^16.0.3",
"drizzle-kit": "^0.18.0",
"eslint": "^8.31.0",
"eslint-config-next": "13.4.3-canary.3",
"eslint-config-prettier": "^8.3.0",
@ -69,6 +77,7 @@
"prettier": "^2.7.1",
"prisma": "^4.14.0",
"tailwindcss": "^3.3.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
},
"pnpm": {

744
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

11
tsconfig.migration.json Normal file
View file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "react",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": ["node_modules", "**/node_modules/*"]
}