Skip to content
File

Blob: src/worker/db/schema.ts

typescript75 lines
1import { sql } from "drizzle-orm";
2import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
3 
4export const domains = sqliteTable(
5 "domains",
6 {
7 id: text("id").primaryKey(),
8 domain: text("domain").notNull().unique(),
9 isActive: integer("is_active", { mode: "boolean" }).notNull().default(true),
10 createdAt: integer("created_at", { mode: "timestamp_ms" })
11 .notNull()
12 .default(sql`(unixepoch() * 1000)`),
13 },
14 (table) => [index("idx_domains_active").on(table.isActive)],
15);
16 
17export const inboxes = sqliteTable(
18 "inboxes",
19 {
20 id: text("id").primaryKey(),
21 localPart: text("local_part").notNull(),
22 domain: text("domain").notNull(),
23 fullAddress: text("full_address").notNull().unique(),
24 isPermanent: integer("is_permanent", { mode: "boolean" }).notNull().default(false),
25 createdAt: integer("created_at", { mode: "timestamp_ms" })
26 .notNull()
27 .default(sql`(unixepoch() * 1000)`),
28 expiresAt: integer("expires_at", { mode: "timestamp_ms" }),
29 },
30 (table) => [
31 uniqueIndex("idx_inboxes_local_domain").on(table.localPart, table.domain),
32 index("idx_inboxes_domain").on(table.domain),
33 index("idx_inboxes_permanent_expires").on(table.isPermanent, table.expiresAt),
34 index("idx_inboxes_permanent_created").on(table.isPermanent, table.createdAt),
35 index("idx_inboxes_permanent_domain_local").on(table.isPermanent, table.domain, table.localPart),
36 ],
37);
38 
39export const emails = sqliteTable(
40 "emails",
41 {
42 id: text("id").primaryKey(),
43 inboxId: text("inbox_id")
44 .notNull()
45 .references(() => inboxes.id, { onDelete: "cascade" }),
46 recipientAddress: text("recipient_address").notNull().default(""),
47 fromAddress: text("from_address").notNull(),
48 fromName: text("from_name"),
49 subject: text("subject").default("(no subject)"),
50 receivedAt: integer("received_at", { mode: "timestamp_ms" })
51 .notNull()
52 .default(sql`(unixepoch() * 1000)`),
53 isRead: integer("is_read", { mode: "boolean" }).notNull().default(false),
54 sizeBytes: integer("size_bytes").default(0),
55 hasAttachments: integer("has_attachments", { mode: "boolean" }).notNull().default(false),
56 bodyKey: text("body_key"),
57 },
58 (table) => [index("idx_emails_inbox_received_id").on(table.inboxId, table.receivedAt, table.id)],
59);
60 
61export const attachments = sqliteTable(
62 "attachments",
63 {
64 id: text("id").primaryKey(),
65 emailId: text("email_id")
66 .notNull()
67 .references(() => emails.id, { onDelete: "cascade" }),
68 filename: text("filename"),
69 contentType: text("content_type"),
70 sizeBytes: integer("size_bytes").default(0),
71 storageKey: text("storage_key").notNull(),
72 },
73 (table) => [index("idx_attachments_email").on(table.emailId)],
74);