feat: implement security report generation backend (task 21)

- Add report-schedules DB schema table
- Create reports tRPC router with getReports, generateReport, getReport,
  deleteReport, getScheduledReports, updateSchedule procedures
- Create reports service with async report generation lifecycle
- Create report generator (compileData, renderHTML, generatePDF, uploadPDF)
- Add HTML templates for monthly-plus, annual-premium, weekly-digest
- Add Valibot schemas for input validation
- Wire router into root.ts and update DB schema exports/relations
- Install puppeteer for HTML-to-PDF conversion
- Write unit tests for router (11 tests) and service (12 tests)
This commit is contained in:
2026-05-25 17:08:43 -04:00
parent 4f7882a10d
commit 659ab9b71a
16 changed files with 3102 additions and 10 deletions

View File

@@ -0,0 +1,18 @@
import { pgTable, text, timestamp, index, uuid, boolean } from "drizzle-orm/pg-core";
import { users } from "./auth";
import { reportType } from "./enums";
export const reportSchedules = pgTable("report_schedules", {
id: uuid("id").defaultRandom().primaryKey(),
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
enabled: boolean("enabled").default(true).notNull(),
frequency: text("frequency").notNull(),
reportType: reportType("report_type").notNull(),
lastGeneratedAt: timestamp("last_generated_at", { withTimezone: true, mode: "date" }),
nextScheduledAt: timestamp("next_scheduled_at", { withTimezone: true, mode: "date" }),
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull().$onUpdate(() => new Date()),
}, (table) => ({
userIdIdx: index("report_schedules_user_id_idx").on(table.userId),
enabledIdx: index("report_schedules_enabled_idx").on(table.enabled),
}));