Implement comprehensive auth template system

This commit is contained in:
Aman Varshney
2025-03-21 17:44:48 +05:30
parent fbdf12fcca
commit 1ca76b23f5
22 changed files with 394 additions and 198 deletions

View File

@@ -31,12 +31,6 @@ const trpcClient = trpc.createClient({
links: [
httpBatchLink({
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
fetch(url, options) {
return fetch(url, {
...options,
credentials: "include",
});
},
}),
],
});

View File

@@ -0,0 +1,77 @@
import {
QueryCache,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { httpBatchLink } from "@trpc/client";
import { createTRPCQueryUtils } from "@trpc/react-query";
import ReactDOM from "react-dom/client";
import { toast } from "sonner";
import Loader from "./components/loader";
import { routeTree } from "./routeTree.gen";
import { trpc } from "./utils/trpc";
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
toast.error(error.message, {
action: {
label: "retry",
onClick: () => {
queryClient.invalidateQueries();
},
},
});
},
}),
});
const trpcClient = trpc.createClient({
links: [
httpBatchLink({
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
fetch(url, options) {
return fetch(url, {
...options,
credentials: "include",
});
},
}),
],
});
export const trpcQueryUtils = createTRPCQueryUtils({
queryClient,
client: trpcClient,
});
const router = createRouter({
routeTree,
defaultPreload: "intent",
context: { trpcQueryUtils },
defaultPendingComponent: () => <Loader />,
Wrap: function WrapComponent({ children }) {
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
</trpc.Provider>
);
},
});
// Register things for typesafety
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
const rootElement = document.getElementById("app")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(<RouterProvider router={router} />);
}

View File

@@ -1,47 +1,7 @@
import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: boolean('email_verified').notNull(),
image: text('image'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull()
});
export const session = pgTable("session", {
id: text("id").primaryKey(),
expiresAt: timestamp('expires_at').notNull(),
token: text('token').notNull().unique(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
ipAddress: text('ip_address'),
userAgent: text('user_agent'),
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' })
});
export const account = pgTable("account", {
id: text("id").primaryKey(),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
idToken: text('id_token'),
accessTokenExpiresAt: timestamp('access_token_expires_at'),
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull()
});
export const verification = pgTable("verification", {
id: text("id").primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at'),
updatedAt: timestamp('updated_at')
});
export const todo = pgTable("todo", {
id: serial("id").primaryKey(),
text: text("text").notNull(),
completed: boolean("completed").default(false).notNull()
});

View File

@@ -0,0 +1,53 @@
import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
export const todo = pgTable("todo", {
id: serial("id").primaryKey(),
text: text("text").notNull(),
completed: boolean("completed").default(false).notNull()
});
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
emailVerified: boolean('email_verified').notNull(),
image: text('image'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull()
});
export const session = pgTable("session", {
id: text("id").primaryKey(),
expiresAt: timestamp('expires_at').notNull(),
token: text('token').notNull().unique(),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull(),
ipAddress: text('ip_address'),
userAgent: text('user_agent'),
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' })
});
export const account = pgTable("account", {
id: text("id").primaryKey(),
accountId: text('account_id').notNull(),
providerId: text('provider_id').notNull(),
userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }),
accessToken: text('access_token'),
refreshToken: text('refresh_token'),
idToken: text('id_token'),
accessTokenExpiresAt: timestamp('access_token_expires_at'),
refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('created_at').notNull(),
updatedAt: timestamp('updated_at').notNull()
});
export const verification = pgTable("verification", {
id: text("id").primaryKey(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expires_at').notNull(),
createdAt: timestamp('created_at'),
updatedAt: timestamp('updated_at')
});

View File

@@ -5,7 +5,7 @@ import * as schema from "../db/schema";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite",
provider: "pg",
schema: schema,
}),
trustedOrigins: [process.env.CORS_ORIGIN!],

View File

