78 lines
2.2 KiB
TypeScript
78 lines
2.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Webhook Source IPs API
|
||
|
|
* View unique source IPs from webhook logs for whitelisting
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { NextRequest, NextResponse } from 'next/server';
|
||
|
|
import { postgresClient } from '@/lib/services/postgres-client';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GET /api/webhooks/ips
|
||
|
|
* Get unique source IPs from webhook logs
|
||
|
|
*/
|
||
|
|
export async function GET(request: NextRequest) {
|
||
|
|
try {
|
||
|
|
const searchParams = request.nextUrl.searchParams;
|
||
|
|
const hours = parseInt(searchParams.get('hours') || '168'); // Default 7 days
|
||
|
|
const entityType = searchParams.get('entityType') || undefined;
|
||
|
|
|
||
|
|
// Build query to get unique IPs with counts
|
||
|
|
let query = `
|
||
|
|
SELECT
|
||
|
|
source_ip,
|
||
|
|
user_agent,
|
||
|
|
COUNT(*) as request_count,
|
||
|
|
MIN(received_at) as first_seen,
|
||
|
|
MAX(received_at) as last_seen,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'processed') as successful_count,
|
||
|
|
COUNT(*) FILTER (WHERE status = 'failed') as failed_count,
|
||
|
|
array_agg(DISTINCT entity_type) as entity_types
|
||
|
|
FROM webhook_logs
|
||
|
|
WHERE received_at >= NOW() - INTERVAL '${hours} hours'
|
||
|
|
AND source_ip IS NOT NULL
|
||
|
|
`;
|
||
|
|
|
||
|
|
const params: any[] = [];
|
||
|
|
|
||
|
|
if (entityType) {
|
||
|
|
query += ` AND entity_type = $1`;
|
||
|
|
params.push(entityType);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += `
|
||
|
|
GROUP BY source_ip, user_agent
|
||
|
|
ORDER BY request_count DESC
|
||
|
|
`;
|
||
|
|
|
||
|
|
const result = await postgresClient.query(query, params);
|
||
|
|
|
||
|
|
// Format the results
|
||
|
|
const ips = result.rows.map((row: any) => ({
|
||
|
|
ip: row.source_ip,
|
||
|
|
userAgent: row.user_agent,
|
||
|
|
requestCount: parseInt(row.request_count),
|
||
|
|
successfulCount: parseInt(row.successful_count),
|
||
|
|
failedCount: parseInt(row.failed_count),
|
||
|
|
firstSeen: row.first_seen,
|
||
|
|
lastSeen: row.last_seen,
|
||
|
|
entityTypes: row.entity_types,
|
||
|
|
}));
|
||
|
|
|
||
|
|
return NextResponse.json({
|
||
|
|
success: true,
|
||
|
|
ips,
|
||
|
|
count: ips.length,
|
||
|
|
period: `${hours} hours`,
|
||
|
|
});
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||
|
|
console.error('[WEBHOOK IPS API] Error fetching IPs:', errorMessage);
|
||
|
|
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: 'Failed to fetch webhook IPs', details: errorMessage },
|
||
|
|
{ status: 500 }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|