Rene Fichtmueller 5554c1a53e feat: BGP Hijack Alerting + Webhooks (Feature 1)
- Deterministic Classification: MOAS/HIJACK/LEAK type detection
- Severity scoring: CRITICAL/HIGH/MEDIUM/LOW based on prefix length
- Optional Ollama enrichment (qwen2.5:3b) for CRITICAL only (5s timeout)
- PostgreSQL backend: hijack_events, webhook_subscriptions, webhook_deliveries
- HMAC-SHA256 webhook signing with exponential backoff retry
- Retry scheduler: node-cron job every 5 minutes
- 6 API endpoints: POST/GET/DELETE webhooks, test delivery, list/resolve hijacks
- 22 comprehensive tests (80%+ coverage)
- Zero external API costs (deterministic + local Ollama only)
2026-04-29 07:45:15 +02:00

91 lines
2.2 KiB
TypeScript

import type { Pool, QueryResult } from 'pg'
import type { PDFReport } from './types'
export class PDFExportDatabaseClient {
constructor(private pool: Pool) {}
async recordPDFGeneration(
resource_value: string,
format: string,
file_hash: string,
file_size_bytes: number,
metadata: Record<string, unknown> = {}
): Promise<PDFReport> {
const query = `
INSERT INTO pdf_reports (
resource_type,
resource_value,
format,
generated_at,
expires_at,
file_hash,
file_size_bytes,
metadata
) VALUES ($1, $2, $3, NOW(), NOW() + INTERVAL '5 minutes', $4, $5, $6)
ON CONFLICT (resource_value, format, DATE(generated_at))
DO UPDATE SET
file_hash = EXCLUDED.file_hash,
file_size_bytes = EXCLUDED.file_size_bytes,
expires_at = NOW() + INTERVAL '5 minutes'
RETURNING *
`
const result: QueryResult<PDFReport> = await this.pool.query(query, [
'asn',
resource_value,
format,
file_hash,
file_size_bytes,
JSON.stringify(metadata),
])
return result.rows[0]
}
async getPDFByHash(file_hash: string): Promise<PDFReport | null> {
const query = `
SELECT * FROM pdf_reports
WHERE file_hash = $1 AND expires_at > NOW()
LIMIT 1
`
const result: QueryResult<PDFReport> = await this.pool.query(query, [
file_hash,
])
return result.rows[0] || null
}
async cleanupExpiredPDFs(): Promise<number> {
const query = `
DELETE FROM pdf_reports
WHERE expires_at <= NOW()
`
const result: QueryResult = await this.pool.query(query)
return result.rowCount || 0
}
async getPDFStats(): Promise<{
total_records: number
total_size_bytes: number
expired_records: number
}> {
const query = `
SELECT
COUNT(*) as total_records,
COALESCE(SUM(file_size_bytes), 0) as total_size_bytes,
COUNT(CASE WHEN expires_at <= NOW() THEN 1 END) as expired_records
FROM pdf_reports
`
const result: QueryResult<{
total_records: number
total_size_bytes: number
expired_records: number
}> = await this.pool.query(query)
return result.rows[0]
}
}