Implement TODO stubs: webhook secret lookup, alert→incident wiring, catalog upsert/stage

- P3: getWebhookSecret() now queries DB; ingestAlert() creates/attaches incidents, auto-resolves on resolved status
- P4: stageUpdates() writes to staged_updates table; upsertService() with ON CONFLICT; getService/updateOwner implemented
This commit is contained in:
2026-03-01 03:18:05 +00:00
parent 2c112b2fb1
commit bdaa732ce1
2 changed files with 84 additions and 11 deletions

View File

@@ -133,17 +133,62 @@ function mapGrafanaSeverity(s: string | undefined): CanonicalAlert['severity'] {
}
async function getWebhookSecret(tenantSlug: string, provider: string): Promise<{ tenantId: string; secret: string } | null> {
// TODO: SELECT ws.secret, t.id FROM webhook_secrets ws JOIN tenants t ON ws.tenant_id = t.id WHERE t.slug = $1 AND ws.provider = $2
return null;
const { pool } = await import('../data/db.js');
const result = await pool.query(
`SELECT ws.secret, t.id as tenant_id
FROM webhook_secrets ws
JOIN tenants t ON ws.tenant_id = t.id
WHERE t.slug = $1 AND ws.provider = $2`,
[tenantSlug, provider],
);
if (!result.rows[0]) return null;
return { tenantId: result.rows[0].tenant_id, secret: result.rows[0].secret };
}
async function ingestAlert(tenantId: string, alert: CanonicalAlert): Promise<void> {
await withTenant(tenantId, async (client) => {
await client.query(
// Persist raw alert
const alertResult = await client.query(
`INSERT INTO alerts (tenant_id, source_provider, source_id, fingerprint, title, severity, status, service, environment, tags, raw_payload)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id`,
[tenantId, alert.sourceProvider, alert.sourceId, alert.fingerprint, alert.title, alert.severity, alert.status, alert.service, alert.environment, JSON.stringify(alert.tags), JSON.stringify(alert.rawPayload)],
);
// TODO: Feed into correlation engine
const alertId = alertResult.rows[0].id;
// Check for existing open incident with same fingerprint
const existing = await client.query(
`SELECT id, alert_count FROM incidents
WHERE fingerprint = $1 AND status IN ('open', 'acknowledged')
ORDER BY created_at DESC LIMIT 1`,
[alert.fingerprint],
);
if (existing.rows[0]) {
// Attach to existing incident
await client.query('UPDATE alerts SET incident_id = $1 WHERE id = $2', [existing.rows[0].id, alertId]);
await client.query(
`UPDATE incidents SET alert_count = alert_count + 1, last_alert_at = now() WHERE id = $1`,
[existing.rows[0].id],
);
} else if (alert.status === 'firing') {
// Create new incident
const incident = await client.query(
`INSERT INTO incidents (tenant_id, incident_key, fingerprint, service, title, severity, alert_count, first_alert_at, last_alert_at)
VALUES ($1, $2, $3, $4, $5, $6, 1, now(), now())
RETURNING id`,
[tenantId, `inc_${crypto.randomUUID().slice(0, 8)}`, alert.fingerprint, alert.service ?? 'unknown', alert.title, alert.severity],
);
await client.query('UPDATE alerts SET incident_id = $1 WHERE id = $2', [incident.rows[0].id, alertId]);
}
// Auto-resolve if alert status is resolved
if (alert.status === 'resolved') {
await client.query(
`UPDATE incidents SET status = 'resolved', resolved_at = now()
WHERE fingerprint = $1 AND status IN ('open', 'acknowledged')`,
[alert.fingerprint],
);
}
});
}