import type { ApiSettings, DeltaDeletedItem, MailConnectionTestResponse, MailImapFolderListResponse, MailImapTestPayload, MailCredentialEnvelope, MailProfilePolicy, MailProfilePolicyResponse, MailProfileScope, MailSecurity, MailServerEndpoint, MailServerProfile, MailServerProfilePayload, MailSmtpTestPayload, MockMailboxMessage, MockMailboxMessageResponse } from "@govoplan/core-webui"; import { apiFetch, apiGetList, apiPath, apiPost, apiPostJson } from "./client"; export { mailProfilePatternKeys, mailProfilePolicyLimitKeys } from "@govoplan/core-webui"; export type { MailConnectionTestResponse, MailCredentialEnvelope, MailCredentialPolicy, MailImapFolderListResponse, MailImapFolderResponse, MailImapTestPayload, MailProfilePatternKey, MailProfilePatternRules, MailProfilePolicy, MailProfilePolicyLimitKey, MailProfilePolicyLimitPermissions, MailProfilePolicyResponse, MailProfileScope, MailSecurity, MailServerEndpoint, MailServerProfile, MailServerProfileCredentialsPayload, MailServerProfileListResponse, MailServerProfilePayload, MailSmtpTestPayload, MailTransportCredentialsPayload, MockMailboxMessage, MockMailboxMessageResponse } from "@govoplan/core-webui"; export type { MailPolicySourceStep as PolicySourceStep } from "@govoplan/core-webui"; export type MailAddressLookupCandidate = { contact_id: string; address_book_id: string; display_name: string; email?: string | null; email_label?: string | null; organization?: string | null; role_title?: string | null; tags: string[]; source_kind: string; source_ref?: string | null; source_revision?: string | null; provenance: Record; }; export type MailAddressLookupResponse = { available: boolean; candidates: MailAddressLookupCandidate[]; }; export type MailAddressWriteTarget = { address_book_id: string; address_book_label?: string | null; operation: string; allowed: boolean; reason: string; message: string; scope_type?: string | null; scope_id?: string | null; source_kind?: string | null; read_only: boolean; required_scopes: string[]; provenance: Record; }; export type MailAddressWriteTargetResponse = { available: boolean; targets: MailAddressWriteTarget[]; }; export type MailContactCreatePayload = { address_book_id: string; display_name?: string | null; email: string; }; export type MailContactCreateResponse = { contact_id: string; address_book_id: string; display_name: string; email?: string | null; source_kind: string; provenance: Record; }; export type MailMailboxAttachment = { filename?: string | null; content_type: string; size_bytes: number; }; export type MailMailboxMessageSummary = { uid: string; folder: string; subject?: string | null; from_header?: string | null; to_header?: string | null; cc_header?: string | null; bcc_header?: string | null; date?: string | null; message_id?: string | null; flags?: string[]; size_bytes?: number | null; body_preview?: string | null; attachment_count?: number; }; export type MailMailboxMessageDetail = MailMailboxMessageSummary & { body_text?: string | null; body_html?: string | null; headers?: Record; attachments?: MailMailboxAttachment[]; }; export type MailMailboxMessageListResponse = { profile_id: string; folder: string; host?: string | null; port?: number | null; security?: MailSecurity | string | null; total_count?: number; offset?: number; limit?: number; cursor?: string | null; next_cursor?: string | null; cursor_stable?: boolean; full?: boolean; from_cache?: boolean; refreshing?: boolean; indexed_at?: string | null; messages: MailMailboxMessageSummary[]; }; export type MailMailboxBootstrapResponse = { profile_id: string; folder: string; folders: MailImapFolderListResponse; messages: MailMailboxMessageListResponse; }; export type MailMailboxMessageResponse = { profile_id: string; folder: string; host?: string | null; port?: number | null; security?: MailSecurity | string | null; message: MailMailboxMessageDetail; }; export type MailMailboxProtocol = "imap" | "jmap"; export type MailMailboxChangesResponse = { profile_id: string; protocol: "jmap"; account_id: string; old_state: string; new_state: string; has_more_changes: boolean; created: string[]; updated: string[]; destroyed: string[]; }; export type MailSettingsDeltaResponse = { profiles: MailServerProfile[]; policy?: MailProfilePolicyResponse | null; changed_sections?: string[]; deleted: DeltaDeletedItem[]; watermark?: string | null; has_more: boolean; full: boolean; }; export type MailBounceSource = { id: string; tenant_id: string; profile_id: string; folder: string; imap_server_id?: string | null; imap_credential_id?: string | null; expected_imap_transport_revision: string; is_active: boolean; uidvalidity?: string | null; highest_processed_uid: number; last_scanned_at?: string | null; last_success_at?: string | null; last_error?: string | null; }; export type MailBounceObservation = { id: string; profile_id: string; folder: string; uid: string; original_message_id?: string | null; command_id?: string | null; recipient?: string | null; action: string; status_code?: string | null; diagnostic?: string | null; permanent: boolean; observed_at: string; matched: boolean; evidence: Record; }; export type MailBounceSourcePayload = { profile_id: string; folder: string; imap_server_id?: string | null; imap_credential_id?: string | null; is_active: boolean; }; export async function listMailBounceSources(settings: ApiSettings): Promise { const response = await apiFetch<{ sources: MailBounceSource[] }>(settings, "/api/v1/mail/bounce-sources"); return response.sources; } export async function saveMailBounceSource(settings: ApiSettings, payload: MailBounceSourcePayload): Promise { return apiFetch(settings, "/api/v1/mail/bounce-sources", { method: "POST", body: JSON.stringify(payload) }); } export async function removeMailBounceSource(settings: ApiSettings, sourceId: string): Promise { await apiFetch(settings, `/api/v1/mail/bounce-sources/${encodeURIComponent(sourceId)}`, { method: "DELETE" }); } export async function scanMailBounceSource(settings: ApiSettings, sourceId: string): Promise<{ sources: number; processed_messages: number; observations: number; failures: Array> }> { return apiFetch(settings, `/api/v1/mail/bounce-sources/${encodeURIComponent(sourceId)}/scan`, { method: "POST" }); } export async function listMailBounceObservations(settings: ApiSettings, limit = 100): Promise { const response = await apiFetch<{ observations: MailBounceObservation[] }>(settings, apiPath("/api/v1/mail/bounce-observations", { limit })); return response.observations; } export async function lookupMailAddresses(settings: ApiSettings, query: string, limit = 25): Promise { return apiFetch(settings, apiPath("/api/v1/mail/address-lookup", { query, limit })); } export async function listMailAddressWriteTargets(settings: ApiSettings): Promise { return apiFetch(settings, "/api/v1/mail/address-write-targets"); } export async function createMailAddressContact( settings: ApiSettings, payload: MailContactCreatePayload ): Promise { return apiFetch(settings, "/api/v1/mail/address-contacts", { method: "POST", body: JSON.stringify(payload) }); } export async function listMailServerProfiles(settings: ApiSettings, includeInactive = false, campaignId?: string): Promise { return apiGetList(settings, "/api/v1/mail/profiles", "profiles", { include_inactive: includeInactive ? true : undefined, campaign_id: campaignId }); } export async function fetchMailSettingsDelta( settings: ApiSettings, params: { scope_type?: MailProfileScope; scope_id?: string | null; include_inactive?: boolean; campaign_id?: string | null; since?: string | null; limit?: number; } = {} ): Promise { return apiFetch(settings, apiPath("/api/v1/mail/settings/delta", { scope_type: params.scope_type, scope_id: params.scope_id, include_inactive: params.include_inactive ? true : undefined, campaign_id: params.campaign_id, since: params.since, limit: params.limit })); } export async function createMailServerProfile(settings: ApiSettings, payload: MailServerProfilePayload): Promise { return apiFetch(settings, "/api/v1/mail/profiles", { method: "POST", body: JSON.stringify(payload) }); } export type MailServerProfileUpdatePayload = Partial & { clear_imap?: boolean }; export type MailServerEndpointPayload = { protocol: "smtp" | "imap" | "jmap" | "pop3"; name: string; config: Record; inherit_to_lower_scopes?: boolean | null; is_default?: boolean; is_active?: boolean; }; export type MailPop3ServerConfig = { host?: string | null; port?: number | null; security?: MailSecurity | string; timeout_seconds?: number; max_message_bytes?: number; max_batch_bytes?: number; preview_body_lines?: number; legacy_import_enabled?: boolean; allow_delete_after_import?: boolean; }; export type MailPop3ServerEndpoint = Omit & { protocol: "pop3"; config: MailPop3ServerConfig; }; export type MailPop3MessagePreview = { message_number: number; uidl: string; subject?: string | null; from_header?: string | null; to_header?: string | null; date?: string | null; message_id?: string | null; size_bytes: number; body_preview?: string | null; already_imported: boolean; }; export type MailPop3PreviewResponse = { profile_id: string; server_id: string; transport_revision: string; host: string; port: number; security: string; message_count: number; mailbox_size_bytes: number; delete_after_import_allowed: boolean; messages: MailPop3MessagePreview[]; }; export type MailPop3ImportRecord = { id: string; profile_id: string; pop3_server_id: string; transport_revision: string; provider_uidl: string; message_id?: string | null; subject?: string | null; from_header?: string | null; to_header?: string | null; date?: string | null; body_preview?: string | null; size_bytes: number; raw_sha256: string; status: string; imported_at: string; deletion_requested: boolean; deletion_status: string; deletion_attempted_at?: string | null; deletion_error?: string | null; }; export type MailPop3ImportResponse = { imports: MailPop3ImportRecord[]; duplicate_uidls: string[]; deletion_status: string; }; export type MailCredentialCreatePayload = { name: string; description?: string | null; credential_kind?: string; username?: string | null; password?: string | null; public_data?: Record; secret_data?: Record; inherit_to_lower_scopes?: boolean | null; allowed_modules?: string[]; allowed_server_refs?: string[]; is_default?: boolean; }; export type MailCredentialUpdatePayload = Partial & { is_active?: boolean; }; export async function updateMailServerProfile(settings: ApiSettings, profileId: string, payload: MailServerProfileUpdatePayload): Promise { return apiFetch(settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}`, { method: "PATCH", body: JSON.stringify(payload) }); } export async function deactivateMailServerProfile(settings: ApiSettings, profileId: string): Promise { return apiFetch(settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}`, { method: "DELETE" }); } export async function createMailServerEndpoint( settings: ApiSettings, profileId: string, payload: MailServerEndpointPayload ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers`, { method: "POST", body: JSON.stringify(payload) } ); } export async function updateMailServerEndpoint( settings: ApiSettings, profileId: string, serverId: string, payload: Partial> ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}`, { method: "PATCH", body: JSON.stringify(payload) } ); } export async function deactivateMailServerEndpoint( settings: ApiSettings, profileId: string, serverId: string ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}`, { method: "DELETE" } ); } export async function listAvailableMailCredentials( settings: ApiSettings, profileId: string, serverId: string, includeInactive = false ): Promise { return apiGetList( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}/available-credentials`, "credentials", { include_inactive: includeInactive ? true : undefined } ); } export async function createMailServerCredential( settings: ApiSettings, profileId: string, serverId: string, payload: MailCredentialCreatePayload ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}/credentials`, { method: "POST", body: JSON.stringify(payload) } ); } export async function bindMailServerCredential( settings: ApiSettings, profileId: string, serverId: string, credentialId: string, isDefault = false ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}/credential-bindings`, { method: "POST", body: JSON.stringify({ credential_id: credentialId, is_default: isDefault }) } ); } export async function updateMailServerCredential( settings: ApiSettings, profileId: string, serverId: string, credentialId: string, payload: MailCredentialUpdatePayload ): Promise { return apiFetch( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}/credentials/${encodeURIComponent(credentialId)}`, { method: "PATCH", body: JSON.stringify(payload) } ); } export async function unlinkMailServerCredential( settings: ApiSettings, profileId: string, serverId: string, credentialId: string, retireIfUnused = false ): Promise { await apiFetch( settings, apiPath( `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/servers/${encodeURIComponent(serverId)}/credentials/${encodeURIComponent(credentialId)}`, { retire_if_unused: retireIfUnused ? true : undefined } ), { method: "DELETE" } ); } export async function getMailProfilePolicy( settings: ApiSettings, scopeType: MailProfileScope, scopeId?: string | null, campaignId?: string | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/policies/${encodeURIComponent(scopeType)}`, { scope_id: scopeId, campaign_id: campaignId })); } export async function updateMailProfilePolicy( settings: ApiSettings, scopeType: MailProfileScope, policy: MailProfilePolicy, scopeId?: string | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/policies/${encodeURIComponent(scopeType)}`, { scope_id: scopeId }), { method: "PUT", body: JSON.stringify({ policy }) }); } export async function testMailProfileSmtp( settings: ApiSettings, profileId: string, serverId?: string | null, credentialId?: string | null, campaignId?: string | null ): Promise { return apiPost( settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/test-smtp`, { server_id: serverId, credential_id: credentialId, campaign_id: campaignId }) ); } export async function testMailProfileImap( settings: ApiSettings, profileId: string, serverId?: string | null, credentialId?: string | null, campaignId?: string | null ): Promise { return apiPost( settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/test-imap`, { server_id: serverId, credential_id: credentialId, campaign_id: campaignId }) ); } export async function testMailProfileJmap( settings: ApiSettings, profileId: string, serverId?: string | null, credentialId?: string | null, campaignId?: string | null ): Promise { return apiPost( settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/test-jmap`, { server_id: serverId, credential_id: credentialId, campaign_id: campaignId }) ); } export async function testMailProfilePop3( settings: ApiSettings, profileId: string, serverId: string, credentialId?: string | null ): Promise { return apiPost( settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/test-pop3`, { server_id: serverId, credential_id: credentialId }) ); } export async function previewMailProfilePop3( settings: ApiSettings, profileId: string, payload: { server_id: string; credential_id?: string | null; limit?: number } ): Promise { return apiPostJson( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/pop3/preview`, payload ); } export async function importMailProfilePop3( settings: ApiSettings, profileId: string, payload: { server_id: string; credential_id?: string | null; expected_transport_revision: string; uidls: string[]; delete_after_import?: boolean; } ): Promise { return apiPostJson( settings, `/api/v1/mail/profiles/${encodeURIComponent(profileId)}/pop3/import`, payload ); } export async function listMailPop3Imports( settings: ApiSettings, profileId?: string | null, limit = 100 ): Promise { const response = await apiFetch<{ imports: MailPop3ImportRecord[] }>( settings, apiPath("/api/v1/mail/pop3/imports", { profile_id: profileId, limit }) ); return response.imports; } export async function listMailProfileImapFolders( settings: ApiSettings, profileId: string, serverId?: string | null, credentialId?: string | null, campaignId?: string | null ): Promise { return apiPost( settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/list-imap-folders`, { server_id: serverId, credential_id: credentialId, campaign_id: campaignId }) ); } export async function listMailboxFolders(settings: ApiSettings, profileId: string, includeStatus = false, refresh = false, protocol: MailMailboxProtocol = "imap"): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/mailbox/folders`, { include_status: includeStatus ? true : undefined, refresh: refresh ? true : undefined, protocol })); } export async function bootstrapMailbox( settings: ApiSettings, profileId: string, folder = "INBOX", limit = 50, offset = 0, refresh = false, protocol: MailMailboxProtocol = "imap" ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/mailbox/bootstrap`, { folder, limit, offset, refresh: refresh ? true : undefined, protocol })); } export async function listMailboxMessages( settings: ApiSettings, profileId: string, folder = "INBOX", limit = 50, offset = 0, cursor?: string | null, refresh = false, protocol: MailMailboxProtocol = "imap", query?: string | null ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/mailbox/messages`, { folder, limit, offset, cursor, refresh: refresh ? true : undefined, protocol, q: query || undefined })); } export async function getMailboxMessage( settings: ApiSettings, profileId: string, folder: string, uid: string, protocol: MailMailboxProtocol = "imap" ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/mailbox/messages/${encodeURIComponent(uid)}`, { folder, protocol })); } export async function getMailboxChanges( settings: ApiSettings, profileId: string, sinceState: string, maxChanges = 500 ): Promise { return apiFetch(settings, apiPath(`/api/v1/mail/profiles/${encodeURIComponent(profileId)}/mailbox/changes`, { since_state: sinceState, max_changes: maxChanges })); } export async function testSmtpSettings( settings: ApiSettings, payload: MailSmtpTestPayload ): Promise { return apiPostJson(settings, "/api/v1/mail/test-smtp", payload); } export async function testImapSettings( settings: ApiSettings, payload: MailImapTestPayload ): Promise { return apiPostJson(settings, "/api/v1/mail/test-imap", payload); } export async function listImapFolders( settings: ApiSettings, payload: MailImapTestPayload ): Promise { return apiPostJson(settings, "/api/v1/mail/list-imap-folders", payload); } export type MockMailboxListResponse = { messages: MockMailboxMessage[]; }; export type MockMailboxFailureConfig = { fail_next_smtp?: boolean | null; fail_next_imap?: boolean | null; smtp_reject_recipients_containing?: string | null; }; export async function listMockMailboxMessages(settings: ApiSettings, kind?: string): Promise { return apiFetch(settings, apiPath("/api/v1/dev/mailbox/messages", { kind })); } export async function getMockMailboxMessage(settings: ApiSettings, id: string): Promise { return apiFetch(settings, `/api/v1/dev/mailbox/messages/${encodeURIComponent(id)}`); } export async function clearMockMailboxMessages(settings: ApiSettings): Promise<{ deleted_count: number }> { return apiFetch<{ deleted_count: number }>(settings, "/api/v1/dev/mailbox/messages", { method: "DELETE" }); } export async function updateMockMailboxFailures(settings: ApiSettings, payload: MockMailboxFailureConfig): Promise<{ config: MockMailboxFailureConfig }> { return apiFetch<{ config: MockMailboxFailureConfig }>(settings, "/api/v1/dev/mailbox/failures", { method: "POST", body: JSON.stringify(payload) }); }