feat(drift): add normalizer, chunk assembly, daily digest, Slack interactions, analytics
Some checks failed
CI — P2 Drift (Go + Node) / agent (push) Successful in 49s
CI — P2 Drift (Go + Node) / saas (push) Successful in 29s
CI — P2 Drift (Go + Node) / build-push (push) Failing after 48s

- Canonical schema normalizer: cross-provider resource type mapping
- Chunked report reassembly via Redis (10min TTL, out-of-order safe)
- Daily drift digest worker with Slack Block Kit summary
- Slack interactive handler: remediate + accept drift actions
- Analytics API: drift trends and health summary
- 005_drift_features.sql migration (remediations, acceptances, indexes)
This commit is contained in:
Max
2026-03-03 06:56:44 +00:00
parent ef3d00f124
commit f133ca8ff6
9 changed files with 810 additions and 41 deletions

View File

@@ -0,0 +1,39 @@
-- 005: Canonical resources, remediations, drift acceptances, analytics indexes
-- Add canonical_resources JSONB column to drift_reports
ALTER TABLE drift_reports ADD COLUMN IF NOT EXISTS canonical_resources JSONB;
-- Remediations (from Slack interactive actions)
CREATE TABLE IF NOT EXISTS remediations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
stack_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed', 'failed', 'cancelled')),
requested_by TEXT NOT NULL,
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_remediations_tenant_stack ON remediations(tenant_id, stack_name);
ALTER TABLE remediations ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_iso_remediations ON remediations
USING (tenant_id::text = current_setting('app.tenant_id', true));
-- Drift acceptances (from Slack interactive actions)
CREATE TABLE IF NOT EXISTS drift_acceptances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
report_id UUID NOT NULL REFERENCES drift_reports(id) ON DELETE CASCADE,
accepted_by TEXT NOT NULL,
accepted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
reason TEXT
);
CREATE INDEX IF NOT EXISTS idx_drift_acceptances_tenant ON drift_acceptances(tenant_id, report_id);
ALTER TABLE drift_acceptances ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_iso_drift_acceptances ON drift_acceptances
USING (tenant_id::text = current_setting('app.tenant_id', true));
-- Indexes for time-series analytics queries
CREATE INDEX IF NOT EXISTS idx_drift_reports_tenant_scanned ON drift_reports(tenant_id, scanned_at DESC);
CREATE INDEX IF NOT EXISTS idx_drift_reports_score_time ON drift_reports(tenant_id, stack_name, scanned_at DESC, drift_score);