export interface Folder { id: number; name: string; system: boolean; messageCount: number; unreadCount: number; } export interface Message { id: number; folderId: number; senderEmail: string; senderName?: string | null; recipientEmail: string; ccEmail?: string | null; subject: string; body: string; html: boolean; read: boolean; flagged: boolean; spam: boolean; messageSize?: number | null; receivedAt: string; hasAttachments: boolean; attachmentCount: number; answeredBy?: string | null; // login that answered it from the web client (shared mailboxes) } /** MailboxAccess.Entry — a mailbox of the logged-in user (left menu) */ export interface MailboxEntry { email: string; displayName: string; kind: 'OWN' | 'MEMBER' | 'ATTACHED'; shared: boolean; unread: number; } export interface Attachment { id: number; filename: string; contentType: string; sizeBytes: number; } export interface Contact { id?: number; name: string; email: string; phone?: string; company?: string; } export interface BlacklistEntry { id: number; senderEmail: string; reason?: string | null; createdAt?: string; } export interface Profile { email: string; displayName: string; usedBytes: number; quotaBytes: number; createdAt: string; admin?: boolean; // may manage mailboxes (Settings → Mailboxes) } /** MailboxAdminController.MailboxView */ export interface Mailbox { id: number; email: string; displayName: string; active: boolean; admin: boolean; trap: boolean; // what it sends to outside addresses stays in its own INBOX (staging@) shared: boolean; // shared mailbox: no login of its own, members open it members: string[]; // who may open it usedBytes: number; quotaBytes: number; createdAt: string; } /** MailLists.MailList — mail to the address → a copy to every member, into the folder (null — INBOX) */ export interface MailList { id: number; address: string; folder: string | null; description: string | null; openToOutside: boolean; members: string[]; } /** password — generated by the server, shown once; null when the administrator chose it */ export interface MailboxCreated { mailbox: Mailbox; password: string | null; } export interface ServerConfig { registrationEnabled: boolean; domains: string[]; } export interface Draft { to: string; cc: string; subject: string; body: string; html: boolean; } /** System folders: label and icon in the web client. */ export const FOLDER_VIEW: Record = { INBOX: { label: 'Inbox', icon: 'inbox' }, Drafts: { label: 'Drafts', icon: 'edit_note' }, Sent: { label: 'Sent', icon: 'send' }, Spam: { label: 'Spam', icon: 'report' }, Trash: { label: 'Trash', icon: 'delete' } }; export function folderLabel(folder: Folder): string { return folder.system ? (FOLDER_VIEW[folder.name]?.label ?? folder.name) : folder.name; } export function folderIcon(folder: Folder): string { return folder.system ? (FOLDER_VIEW[folder.name]?.icon ?? 'folder') : 'folder'; } export function formatBytes(bytes: number | null | undefined): string { const value = bytes ?? 0; if (value < 1024) return `${value} B`; if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`; if (value < 1024 * 1024 * 1024) return `${(value / 1024 / 1024).toFixed(1)} MB`; return `${(value / 1024 / 1024 / 1024).toFixed(2)} GB`; } /** Text of an API error: our {error}, Spring's {message}, or the status. */ export function errorText(error: unknown): string { const e = error as { error?: { error?: string; message?: string; status?: number } | string; status?: number; message?: string }; if (e?.error && typeof e.error === 'object') { // Spring's error body: {status, error: "Bad Request", message: "No such mailbox: …"} — the reason is in message const body = e.error; const springBody = typeof body.status === 'number'; return (springBody ? body.message || body.error : body.error || body.message) || `Error ${e.status ?? ''}`.trim(); } if (typeof e?.error === 'string' && e.error) return e.error; if (e?.status === 0) return 'Server is not reachable'; return e?.message || 'Something went wrong'; }