-
Notifications
You must be signed in to change notification settings - Fork 68
feat: incident.io connector #287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
afa016c
feat: add incident.io connector backend
beng360 ea966b1
feat: add incident.io connector frontend
beng360 ab5f81d
feat: add incident.io skill definition for RCA agent
beng360 aab5c6e
fix: harden incident.io connector end-to-end
beng360 6776ec9
fix: resolve CodeQL and code quality issues on incident.io connector
beng360 1dbaa28
fix: remaining CodeRabbit issues — fuzzy match and API filter params
beng360 d0d501c
remove unsued logos
OlivierTrudeau ac5c660
Fix damian wrong ordering of creation of tables
OlivierTrudeau 1f81ad4
fix: resolve remaining CodeRabbit review comments on incident.io conn…
beng360 7d3161c
fix: address Olivier's review — remove Next.js webhook proxy, add mis…
beng360 afc897d
refactor: use ConnectorRegistry for source display names instead of h…
beng360 8a419bc
Merge remote-tracking branch 'origin/main' into feat/incidentio-conne…
beng360 dc7b43e
fix logo
OlivierTrudeau a96cc94
Remove prefix matching from load_skill fuzzy match
beng360 751297c
Address PR #287 review: webhook signing, pagination, UX fixes
beng360 9dd0e5a
Fix CodeQL and SonarCloud security findings
beng360 7405379
Harden incidentio routes against log injection and info exposure
beng360 51cb51c
Fix SonarCloud CSRF hotspots and reduce code duplication
beng360 ffa7e3b
Extract useConnectorAuth hook to reduce auth page duplication
beng360 7c59994
Fix infinite re-render loop in useConnectorAuth
beng360 0f2dd90
Fix incident.io field extraction and org-scoped preference storage
beng360 e116c5d
Remove user-controlled data from log statements in store_user_preference
beng360 e786673
Merge remote-tracking branch 'origin/main' into feat/incidentio-conne…
beng360 242a086
Fix incident.io webhook: add to open prefixes, match v2 event types, …
beng360 4f9b1d1
Clarify webhook signing secret description in incident.io setup
beng360 9a11b7f
Show webhook signing secret saved state in incident.io setup UI
beng360 3bbbadf
Fix all 23 SonarCloud quality gate issues on PR #287
beng360 779d74e
Fix 2 SonarCloud reliability bugs (S7727, S7502) to pass quality gate
beng360 b16a15a
Merge remote-tracking branch 'origin/main' into feat/incidentio-conne…
beng360 f803698
Merge commit '0bdf89b09245ba2ead91b8c69616a85d3226105c' into feat/inc…
OlivierTrudeau 909f7ac
Fix stale cache showing false "connected" state and use set_rls_conte…
beng360 ff7d167
Merge remote-tracking branch 'origin/main' into feat/incidentio-conne…
beng360 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import { NextRequest, NextResponse } from 'next/server'; | ||
| import { getAuthenticatedUser } from '@/lib/auth-helper'; | ||
|
|
||
| const API_BASE_URL = process.env.BACKEND_URL; | ||
| const FETCH_TIMEOUT_MS = 15000; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
| const { searchParams } = new URL(request.url); | ||
| const limit = searchParams.get('limit') || '50'; | ||
| const offset = searchParams.get('offset') || '0'; | ||
| const severity = searchParams.get('severity'); | ||
|
|
||
| const params = new URLSearchParams({ limit, offset }); | ||
| if (severity) params.append('severity', severity); | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/alerts?${params}`, { | ||
| method: 'GET', | ||
| headers: authHeaders, | ||
| credentials: 'include', | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/alerts] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to get incidents' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Request timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/alerts] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to get incidents' }, { status: 500 }); | ||
| } | ||
| } |
47 changes: 47 additions & 0 deletions
47
client/src/app/api/incident-io/alerts/webhook-url/route.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { NextResponse } from 'next/server'; | ||
| import { getAuthenticatedUser } from '@/lib/auth-helper'; | ||
|
|
||
| const API_BASE_URL = process.env.BACKEND_URL; | ||
| const FETCH_TIMEOUT_MS = 15000; | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/alerts/webhook-url`, { | ||
| method: 'GET', | ||
| headers: authHeaders, | ||
| credentials: 'include', | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/alerts/webhook-url] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to get webhook URL' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Request timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/alerts/webhook-url] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to get webhook URL' }, { status: 500 }); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { NextRequest, NextResponse } from 'next/server'; | ||
| import { getAuthenticatedUser } from '@/lib/auth-helper'; | ||
|
|
||
| const API_BASE_URL = process.env.BACKEND_URL; | ||
| const FETCH_TIMEOUT_MS = 15000; | ||
|
|
||
| export async function POST(request: NextRequest) { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
| const payload = await request.json(); | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/connect`, { | ||
| method: 'POST', | ||
| headers: { | ||
| ...authHeaders, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(payload), | ||
| credentials: 'include', | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/connect] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to connect to incident.io' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Connection timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/connect] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to connect to incident.io' }, { status: 500 }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { NextResponse } from 'next/server'; | ||
| import { getAuthenticatedUser } from '@/lib/auth-helper'; | ||
|
|
||
| const API_BASE_URL = process.env.BACKEND_URL; | ||
| const FETCH_TIMEOUT_MS = 15000; | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/rca-settings`, { | ||
| method: 'GET', | ||
| headers: authHeaders, | ||
| credentials: 'include', | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/rca-settings] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to get RCA settings' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Request timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/rca-settings] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to get RCA settings' }, { status: 500 }); | ||
| } | ||
| } | ||
|
|
||
| export async function PUT(request: Request) { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
| const body = await request.json(); | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/rca-settings`, { | ||
| method: 'PUT', | ||
| headers: { | ||
| ...authHeaders, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| credentials: 'include', | ||
| body: JSON.stringify(body), | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/rca-settings] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to update RCA settings' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Request timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/rca-settings] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to update RCA settings' }, { status: 500 }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { NextResponse } from 'next/server'; | ||
| import { getAuthenticatedUser } from '@/lib/auth-helper'; | ||
|
|
||
| const API_BASE_URL = process.env.BACKEND_URL; | ||
| const FETCH_TIMEOUT_MS = 15000; | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const authResult = await getAuthenticatedUser(); | ||
|
|
||
| if (authResult instanceof NextResponse) { | ||
| return authResult; | ||
| } | ||
|
|
||
| const { headers: authHeaders } = authResult; | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); | ||
|
|
||
| let response: Response; | ||
| try { | ||
| response = await fetch(`${API_BASE_URL}/incidentio/status`, { | ||
| method: 'GET', | ||
| headers: authHeaders, | ||
| credentials: 'include', | ||
| signal: controller.signal, | ||
| }); | ||
| } finally { | ||
| clearTimeout(timeoutId); | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| const text = await response.text(); | ||
| console.error('[api/incident-io/status] Backend error:', text); | ||
| return NextResponse.json({ error: 'Failed to get incident.io status' }, { status: response.status }); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return NextResponse.json(data); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.name === 'AbortError') { | ||
| return NextResponse.json({ error: 'Request timeout' }, { status: 504 }); | ||
| } | ||
| console.error('[api/incident-io/status] Error:', error instanceof Error ? error.message : 'Unknown error'); | ||
| return NextResponse.json({ error: 'Failed to get incident.io status' }, { status: 500 }); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.