wulf-pulse/app/api/sync/classifications/route.ts
lorentz fcdec8e38b feat: Mimecast multi-tenant held mail viewer
- migration 062: mimecast_tenants table (company_id, client_id/secret, account_code)
- Seed Wulf (CUSA13A95) + Seubert (CUSA96A181) tenants
- MimecastClient.getHeldMessages(): full pagination via meta.pagination.next cursor
  (API always returns 10/page regardless of pageSize param, totalCount in meta)
- getMimecastClientForTenant() factory for per-tenant instantiation
- GET /api/mimecast/held?tenantId=&recipient= — fetches all tenants in parallel,
  merges + sorts by date, returns per-tenant counts + combined messages[]
- Held Mail tab on /admin/sync/mimecast (on-demand load, recipient filter,
  tenant badges, policy filter dropdown, DMARC/impersonation highlighted red)
2026-03-31 22:38:22 -04:00

119 lines
3.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { postgresClient } from '@/lib/services/postgres-client';
import { getAutotaskClient } from '@/lib/services/autotask-factory';
export async function POST(request: NextRequest) {
try {
console.log('Starting classification icons sync...');
// Fetch classification picklist from Companies field info
const autotaskClient = getAutotaskClient();
const fields = await autotaskClient.getFieldInfo('Companies');
const classificationField = fields.find((f: any) => f.name === 'classification');
if (!classificationField || !classificationField.picklistValues?.length) {
return NextResponse.json(
{ error: 'No classification picklist found in Companies field info' },
{ status: 404 }
);
}
const classifications = classificationField.picklistValues;
console.log(`Fetched ${classifications.length} classification values from Autotask`);
let inserted = 0;
let updated = 0;
// Upsert each classification
for (const classification of classifications) {
const result = await postgresClient.query(
`INSERT INTO company_classifications (
classification_id,
name,
description,
is_active,
is_system,
updated_at,
synced_at
) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (classification_id)
DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
is_active = EXCLUDED.is_active,
is_system = EXCLUDED.is_system,
updated_at = CURRENT_TIMESTAMP,
synced_at = CURRENT_TIMESTAMP
RETURNING (xmax = 0) AS inserted`,
[
parseInt(String(classification.value)),
classification.label,
null,
classification.isActive !== false,
classification.isSystem || false
]
);
if (result.rows[0]?.inserted) {
inserted++;
} else {
updated++;
}
}
console.log(`Classification sync complete: ${inserted} inserted, ${updated} updated`);
return NextResponse.json({
success: true,
total: classifications.length,
inserted,
updated,
message: `Synced ${classifications.length} classifications from Companies field info`
});
} catch (error) {
console.error('Error syncing classifications:', error);
return NextResponse.json(
{
error: 'Failed to sync classifications',
details: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
);
}
}
export async function GET(request: NextRequest) {
try {
const result = await postgresClient.query(
`SELECT
classification_id,
name,
description,
is_active,
is_system,
synced_at
FROM company_classifications
WHERE is_active = true
ORDER BY name`
);
return NextResponse.json({
classifications: result.rows.map(row => ({
id: row.classification_id,
name: row.name,
description: row.description,
isActive: row.is_active,
isSystem: row.is_system,
syncedAt: row.synced_at
}))
});
} catch (error) {
console.error('Error fetching classifications:', error);
return NextResponse.json(
{ error: 'Failed to fetch classifications' },
{ status: 500 }
);
}
}