96 lines
3.6 KiB
TypeScript
96 lines
3.6 KiB
TypeScript
|
|
import {
|
||
|
|
pgTable,
|
||
|
|
text,
|
||
|
|
boolean,
|
||
|
|
integer,
|
||
|
|
timestamp,
|
||
|
|
index,
|
||
|
|
uniqueIndex,
|
||
|
|
} from 'drizzle-orm/pg-core';
|
||
|
|
import { sql } from 'drizzle-orm';
|
||
|
|
import { ports } from './ports';
|
||
|
|
import { clients } from './clients';
|
||
|
|
import { files } from './documents';
|
||
|
|
|
||
|
|
export const emailAccounts = pgTable(
|
||
|
|
'email_accounts',
|
||
|
|
{
|
||
|
|
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||
|
|
userId: text('user_id').notNull(), // references Better Auth user ID
|
||
|
|
portId: text('port_id')
|
||
|
|
.notNull()
|
||
|
|
.references(() => ports.id),
|
||
|
|
provider: text('provider').notNull(), // google, outlook, custom
|
||
|
|
emailAddress: text('email_address').notNull(),
|
||
|
|
smtpHost: text('smtp_host').notNull(),
|
||
|
|
smtpPort: integer('smtp_port').notNull(),
|
||
|
|
imapHost: text('imap_host').notNull(),
|
||
|
|
imapPort: integer('imap_port').notNull(),
|
||
|
|
// credentials_enc stored as base64-encoded text (encrypted at application layer)
|
||
|
|
credentialsEnc: text('credentials_enc').notNull(),
|
||
|
|
isActive: boolean('is_active').notNull().default(true),
|
||
|
|
lastSyncAt: timestamp('last_sync_at', { withTimezone: true }),
|
||
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||
|
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||
|
|
},
|
||
|
|
(table) => [
|
||
|
|
index('idx_ea_user').on(table.userId),
|
||
|
|
index('idx_ea_port').on(table.portId),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
|
||
|
|
export const emailThreads = pgTable(
|
||
|
|
'email_threads',
|
||
|
|
{
|
||
|
|
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||
|
|
portId: text('port_id')
|
||
|
|
.notNull()
|
||
|
|
.references(() => ports.id),
|
||
|
|
clientId: text('client_id').references(() => clients.id),
|
||
|
|
subject: text('subject'),
|
||
|
|
lastMessageAt: timestamp('last_message_at', { withTimezone: true }),
|
||
|
|
messageCount: integer('message_count').notNull().default(0),
|
||
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||
|
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||
|
|
},
|
||
|
|
(table) => [
|
||
|
|
index('idx_et_client').on(table.clientId),
|
||
|
|
index('idx_et_port').on(table.portId),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
|
||
|
|
export const emailMessages = pgTable(
|
||
|
|
'email_messages',
|
||
|
|
{
|
||
|
|
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||
|
|
threadId: text('thread_id')
|
||
|
|
.notNull()
|
||
|
|
.references(() => emailThreads.id, { onDelete: 'cascade' }),
|
||
|
|
messageIdHeader: text('message_id_header'), // email Message-ID header
|
||
|
|
fromAddress: text('from_address').notNull(),
|
||
|
|
toAddresses: text('to_addresses').array().notNull(),
|
||
|
|
ccAddresses: text('cc_addresses').array(),
|
||
|
|
subject: text('subject'),
|
||
|
|
bodyText: text('body_text'),
|
||
|
|
bodyHtml: text('body_html'),
|
||
|
|
direction: text('direction').notNull(), // inbound, outbound
|
||
|
|
sentAt: timestamp('sent_at', { withTimezone: true }).notNull(),
|
||
|
|
attachmentFileIds: text('attachment_file_ids').array(), // references to files table
|
||
|
|
rawFileId: text('raw_file_id').references(() => files.id),
|
||
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||
|
|
},
|
||
|
|
(table) => [
|
||
|
|
index('idx_em_thread').on(table.threadId),
|
||
|
|
uniqueIndex('idx_em_message_id').on(table.messageIdHeader).where(
|
||
|
|
sql`${table.messageIdHeader} IS NOT NULL`
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
|
||
|
|
export type EmailAccount = typeof emailAccounts.$inferSelect;
|
||
|
|
export type NewEmailAccount = typeof emailAccounts.$inferInsert;
|
||
|
|
export type EmailThread = typeof emailThreads.$inferSelect;
|
||
|
|
export type NewEmailThread = typeof emailThreads.$inferInsert;
|
||
|
|
export type EmailMessage = typeof emailMessages.$inferSelect;
|
||
|
|
export type NewEmailMessage = typeof emailMessages.$inferInsert;
|