@@ -1,55 +1,7 @@
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const user = sqliteTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: integer("email_verified", { mode: "boolean" }).notNull(),
image: text("image"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const session = sqliteTable("session", {
id: text("id").primaryKey(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
token: text("token").notNull().unique(),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id),
});
export const account = sqliteTable("account", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: integer("access_token_expires_at", {
mode: "timestamp",
}),
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
mode: "timestamp",
}),
scope: text("scope"),
password: text("password"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const verification = sqliteTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
createdAt: integer("created_at", { mode: "timestamp" }),
updatedAt: integer("updated_at", { mode: "timestamp" }),
export const todo = sqliteTable("todo", {
id: integer("id").primaryKey({ autoIncrement: true }),
text: text("text").notNull(),
completed: integer("completed", { mode: "boolean" }).default(false).notNull()
});

View File

@@ -0,0 +1,61 @@
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const todo = sqliteTable("todo", {
id: integer("id").primaryKey({ autoIncrement: true }),
text: text("text").notNull(),
completed: integer("completed", { mode: "boolean" }).default(false).notNull()
});
export const user = sqliteTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: integer("email_verified", { mode: "boolean" }).notNull(),
image: text("image"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const session = sqliteTable("session", {
id: text("id").primaryKey(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
token: text("token").notNull().unique(),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id),
});
export const account = sqliteTable("account", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: integer("access_token_expires_at", {
mode: "timestamp",
}),
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
mode: "timestamp",
}),
scope: text("scope"),
password: text("password"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});
export const verification = sqliteTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
createdAt: integer("created_at", { mode: "timestamp" }),
updatedAt: integer("updated_at", { mode: "timestamp" }),
});

View File

@@ -1,9 +1,3 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
@@ -13,6 +7,14 @@ datasource db {
url = env("DATABASE_URL")
}
model Todo {
id Int @id @default(autoincrement())
text String
completed Boolean @default(false)
@@map("todo")
}
model User {
id String @id @map("_id")
name String

View File

@@ -0,0 +1,76 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgres"
url = env("DATABASE_URL")
}
model Todo {
id Int @id @default(autoincrement())
text String
completed Boolean @default(false)
@@map("todo")
}
model User {
id String @id @map("_id")
name String
email String
emailVerified Boolean
image String?
createdAt DateTime
updatedAt DateTime
sessions Session[]
accounts Account[]
@@unique([email])
@@map("user")
}
model Session {
id String @id @map("_id")
expiresAt DateTime
token String
createdAt DateTime
updatedAt DateTime
ipAddress String?
userAgent String?
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([token])
@@map("session")
}
model Account {
id String @id @map("_id")
accountId String
providerId String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime
updatedAt DateTime
@@map("account")
}
model Verification {
id String @id @map("_id")
identifier String
value String
expiresAt DateTime
createdAt DateTime?
updatedAt DateTime?
@@map("verification")
}

View File

@@ -1,74 +1,16 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = "file:./dev.db"
url = "file:./local.db"
}
model User {
id String @id @map("_id")
name String
email String
emailVerified Boolean
image String?
createdAt DateTime
updatedAt DateTime
sessions Session[]
accounts Account[]
model Todo {
id Int @id @default(autoincrement())
text String
completed Boolean @default(false)
@@unique([email])
@@map("user")
}
model Session {
id String @id @map("_id")
expiresAt DateTime
token String
createdAt DateTime
updatedAt DateTime
ipAddress String?
userAgent String?
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([token])
@@map("session")
}
model Account {
id String @id @map("_id")
accountId String
providerId String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime
updatedAt DateTime
@@map("account")
}
model Verification {
id String @id @map("_id")
identifier String
value String
expiresAt DateTime
createdAt DateTime?
updatedAt DateTime?
@@map("verification")
@@map("todo")
}

View File

@@ -0,0 +1,16 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = "file:./local.db"
}
model Todo {
id Int @id @default(autoincrement())
text String
completed Boolean @default(false)
@@map("todo")
}