diff --git a/docker-compose.yml b/docker-compose.yml index d29e7b1..a299122 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -107,6 +107,7 @@ services: MIMECAST_CLIENT_ID: ${MIMECAST_CLIENT_ID} MIMECAST_CLIENT_SECRET: ${MIMECAST_CLIENT_SECRET} MIMECAST_BASE_URL: ${MIMECAST_BASE_URL:-https://api.services.mimecast.com} + MIMECAST_ACCOUNT_CODE: ${MIMECAST_ACCOUNT_CODE} # IT Glue Configuration ITGLUE_API_KEY: ${ITGLUE_API_KEY} diff --git a/lib/services/mimecast-client.ts b/lib/services/mimecast-client.ts index 06b8e31..c55c1bb 100644 --- a/lib/services/mimecast-client.ts +++ b/lib/services/mimecast-client.ts @@ -15,6 +15,7 @@ export interface MimecastConfig { clientId: string; clientSecret: string; baseUrl?: string; + accountCode?: string; } interface TokenResponse { @@ -82,12 +83,24 @@ export class MimecastClient { private readonly clientId: string; private readonly clientSecret: string; private readonly baseUrl: string; + private readonly accountCode: string; private tokenCache: TokenCache | null = null; constructor(config: MimecastConfig) { this.clientId = config.clientId; this.clientSecret = config.clientSecret; this.baseUrl = (config.baseUrl ?? 'https://api.services.mimecast.com').replace(/\/$/, ''); + this.accountCode = config.accountCode ?? ''; + } + + /** + * Format a Date to the Mimecast-required format: yyyy-MM-ddTHH:mm:ss+0000 + * Note: NO milliseconds — the API rejects .SSS variants + */ + static formatDate(d: Date): string { + const pad = (n: number) => String(n).padStart(2, '0'); + return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` + + `T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}+0000`; } // ── Auth ───────────────────────────────────────────────────────────────── @@ -135,13 +148,18 @@ export class MimecastClient { url = `${url}?${new URLSearchParams(params).toString()}`; } + const headers: Record = { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }; + if (this.accountCode) { + headers['x-mc-account'] = this.accountCode; + } + const res = await fetch(url, { method, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - Accept: 'application/json', - }, + headers, body: body ? JSON.stringify(body) : undefined, }); @@ -160,15 +178,19 @@ export class MimecastClient { * Response: { data: [{ trackedEmails: [...], pageToken: string }] } */ async getMessageLogs(options: { - from: string; // ISO datetime string - to: string; + from: Date; + to: Date; cursor?: string; pageSize?: number; + senderFilter?: string; }): Promise> { const reqData: Record = { - from: options.from, - to: options.to, - pageSize: options.pageSize ?? 500, + accountCode: this.accountCode, + advancedTrackAndTraceOptions: { + from: options.senderFilter ?? '', + }, + start: MimecastClient.formatDate(options.from), + end: MimecastClient.formatDate(options.to), }; if (options.cursor) reqData.pageToken = options.cursor; @@ -380,7 +402,7 @@ export class MimecastClient { */ async testConnection(): Promise<{ ok: boolean; accountName?: string; packageName?: string; error?: string }> { try { - const data = await this.request('POST', '/api/account/get-account', { data: [{}] }); + const data = await this.request('POST', '/api/account/get-account', { data: [{ accountCode: this.accountCode }] }); const account = data.data?.[0] ?? {}; return { ok: true, @@ -406,6 +428,7 @@ export function getMimecastClient(): MimecastClient { clientId, clientSecret, baseUrl: process.env.MIMECAST_BASE_URL ?? 'https://api.services.mimecast.com', + accountCode: process.env.MIMECAST_ACCOUNT_CODE ?? '', }); } return _client; diff --git a/lib/services/mimecast-sync-service.ts b/lib/services/mimecast-sync-service.ts index aae47f5..977127b 100644 --- a/lib/services/mimecast-sync-service.ts +++ b/lib/services/mimecast-sync-service.ts @@ -196,12 +196,9 @@ export async function syncMimecastMessages( let total = 0; let cursor: string | null = null; - const from = fromDate.toISOString(); - const to = toDate.toISOString(); - do { try { - const result = await client.getMessageLogs({ from, to, cursor: cursor ?? undefined, pageSize: 500 }); + const result = await client.getMessageLogs({ from: fromDate, to: toDate, cursor: cursor ?? undefined, pageSize: 500 }); if (result.items.length > 0) { total += await upsertMessages(result.items); }