diff --git a/src/db/schema/auditLogs.ts b/src/db/schema/auditLogs.ts new file mode 100644 index 0000000..4141dee --- /dev/null +++ b/src/db/schema/auditLogs.ts @@ -0,0 +1,18 @@ +import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core'; +import { jsonb } from 'drizzle-orm/pg-core'; +import { users } from './users.js'; + +export const auditLogs = pgTable('audit_logs', { + id: uuid('id').primaryKey().defaultRandom(), + adminId: uuid('admin_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + action: varchar('action', { length: 100 }).notNull(), + targetType: varchar('target_type', { length: 50 }).notNull(), + targetId: uuid('target_id').notNull(), + details: jsonb('details').$type>(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), +}); + +export type AuditLog = typeof auditLogs.$inferSelect; +export type NewAuditLog = typeof auditLogs.$inferInsert; diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts index e7a3efc..e7b0e0b 100644 --- a/src/db/schema/index.ts +++ b/src/db/schema/index.ts @@ -7,3 +7,7 @@ export * from './testQuestions.js'; export * from './questionBank.js'; export * from './questionCacheMeta.js'; export * from './questionReports.js'; +export * from './userStats.js'; +export * from './auditLogs.js'; +export * from './userQuestionLog.js'; +export * from './verificationTokens.js'; diff --git a/src/db/schema/userQuestionLog.ts b/src/db/schema/userQuestionLog.ts new file mode 100644 index 0000000..51bea14 --- /dev/null +++ b/src/db/schema/userQuestionLog.ts @@ -0,0 +1,17 @@ +import { pgTable, uuid, timestamp } from 'drizzle-orm/pg-core'; +import { users } from './users.js'; +import { questionBank } from './questionBank.js'; + +export const userQuestionLog = pgTable('user_question_log', { + id: uuid('id').primaryKey().defaultRandom(), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + questionBankId: uuid('question_bank_id') + .notNull() + .references(() => questionBank.id, { onDelete: 'cascade' }), + seenAt: timestamp('seen_at', { withTimezone: true }).notNull().defaultNow(), +}); + +export type UserQuestionLog = typeof userQuestionLog.$inferSelect; +export type NewUserQuestionLog = typeof userQuestionLog.$inferInsert; diff --git a/src/db/schema/userStats.ts b/src/db/schema/userStats.ts new file mode 100644 index 0000000..732cc15 --- /dev/null +++ b/src/db/schema/userStats.ts @@ -0,0 +1,26 @@ +import { pgTable, uuid, integer, timestamp } from 'drizzle-orm/pg-core'; +import { unique } from 'drizzle-orm/pg-core'; +import { stackEnum, levelEnum } from './enums.js'; +import { users } from './users.js'; + +export const userStats = pgTable( + 'user_stats', + { + id: uuid('id').primaryKey().defaultRandom(), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + stack: stackEnum('stack').notNull(), + level: levelEnum('level').notNull(), + totalQuestions: integer('total_questions').notNull().default(0), + correctAnswers: integer('correct_answers').notNull().default(0), + testsTaken: integer('tests_taken').notNull().default(0), + lastTestAt: timestamp('last_test_at', { withTimezone: true }), + }, + (t) => ({ + userStackLevelUnique: unique().on(t.userId, t.stack, t.level), + }) +); + +export type UserStat = typeof userStats.$inferSelect; +export type NewUserStat = typeof userStats.$inferInsert; diff --git a/src/db/schema/verificationTokens.ts b/src/db/schema/verificationTokens.ts new file mode 100644 index 0000000..5bf62a9 --- /dev/null +++ b/src/db/schema/verificationTokens.ts @@ -0,0 +1,22 @@ +import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core'; +import { users } from './users.js'; + +export const emailVerificationCodes = pgTable('email_verification_codes', { + id: uuid('id').primaryKey().defaultRandom(), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + code: varchar('code', { length: 10 }).notNull(), + expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), +}); + +export const passwordResetTokens = pgTable('password_reset_tokens', { + id: uuid('id').primaryKey().defaultRandom(), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + tokenHash: varchar('token_hash', { length: 255 }).notNull(), + expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), +});