initttt
This commit is contained in:
13
packages/db/drizzle.config.ts
Normal file
13
packages/db/drizzle.config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
import { settings } from "@pkg/settings";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "./schema",
|
||||
out: "./migrations",
|
||||
dialect: "postgresql",
|
||||
verbose: true,
|
||||
strict: false,
|
||||
dbCredentials: {
|
||||
url: settings.databaseUrl,
|
||||
},
|
||||
});
|
||||
11
packages/db/index.ts
Normal file
11
packages/db/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { settings } from "@pkg/settings";
|
||||
import * as schema from "./schema";
|
||||
|
||||
const db = drizzle(settings.databaseUrl, { schema });
|
||||
|
||||
export type Database = typeof db;
|
||||
|
||||
export * from "drizzle-orm";
|
||||
|
||||
export { db, schema };
|
||||
27
packages/db/package.json
Normal file
27
packages/db/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@pkg/db",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"db:gen": "drizzle-kit generate --config=drizzle.config.ts",
|
||||
"db:drop": "drizzle-kit drop --config=drizzle.config.ts",
|
||||
"db:push": "drizzle-kit push --config=drizzle.config.ts",
|
||||
"db:migrate": "drizzle-kit generate --config=drizzle.config.ts && drizzle-kit push --config=drizzle.config.ts",
|
||||
"db:forcemigrate": "drizzle-kit generate --config=drizzle.config.ts && drizzle-kit push --config=drizzle.config.ts --force",
|
||||
"dev": "drizzle-kit studio --config=drizzle.config.ts --verbose"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pkg/settings": "workspace:*",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"postgres": "^3.4.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/pg": "^8.11.10",
|
||||
"drizzle-kit": "^0.31.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
52
packages/db/schema/auth.schema.ts
Normal file
52
packages/db/schema/auth.schema.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
integer,
|
||||
json,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { user } from "./better.auth.schema";
|
||||
import { relations } from "drizzle-orm";
|
||||
|
||||
export const twoFactor = pgTable("two_factor", {
|
||||
id: text("id").primaryKey(),
|
||||
secret: text("secret").notNull(),
|
||||
backupCodes: json("backup_codes").$type<string[]>(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
});
|
||||
|
||||
export const twofaSessions = pgTable("twofa_sessions", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
sessionId: text("session_id").notNull(), // Better Auth session ID
|
||||
|
||||
// Verification Tracking
|
||||
verificationToken: text("verification_token").notNull().unique(), // Unique nonce for this attempt
|
||||
codeUsed: text("code_used"), // The TOTP code submitted (prevent replay)
|
||||
status: varchar("status", { length: 16 }).notNull(), // "pending" | "verified" | "failed" | "expired"
|
||||
|
||||
attempts: integer("attempts").default(0).notNull(),
|
||||
maxAttempts: integer("max_attempts").default(5).notNull(),
|
||||
|
||||
verifiedAt: timestamp("verified_at"),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
|
||||
// Security Audit
|
||||
ipAddress: text("ip_address").default(""),
|
||||
userAgent: text("user_agent").default(""),
|
||||
});
|
||||
|
||||
export const twofaSessionsRelations = relations(twofaSessions, ({ one }) => ({
|
||||
userAccount: one(user, {
|
||||
fields: [twofaSessions.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
75
packages/db/schema/better.auth.schema.ts
Normal file
75
packages/db/schema/better.auth.schema.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { boolean, index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
email: text("email").notNull().unique(),
|
||||
emailVerified: boolean("email_verified").default(false).notNull(),
|
||||
image: text("image"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at")
|
||||
.defaultNow()
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
username: text("username").unique(),
|
||||
displayUsername: text("display_username"),
|
||||
role: text("role"),
|
||||
banned: boolean("banned").default(false),
|
||||
banReason: text("ban_reason"),
|
||||
banExpires: timestamp("ban_expires"),
|
||||
onboardingDone: boolean("onboarding_done").default(false),
|
||||
last2FAVerifiedAt: timestamp("last2_fa_verified_at"),
|
||||
parentId: text("parent_id"),
|
||||
});
|
||||
|
||||
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").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at")
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
},
|
||||
(table) => [index("account_userId_idx").on(table.userId)],
|
||||
);
|
||||
|
||||
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").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at")
|
||||
.defaultNow()
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
},
|
||||
(table) => [index("verification_identifier_idx").on(table.identifier)],
|
||||
);
|
||||
|
||||
export const userRelations = relations(user, ({ many }) => ({
|
||||
accounts: many(account),
|
||||
}));
|
||||
|
||||
export const accountRelations = relations(account, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [account.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
49
packages/db/schema/general.schema.ts
Normal file
49
packages/db/schema/general.schema.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
boolean,
|
||||
json,
|
||||
pgTable,
|
||||
serial,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { user } from "./better.auth.schema";
|
||||
import { relations } from "drizzle-orm";
|
||||
|
||||
export const notifications = pgTable("notifications", {
|
||||
id: serial("id").primaryKey(),
|
||||
|
||||
title: text("title").notNull(),
|
||||
body: text("body").notNull(),
|
||||
priority: varchar("priority", { length: 12 }).default("normal").notNull(), // "low", "normal", "high", "urgent"
|
||||
|
||||
type: varchar("type", { length: 12 }).notNull(),
|
||||
category: varchar("category", { length: 64 }),
|
||||
|
||||
isRead: boolean("is_read").default(false).notNull(),
|
||||
isArchived: boolean("is_archived").default(false).notNull(),
|
||||
|
||||
actionUrl: text("action_url"), // URL to navigate to when clicked
|
||||
actionType: varchar("action_type", { length: 16 }), // Type of action ("link", "function", etc.)
|
||||
actionData: json("action_data"), // Any additional data for the action
|
||||
|
||||
icon: varchar("icon", { length: 64 }), // Optional icon identifier
|
||||
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
|
||||
// Lifecycle management
|
||||
sentAt: timestamp("sent_at").notNull(),
|
||||
readAt: timestamp("read_at"),
|
||||
expiresAt: timestamp("expires_at"),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
});
|
||||
|
||||
export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
userAccount: one(user, {
|
||||
fields: [notifications.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
4
packages/db/schema/index.ts
Normal file
4
packages/db/schema/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./auth.schema";
|
||||
export * from "./better.auth.schema";
|
||||
export * from "./general.schema";
|
||||
export * from "./task.schema";
|
||||
35
packages/db/schema/task.schema.ts
Normal file
35
packages/db/schema/task.schema.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
integer,
|
||||
json,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { user } from "./better.auth.schema";
|
||||
import { relations } from "drizzle-orm";
|
||||
|
||||
export const task = pgTable("task", {
|
||||
id: text("id").primaryKey(),
|
||||
type: varchar("type", { length: 32 }).notNull(),
|
||||
status: varchar("status", { length: 16 }).notNull(),
|
||||
progress: integer("progress").default(0).notNull(),
|
||||
payload: json("payload").$type<Record<string, any>>(),
|
||||
result: json("result").$type<Record<string, any>>(),
|
||||
error: json("error").$type<Record<string, any>>(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
resourceId: text("resource_id").notNull(),
|
||||
startedAt: timestamp("started_at"),
|
||||
completedAt: timestamp("completed_at"),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
});
|
||||
|
||||
export const taskRelations = relations(task, ({ one }) => ({
|
||||
userAccount: one(user, {
|
||||
fields: [task.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
Reference in New Issue
Block a user