Files
pn-new-crm/src/lib/db/schema/email.ts

96 lines
3.6 KiB
TypeScript
Raw Normal View History

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;