types.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. export type ViewKey =
  2. | 'dashboard'
  3. | 'domains'
  4. | 'dns-api'
  5. | 'smtp'
  6. | 'inbox'
  7. | 'tokens'
  8. | 'logs'
  9. | 'webhooks'
  10. | 'admin'
  11. | 'settings';
  12. export type DomainMode = 'list' | 'detail';
  13. export type UserStatus = 'pending_email' | 'pending_review' | 'active' | 'disabled';
  14. export type UserRole = 'admin' | 'user';
  15. export interface UserResourceCounts {
  16. domains: number;
  17. dnsCredentials: number;
  18. apiTokens: number;
  19. inboundMailboxes: number;
  20. inboundMessages: number;
  21. sendEvents: number;
  22. smtpCredential: number;
  23. }
  24. export interface User {
  25. id: number;
  26. username: string;
  27. email: string;
  28. role: UserRole;
  29. status: UserStatus;
  30. createdAt?: string;
  31. updatedAt?: string;
  32. resourceCounts?: UserResourceCounts;
  33. }
  34. export interface AdminUser extends User {
  35. resourceCounts: UserResourceCounts;
  36. }
  37. export interface RuntimeConfig {
  38. appBaseUrl: string;
  39. mailHostname: string;
  40. sendingIp: string;
  41. defaultSpfMechanisms: string;
  42. dmarcPolicy: string;
  43. dmarcRua: string;
  44. sendRequiresVerified: boolean;
  45. engagementTrackingEnabled: boolean;
  46. listUnsubscribeMailto: string;
  47. listUnsubscribeUrl: string;
  48. listUnsubscribePostEnabled: boolean;
  49. feedbackIdEnabled: boolean;
  50. reportAbuseTo: string;
  51. csaComplaintsTo: string;
  52. bounceAddress: string;
  53. bounceEnvelopeEnabled: boolean;
  54. systemChecks?: SystemChecks;
  55. submission?: {
  56. enabled: boolean;
  57. host: string;
  58. ports: Array<{ port: number; protocol: string }>;
  59. username: string;
  60. passwordSet: boolean;
  61. inboundEnabled: boolean;
  62. tls: boolean;
  63. requireTlsForAuth: boolean;
  64. };
  65. apiTokenSet?: boolean;
  66. usingDefaultAdminPassword?: boolean;
  67. }
  68. export interface SystemChecks {
  69. checkedAt: string;
  70. ptr: DnsRecord;
  71. }
  72. export interface DnsRecord {
  73. key: string;
  74. label: string;
  75. host: string;
  76. type: string;
  77. value?: string;
  78. status?: string;
  79. current?: string | string[];
  80. warnings?: string[];
  81. managed?: boolean;
  82. }
  83. export interface DomainStatus {
  84. checkedAt?: string;
  85. verified?: boolean;
  86. records?: DnsRecord[];
  87. optionalRecords?: DnsRecord[];
  88. warnings?: string[];
  89. live?: Record<string, string[]>;
  90. apply?: {
  91. ok: boolean;
  92. results?: Array<{
  93. key: string;
  94. type: string;
  95. host: string;
  96. ok: boolean;
  97. skipped?: boolean;
  98. detail?: string;
  99. error?: string;
  100. }>;
  101. };
  102. }
  103. export interface Domain {
  104. id: number;
  105. userId: number;
  106. dnsCredentialId: number | null;
  107. smtpRelayId: number | null;
  108. domain: string;
  109. selector: string;
  110. verificationToken: string;
  111. dkimPublic: string;
  112. senderHost: string;
  113. sendingIp: string;
  114. spfExtra: string;
  115. dmarcPolicy: string;
  116. dmarcRua: string;
  117. status: DomainStatus;
  118. createdAt: string;
  119. updatedAt: string;
  120. }
  121. export interface DnsCredential {
  122. id: number;
  123. userId?: number;
  124. name: string;
  125. provider: 'cloudflare' | 'aliyun' | 'dnspod' | string;
  126. zoneName: string;
  127. defaultTtl: number;
  128. credentialSet?: boolean;
  129. createdAt: string;
  130. updatedAt: string;
  131. }
  132. export interface SmtpCredential {
  133. id?: number;
  134. userId?: number;
  135. username: string;
  136. password?: string;
  137. passwordSet?: boolean;
  138. passwordRecoverable?: boolean;
  139. createdAt?: string;
  140. updatedAt?: string;
  141. }
  142. export interface SmtpRelay {
  143. id: number;
  144. userId?: number;
  145. name: string;
  146. host: string;
  147. port: number;
  148. secure: boolean;
  149. username: string;
  150. password?: string;
  151. passwordSet: boolean;
  152. helo: string;
  153. isDefault: boolean;
  154. createdAt?: string;
  155. updatedAt?: string;
  156. }
  157. export interface SmtpRelayPayload {
  158. name: string;
  159. host: string;
  160. port: number | string;
  161. secure?: boolean;
  162. username?: string;
  163. password?: string;
  164. helo?: string;
  165. isDefault?: boolean;
  166. }
  167. export interface SystemEmailSettings {
  168. host: string;
  169. port: number;
  170. secure: boolean;
  171. username: string;
  172. password?: string;
  173. passwordSet: boolean;
  174. helo: string;
  175. fromEmail: string;
  176. fromName: string;
  177. testRecipient: string;
  178. }
  179. export interface AdminResourceInventory {
  180. users: AdminUserResourceGroup[];
  181. warnings: Array<{
  182. type: 'domain_dns_credential_owner_mismatch' | string;
  183. domainId: number;
  184. domain: string;
  185. domainUserId: number;
  186. dnsCredentialId: number;
  187. dnsCredentialUserId: number;
  188. }>;
  189. }
  190. export interface AdminUserResourceGroup {
  191. user: AdminUser;
  192. domains: Domain[];
  193. dnsCredentials: DnsCredential[];
  194. smtpCredential: SmtpCredential | null;
  195. apiTokens: ApiToken[];
  196. inboundMailboxes: InboundMailbox[];
  197. inboundMessageCount: number;
  198. sendEventCount: number;
  199. }
  200. export interface UserMergeOptions {
  201. transferDomains: boolean;
  202. transferDnsCredentials: boolean;
  203. transferApiTokens: boolean;
  204. transferSendEvents: boolean;
  205. transferSmtpCredential: boolean;
  206. disableSource: boolean;
  207. }
  208. export interface UserMergePreview {
  209. sourceUser: User;
  210. targetUser: User;
  211. confirmationText: string;
  212. counts: UserResourceCounts;
  213. selectedCounts: UserResourceCounts;
  214. defaultOptions: UserMergeOptions;
  215. resources: {
  216. source: Omit<AdminUserResourceGroup, 'user'>;
  217. target: Omit<AdminUserResourceGroup, 'user'>;
  218. };
  219. smtp: {
  220. sourceCredential: SmtpCredential | null;
  221. targetCredential: SmtpCredential | null;
  222. conflict: boolean;
  223. };
  224. warnings: Array<{ type: string; message?: string }>;
  225. }
  226. export interface UserMergeResult {
  227. sourceUser: User;
  228. targetUser: User;
  229. counts: UserResourceCounts;
  230. warnings: UserMergePreview['warnings'];
  231. }
  232. export interface AuditLogEntry {
  233. id: number;
  234. actorUserId: number | null;
  235. action: string;
  236. targetType: string;
  237. targetId: string;
  238. targetUserId: number | null;
  239. summary: Record<string, unknown>;
  240. createdAt: string;
  241. }
  242. export interface ApiToken {
  243. id: number;
  244. userId?: number;
  245. name: string;
  246. tokenPrefix: string;
  247. token?: string;
  248. lastUsedAt?: string;
  249. createdAt: string;
  250. }
  251. export interface InboundMailbox {
  252. id: number;
  253. userId: number;
  254. domainId: number;
  255. domain: string;
  256. address: string;
  257. localPart: string;
  258. displayName: string;
  259. status: string;
  260. messageCount: number;
  261. unreadCount: number;
  262. lastMessageAt?: string | null;
  263. createdAt: string;
  264. updatedAt: string;
  265. }
  266. export interface InboundMessage {
  267. id: number;
  268. mailboxId: number;
  269. userId: number;
  270. domainId: number;
  271. domain: string;
  272. mailboxAddress: string;
  273. sender: string;
  274. recipients: string[];
  275. subject: string;
  276. messageId: string;
  277. preview: string;
  278. read: boolean;
  279. receivedAt: string;
  280. createdAt: string;
  281. updatedAt: string;
  282. rawMessage?: string;
  283. textBody?: string;
  284. htmlBody?: string;
  285. }
  286. export interface DeliveryLogEntry {
  287. at: string;
  288. phase: 'connect' | 'smtp' | 'auth' | 'envelope' | 'data' | 'queue' | 'quit' | 'error' | string;
  289. direction?: 'client' | 'server' | 'system' | string;
  290. command?: string;
  291. response?: string;
  292. message?: string;
  293. code?: number;
  294. ok?: boolean;
  295. messageBytes?: number;
  296. }
  297. export interface DeliveryAttempt {
  298. at: string;
  299. source?: string;
  300. queueId: string;
  301. recipient?: string;
  302. relay?: string;
  303. dsn?: string;
  304. status: string;
  305. response?: string;
  306. raw?: string;
  307. }
  308. export interface SendEventTimelineEntry {
  309. stage: string;
  310. at: string;
  311. tone: 'success' | 'warning' | 'error' | 'info' | 'neutral';
  312. status?: string;
  313. queueId?: string;
  314. recipient?: string;
  315. relay?: string;
  316. response?: string;
  317. webhookId?: number;
  318. responseStatus?: number | null;
  319. source?: string;
  320. targetOrigin?: string;
  321. trackingLinkId?: number | null;
  322. }
  323. export interface TrackingEvent {
  324. id: number;
  325. sendEventId: number;
  326. trackingLinkId?: number | null;
  327. eventType: 'open' | 'click';
  328. source: 'direct' | 'proxy' | 'scanner';
  329. occurredAt: string;
  330. userAgent?: string;
  331. targetOrigin?: string;
  332. }
  333. export interface TrackingLink {
  334. id: number;
  335. target: string;
  336. targetOrigin: string;
  337. clicks: number;
  338. firstClickedAt?: string | null;
  339. lastClickedAt?: string | null;
  340. }
  341. export interface TrackingSummary {
  342. totalOpens: number;
  343. totalClicks: number;
  344. uniqueOpen: boolean;
  345. uniqueClick: boolean;
  346. proxyOpens: number;
  347. scannerEvents: number;
  348. firstOpenedAt?: string | null;
  349. lastOpenedAt?: string | null;
  350. firstClickedAt?: string | null;
  351. lastClickedAt?: string | null;
  352. }
  353. export interface SendTracking {
  354. enabled: boolean;
  355. opens: boolean;
  356. clicks: boolean;
  357. messageLevel: boolean;
  358. summary?: TrackingSummary;
  359. events?: TrackingEvent[];
  360. eventCount?: number;
  361. eventsTruncated?: boolean;
  362. links?: TrackingLink[];
  363. linkCount?: number;
  364. linksTruncated?: boolean;
  365. }
  366. export interface SendEvent {
  367. id: number;
  368. userId: number;
  369. domainId: number | null;
  370. smtpRelayId: number | null;
  371. domain?: string;
  372. sender: string;
  373. recipients: string[];
  374. subject: string;
  375. status: string;
  376. detail: string;
  377. queueId?: string;
  378. deliveryLog?: DeliveryLogEntry[];
  379. deliveryAttempts?: DeliveryAttempt[];
  380. webhookDeliveries?: WebhookDelivery[];
  381. tracking?: SendTracking;
  382. deliveredAt?: string;
  383. createdAt: string;
  384. }
  385. export interface Analytics {
  386. windowDays: number;
  387. summary: {
  388. total: number;
  389. submitted: number;
  390. queued: number;
  391. failed: number;
  392. accepted: number;
  393. delivered: number;
  394. pending: number;
  395. deferred: number;
  396. bounced: number;
  397. terminalFailed: number;
  398. recipients: number;
  399. today: number;
  400. last7Days: number;
  401. successRate: number;
  402. acceptanceRate: number;
  403. deliveryRate: number;
  404. failureRate: number;
  405. domains: number;
  406. verifiedDomains: number;
  407. };
  408. deliveryFunnel: Array<{
  409. stage: string;
  410. total: number;
  411. rate: number;
  412. }>;
  413. engagement: {
  414. trackedDelivered: number;
  415. totalOpens: number;
  416. uniqueOpens: number;
  417. proxyOpens: number;
  418. totalClicks: number;
  419. uniqueClicks: number;
  420. scannerEvents: number;
  421. openRate: number;
  422. clickRate: number;
  423. clickToOpenRate: number;
  424. };
  425. engagementByDay: Array<{
  426. day: string;
  427. opens: number;
  428. uniqueOpens: number;
  429. clicks: number;
  430. uniqueClicks: number;
  431. scannerEvents: number;
  432. }>;
  433. topLinks: Array<{
  434. fingerprint: string;
  435. target: string;
  436. targetOrigin: string;
  437. clicks: number;
  438. uniqueClicks: number;
  439. lastClickedAt: string;
  440. }>;
  441. byDay: Array<{
  442. day: string;
  443. date?: string;
  444. total: number;
  445. queued: number;
  446. failed: number;
  447. accepted?: number;
  448. delivered?: number;
  449. pending?: number;
  450. terminalFailed?: number;
  451. recipients: number;
  452. }>;
  453. byDomain: Array<{
  454. domain: string;
  455. total: number;
  456. queued: number;
  457. failed: number;
  458. accepted?: number;
  459. delivered?: number;
  460. pending?: number;
  461. terminalFailed?: number;
  462. recipients: number;
  463. }>;
  464. byStatus: Array<{ status: string; total: number }>;
  465. hourly: Array<{
  466. hour: number;
  467. total: number;
  468. queued: number;
  469. failed: number;
  470. accepted?: number;
  471. delivered?: number;
  472. pending?: number;
  473. terminalFailed?: number;
  474. }>;
  475. failureReasons: Array<{
  476. reason: string;
  477. total: number;
  478. statuses: Record<string, number>;
  479. lastSeenAt: string;
  480. }>;
  481. recentFailures: SendEvent[];
  482. }
  483. export interface AppData {
  484. me: User | null;
  485. config: RuntimeConfig | null;
  486. domains: Domain[];
  487. events: SendEvent[];
  488. analytics: Analytics | null;
  489. smtpCredential: SmtpCredential | null;
  490. smtpCredentials: SmtpCredential[];
  491. smtpRelays: SmtpRelay[];
  492. inboundMailboxes: InboundMailbox[];
  493. inboundMessages: InboundMessage[];
  494. dnsCredentials: DnsCredential[];
  495. apiTokens: ApiToken[];
  496. settings: RuntimeConfig | null;
  497. users: User[];
  498. }
  499. export interface AddDomainPayload {
  500. domain: string;
  501. senderHost?: string;
  502. sendingIp?: string;
  503. dnsCredentialId?: number | string;
  504. smtpRelayId?: number | string | null;
  505. selector?: string;
  506. dmarcPolicy?: string;
  507. spfExtra?: string;
  508. immediateCheck?: boolean;
  509. }
  510. export interface DomainPatchPayload {
  511. selector?: string;
  512. dnsCredentialId?: number | string | null;
  513. smtpRelayId?: number | string | null;
  514. senderHost?: string;
  515. sendingIp?: string;
  516. spfExtra?: string;
  517. dmarcPolicy?: string;
  518. dmarcRua?: string;
  519. }
  520. export type WebhookEvent = 'sent' | 'bounced' | 'failed' | 'opened' | 'clicked';
  521. export type WebhookDeliveryStatus = 'pending' | 'processing' | 'success' | 'dead';
  522. export interface Webhook {
  523. id: number;
  524. userId: number;
  525. domainId: number | null;
  526. name: string;
  527. url: string;
  528. secretPrefix: string;
  529. events: WebhookEvent[];
  530. enabled: boolean;
  531. createdAt: string;
  532. updatedAt: string;
  533. /** Present only on create / rotate-secret responses. */
  534. secret?: string;
  535. }
  536. export interface WebhookPayload {
  537. name: string;
  538. url: string;
  539. events: WebhookEvent[];
  540. domainId?: number | string | null;
  541. enabled?: boolean;
  542. }
  543. export interface WebhookPatchPayload {
  544. name?: string;
  545. url?: string;
  546. events?: WebhookEvent[];
  547. domainId?: number | string | null;
  548. enabled?: boolean;
  549. }
  550. export interface WebhookDelivery {
  551. id: number;
  552. webhookId: number;
  553. userId: number;
  554. sendEventId: number;
  555. eventType: WebhookEvent | string;
  556. payloadJson?: string;
  557. status: WebhookDeliveryStatus | string;
  558. attemptCount: number;
  559. nextAttemptAt?: string | null;
  560. lastAttemptAt?: string | null;
  561. responseStatus?: number | null;
  562. responseBodyPreview?: string;
  563. error?: string;
  564. createdAt: string;
  565. }
  566. export interface WebhookDeliveryFilters {
  567. status?: WebhookDeliveryStatus | string;
  568. webhookId?: number;
  569. eventType?: WebhookEvent | string;
  570. limit?: number;
  571. }