From 970a864823dab6ab30655547f952fa527b8272c6 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 4 Mar 2026 13:38:31 +0300 Subject: [PATCH] feat: add question bank schema Made-with: Cursor --- src/db/schema/index.ts | 3 +++ src/db/schema/questionBank.ts | 22 ++++++++++++++++++++++ src/db/schema/questionCacheMeta.ts | 18 ++++++++++++++++++ src/db/schema/questionReports.ts | 21 +++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 src/db/schema/questionBank.ts create mode 100644 src/db/schema/questionCacheMeta.ts create mode 100644 src/db/schema/questionReports.ts diff --git a/src/db/schema/index.ts b/src/db/schema/index.ts index 1225787..e7a3efc 100644 --- a/src/db/schema/index.ts +++ b/src/db/schema/index.ts @@ -4,3 +4,6 @@ export * from './sessions.js'; export * from './subscriptions.js'; export * from './tests.js'; export * from './testQuestions.js'; +export * from './questionBank.js'; +export * from './questionCacheMeta.js'; +export * from './questionReports.js'; diff --git a/src/db/schema/questionBank.ts b/src/db/schema/questionBank.ts new file mode 100644 index 0000000..8ea4e3c --- /dev/null +++ b/src/db/schema/questionBank.ts @@ -0,0 +1,22 @@ +import { pgTable, uuid, text, integer, timestamp } from 'drizzle-orm/pg-core'; +import { jsonb } from 'drizzle-orm/pg-core'; +import { stackEnum, levelEnum, questionTypeEnum, questionStatusEnum, questionSourceEnum } from './enums.js'; + +export const questionBank = pgTable('question_bank', { + id: uuid('id').primaryKey().defaultRandom(), + stack: stackEnum('stack').notNull(), + level: levelEnum('level').notNull(), + type: questionTypeEnum('type').notNull(), + questionText: text('question_text').notNull(), + options: jsonb('options').$type>(), + correctAnswer: jsonb('correct_answer').$type().notNull(), + explanation: text('explanation').notNull(), + status: questionStatusEnum('status').notNull().default('pending'), + source: questionSourceEnum('source').notNull(), + usageCount: integer('usage_count').notNull().default(0), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), + approvedAt: timestamp('approved_at', { withTimezone: true }), +}); + +export type QuestionBank = typeof questionBank.$inferSelect; +export type NewQuestionBank = typeof questionBank.$inferInsert; diff --git a/src/db/schema/questionCacheMeta.ts b/src/db/schema/questionCacheMeta.ts new file mode 100644 index 0000000..c0b1e82 --- /dev/null +++ b/src/db/schema/questionCacheMeta.ts @@ -0,0 +1,18 @@ +import { pgTable, uuid, varchar, integer, timestamp } from 'drizzle-orm/pg-core'; +import { jsonb } from 'drizzle-orm/pg-core'; +import { questionBank } from './questionBank.js'; + +export const questionCacheMeta = pgTable('question_cache_meta', { + id: uuid('id').primaryKey().defaultRandom(), + questionBankId: uuid('question_bank_id') + .notNull() + .references(() => questionBank.id, { onDelete: 'cascade' }), + llmModel: varchar('llm_model', { length: 100 }).notNull(), + promptHash: varchar('prompt_hash', { length: 64 }).notNull(), + generationTimeMs: integer('generation_time_ms').notNull(), + rawResponse: jsonb('raw_response').$type(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), +}); + +export type QuestionCacheMeta = typeof questionCacheMeta.$inferSelect; +export type NewQuestionCacheMeta = typeof questionCacheMeta.$inferInsert; diff --git a/src/db/schema/questionReports.ts b/src/db/schema/questionReports.ts new file mode 100644 index 0000000..b5f152f --- /dev/null +++ b/src/db/schema/questionReports.ts @@ -0,0 +1,21 @@ +import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core'; +import { reportStatusEnum } from './enums.js'; +import { questionBank } from './questionBank.js'; +import { users } from './users.js'; + +export const questionReports = pgTable('question_reports', { + id: uuid('id').primaryKey().defaultRandom(), + questionBankId: uuid('question_bank_id') + .notNull() + .references(() => questionBank.id, { onDelete: 'cascade' }), + userId: uuid('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + reason: text('reason').notNull(), + status: reportStatusEnum('status').notNull().default('open'), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), + resolvedAt: timestamp('resolved_at', { withTimezone: true }), +}); + +export type QuestionReport = typeof questionReports.$inferSelect; +export type NewQuestionReport = typeof questionReports.$inferInsert;