Implements comprehensive IP logging for webhook requests to enable IP whitelisting and security monitoring. Features: - Capture source IP from webhook requests (x-forwarded-for, x-real-ip) - Capture user agent for identification - Store in webhook_logs table - New API endpoint: GET /api/webhooks/ips - View unique IPs with request counts and statistics - Identify Autotask IPs for whitelisting Database Changes: - Added source_ip column (VARCHAR 45) to webhook_logs - Added user_agent column (TEXT) to webhook_logs - Added index on source_ip for efficient queries - Migration 005 for existing installations API Endpoints: - GET /api/webhooks/ips?hours=168&entityType=Tickets Returns unique IPs with: * Request counts (total, successful, failed) * First/last seen timestamps * Entity types accessed * User agent strings Use Cases: 1. Identify Autotask webhook IPs 2. Configure IP whitelist in nginx/Pangolin/Cloudflare 3. Monitor for unauthorized webhook attempts 4. Audit webhook sources 5. Detect IP changes from Autotask Security Benefits: - Enable IP whitelisting for webhook endpoint - Block unauthorized webhook attempts - Monitor for suspicious activity - Audit trail of webhook sources Documentation: - Complete IP whitelisting guide (WEBHOOK_IP_WHITELISTING.md) - Configuration examples for nginx, Pangolin, Cloudflare - Monitoring queries and best practices - Troubleshooting guide Files Modified: - migrations/004_webhook_support.sql - Added IP columns - migrations/005_add_webhook_ip_logging.sql - Migration for existing installs - lib/types/webhook.ts - Added IP fields to WebhookLog - lib/services/webhook-service.ts - Capture and log IPs - app/api/webhooks/autotask/route.ts - Extract IP from headers - app/api/webhooks/ips/route.ts - New IP viewing endpoint - docs/WEBHOOK_IP_WHITELISTING.md - Complete guide Next Steps: 1. Run migration (004 for new, 005 for existing) 2. Deploy updated code 3. Receive webhooks from Autotask 4. View IPs via /api/webhooks/ips 5. Configure IP whitelist in proxy/tunnel
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
/**
|
|
* Autotask Webhook Receiver Endpoint
|
|
* Receives and processes real-time webhook events from Autotask
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { webhookService } from '@/lib/services/webhook-service';
|
|
import { AutotaskWebhookPayload } from '@/lib/types/webhook';
|
|
|
|
/**
|
|
* POST /api/webhooks/autotask
|
|
* Receives webhook events from Autotask
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
// Extract source IP and user agent for logging
|
|
const sourceIp = request.headers.get('x-forwarded-for')?.split(',')[0].trim()
|
|
|| request.headers.get('x-real-ip')
|
|
|| 'unknown';
|
|
const userAgent = request.headers.get('user-agent') || 'unknown';
|
|
|
|
// Parse webhook payload
|
|
const payload: AutotaskWebhookPayload = await request.json();
|
|
|
|
console.log(`[WEBHOOK API] Received ${payload.eventType} event for ${payload.entityType} #${payload.entityId} from IP: ${sourceIp}`);
|
|
|
|
// Validate required fields
|
|
if (!payload.eventId || !payload.eventType || !payload.entityType || !payload.entityId) {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid webhook payload: missing required fields' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Process the webhook asynchronously
|
|
// Note: We return 200 immediately to Autotask, then process in background
|
|
// This prevents timeouts for slow processing
|
|
const result = await webhookService.processWebhook(payload, sourceIp, userAgent);
|
|
|
|
if (result.success) {
|
|
return NextResponse.json({
|
|
success: true,
|
|
eventId: result.eventId,
|
|
action: result.action,
|
|
processingTime: result.processingTime,
|
|
});
|
|
} else {
|
|
// Even if processing failed, we return 200 to Autotask
|
|
// The failure is logged in webhook_logs table
|
|
console.error(`[WEBHOOK API] Processing failed: ${result.error}`);
|
|
return NextResponse.json({
|
|
success: false,
|
|
eventId: result.eventId,
|
|
error: result.error,
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
console.error('[WEBHOOK API] Error processing webhook:', errorMessage);
|
|
|
|
// Return 500 for unexpected errors
|
|
return NextResponse.json(
|
|
{ error: 'Internal server error', details: errorMessage },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/webhooks/autotask
|
|
* Health check endpoint for webhook receiver
|
|
*/
|
|
export async function GET() {
|
|
return NextResponse.json({
|
|
status: 'active',
|
|
endpoint: '/api/webhooks/autotask',
|
|
message: 'Autotask webhook receiver is ready',
|
|
});
|
|
}
|