fix(auth): migrate from next-auth to better-auth (#1453)
This commit is contained in:
parent
453f5bb3e6
commit
b4f595a68c
40 changed files with 668 additions and 390 deletions
49
lib/db/migrations/0010_better_auth.sql
Normal file
49
lib/db/migrations/0010_better_auth.sql
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "name" text;
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "emailVerified" boolean NOT NULL DEFAULT false;
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "image" text;
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "isAnonymous" boolean NOT NULL DEFAULT false;
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "createdAt" timestamp DEFAULT now() NOT NULL;
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "updatedAt" timestamp DEFAULT now() NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Session" (
|
||||
"id" text PRIMARY KEY,
|
||||
"expiresAt" timestamp NOT NULL,
|
||||
"token" text NOT NULL UNIQUE,
|
||||
"createdAt" timestamp DEFAULT now() NOT NULL,
|
||||
"updatedAt" timestamp DEFAULT now() NOT NULL,
|
||||
"ipAddress" text,
|
||||
"userAgent" text,
|
||||
"userId" uuid NOT NULL REFERENCES "User"("id") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Account" (
|
||||
"id" text PRIMARY KEY,
|
||||
"accountId" text NOT NULL,
|
||||
"providerId" text NOT NULL,
|
||||
"userId" uuid NOT NULL REFERENCES "User"("id") ON DELETE CASCADE,
|
||||
"accessToken" text,
|
||||
"refreshToken" text,
|
||||
"idToken" text,
|
||||
"accessTokenExpiresAt" timestamp,
|
||||
"refreshTokenExpiresAt" timestamp,
|
||||
"scope" text,
|
||||
"password" text,
|
||||
"createdAt" timestamp DEFAULT now() NOT NULL,
|
||||
"updatedAt" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Verification" (
|
||||
"id" text PRIMARY KEY,
|
||||
"identifier" text NOT NULL,
|
||||
"value" text NOT NULL,
|
||||
"expiresAt" timestamp NOT NULL,
|
||||
"createdAt" timestamp DEFAULT now() NOT NULL,
|
||||
"updatedAt" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
UPDATE "User" SET "isAnonymous" = true WHERE email ~ '^guest-\d+$';
|
||||
|
||||
INSERT INTO "Account" ("id", "accountId", "providerId", "userId", "password", "createdAt", "updatedAt")
|
||||
SELECT gen_random_uuid()::text, id::text, 'credential', id, password, now(), now()
|
||||
FROM "User" WHERE password IS NOT NULL
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
|
@ -17,7 +17,7 @@ import postgres from "postgres";
|
|||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import type { VisibilityType } from "@/components/visibility-selector";
|
||||
import { ChatbotError } from "../errors";
|
||||
import { generateUUID } from "../utils";
|
||||
|
||||
import {
|
||||
type Chat,
|
||||
chat,
|
||||
|
|
@ -27,58 +27,13 @@ import {
|
|||
type Suggestion,
|
||||
stream,
|
||||
suggestion,
|
||||
type User,
|
||||
user,
|
||||
vote,
|
||||
} from "./schema";
|
||||
import { generateHashedPassword } from "./utils";
|
||||
|
||||
// Optionally, if not using email/pass login, you can
|
||||
// use the Drizzle adapter for Auth.js / NextAuth
|
||||
// https://authjs.dev/reference/adapter/drizzle
|
||||
|
||||
// biome-ignore lint: Forbidden non-null assertion.
|
||||
const client = postgres(process.env.POSTGRES_URL!);
|
||||
const db = drizzle(client);
|
||||
|
||||
export async function getUser(email: string): Promise<User[]> {
|
||||
try {
|
||||
return await db.select().from(user).where(eq(user.email, email));
|
||||
} catch (_error) {
|
||||
throw new ChatbotError(
|
||||
"bad_request:database",
|
||||
"Failed to get user by email"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createUser(email: string, password: string) {
|
||||
const hashedPassword = generateHashedPassword(password);
|
||||
|
||||
try {
|
||||
return await db.insert(user).values({ email, password: hashedPassword });
|
||||
} catch (_error) {
|
||||
throw new ChatbotError("bad_request:database", "Failed to create user");
|
||||
}
|
||||
}
|
||||
|
||||
export async function createGuestUser() {
|
||||
const email = `guest-${Date.now()}`;
|
||||
const password = generateHashedPassword(generateUUID());
|
||||
|
||||
try {
|
||||
return await db.insert(user).values({ email, password }).returning({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
});
|
||||
} catch (_error) {
|
||||
throw new ChatbotError(
|
||||
"bad_request:database",
|
||||
"Failed to create guest user"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveChat({
|
||||
id,
|
||||
userId,
|
||||
|
|
|
|||
|
|
@ -15,10 +15,62 @@ export const user = pgTable("User", {
|
|||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
email: varchar("email", { length: 64 }).notNull(),
|
||||
password: varchar("password", { length: 64 }),
|
||||
name: text("name"),
|
||||
emailVerified: boolean("emailVerified").notNull().default(false),
|
||||
image: text("image"),
|
||||
isAnonymous: boolean("isAnonymous").notNull().default(false),
|
||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export type User = InferSelectModel<typeof user>;
|
||||
|
||||
export const session = pgTable("Session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp("expiresAt").notNull(),
|
||||
token: text("token").notNull().unique(),
|
||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
|
||||
ipAddress: text("ipAddress"),
|
||||
userAgent: text("userAgent"),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export type Session = InferSelectModel<typeof session>;
|
||||
|
||||
export const account = pgTable("Account", {
|
||||
id: text("id").primaryKey(),
|
||||
accountId: text("accountId").notNull(),
|
||||
providerId: text("providerId").notNull(),
|
||||
userId: uuid("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
accessToken: text("accessToken"),
|
||||
refreshToken: text("refreshToken"),
|
||||
idToken: text("idToken"),
|
||||
accessTokenExpiresAt: timestamp("accessTokenExpiresAt"),
|
||||
refreshTokenExpiresAt: timestamp("refreshTokenExpiresAt"),
|
||||
scope: text("scope"),
|
||||
password: text("password"),
|
||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export type Account = InferSelectModel<typeof account>;
|
||||
|
||||
export const verification = pgTable("Verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text("identifier").notNull(),
|
||||
value: text("value").notNull(),
|
||||
expiresAt: timestamp("expiresAt").notNull(),
|
||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export type Verification = InferSelectModel<typeof verification>;
|
||||
|
||||
export const chat = pgTable("Chat", {
|
||||
id: uuid("id").primaryKey().notNull().defaultRandom(),
|
||||
createdAt: timestamp("createdAt").notNull(),
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import { generateId } from "ai";
|
||||
import { genSaltSync, hashSync } from "bcrypt-ts";
|
||||
|
||||
export function generateHashedPassword(password: string) {
|
||||
const salt = genSaltSync(10);
|
||||
const hash = hashSync(password, salt);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
export function generateDummyPassword() {
|
||||
const password = generateId();
|
||||
const hashedPassword = generateHashedPassword(password);
|
||||
|
||||
return hashedPassword;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue