-
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 30 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
Some comments aren't visible on the classic Files Changed page.
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,6 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| return forwardRequest(request, 'GET', '/incidentio/alerts', 'incident-io/alerts'); | ||
| } |
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,6 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| return forwardRequest(request, 'GET', '/incidentio/alerts/webhook-url', 'incident-io/webhook-url'); | ||
| } |
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,6 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function POST(request: NextRequest) { | ||
| return forwardRequest(request, 'POST', '/incidentio/connect', 'incident-io/connect'); | ||
| } |
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,10 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| return forwardRequest(request, 'GET', '/incidentio/rca-settings', 'incident-io/rca-settings'); | ||
| } | ||
|
|
||
| export async function PUT(request: NextRequest) { | ||
| return forwardRequest(request, 'PUT', '/incidentio/rca-settings', 'incident-io/rca-settings'); | ||
| } |
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,6 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| return forwardRequest(request, 'GET', '/incidentio/status', 'incident-io/status'); | ||
| } |
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,6 @@ | ||
| import { NextRequest } from 'next/server'; | ||
| import { forwardRequest } from '@/lib/backend-proxy'; | ||
|
|
||
| export async function PUT(request: NextRequest) { | ||
| return forwardRequest(request, 'PUT', '/incidentio/webhook-secret', 'incident-io/webhook-secret'); | ||
| } |
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,170 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import { useToast } from "@/hooks/use-toast"; | ||
| import { useConnectorAuth } from "@/hooks/use-connector-auth"; | ||
| import { incidentIoService } from "@/lib/services/incident-io"; | ||
| import { Button } from "@/components/ui/button"; | ||
| import { Input } from "@/components/ui/input"; | ||
| import { Label } from "@/components/ui/label"; | ||
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | ||
| import { Loader2 } from "lucide-react"; | ||
| import { getUserFriendlyError } from "@/lib/utils"; | ||
| import { IncidentIoWebhookStep } from "@/components/incident-io/IncidentIoWebhookStep"; | ||
| import ConnectorAuthGuard from "@/components/connectors/ConnectorAuthGuard"; | ||
| import Image from "next/image"; | ||
|
|
||
| export default function IncidentIoAuthPage() { | ||
| const { toast } = useToast(); | ||
| const [apiKey, setApiKey] = useState(""); | ||
| const [loading, setLoading] = useState(false); | ||
|
|
||
| const { | ||
| isConnected, | ||
| isCheckingStatus, | ||
| updateLocalState, | ||
| disconnect, | ||
| } = useConnectorAuth({ | ||
| cacheKey: "incident_io_connection_status", | ||
| storageKey: "isIncidentIoConnected", | ||
| fetchStatus: () => incidentIoService.getStatus(), | ||
| disconnectPath: "/api/connected-accounts/incidentio", | ||
| }); | ||
|
|
||
| const handleConnect = async (event: React.FormEvent<HTMLFormElement>) => { | ||
| event.preventDefault(); | ||
| setLoading(true); | ||
|
|
||
| try { | ||
| const result = await incidentIoService.connect({ apiKey }); | ||
| updateLocalState(result); | ||
| toast({ title: "Success", description: "incident.io connected successfully!" }); | ||
| } catch (err: any) { | ||
| console.error("incident.io connection failed", err); | ||
| toast({ | ||
| title: "Failed to connect to incident.io", | ||
| description: getUserFriendlyError(err), | ||
| variant: "destructive", | ||
| }); | ||
| } finally { | ||
| setLoading(false); | ||
| setApiKey(""); | ||
| } | ||
| }; | ||
|
|
||
| const handleDisconnect = async () => { | ||
| setLoading(true); | ||
| try { | ||
| await disconnect(); | ||
| toast({ title: "Success", description: "incident.io disconnected successfully" }); | ||
| } catch (err: any) { | ||
| console.error("incident.io disconnect failed", err); | ||
| toast({ | ||
| title: "Failed to disconnect incident.io", | ||
| description: getUserFriendlyError(err), | ||
| variant: "destructive", | ||
| }); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| if (isCheckingStatus) { | ||
| return ( | ||
| <ConnectorAuthGuard connectorName="incident.io"> | ||
| <div className="container mx-auto py-8 px-4 max-w-2xl"> | ||
| <Card> | ||
| <CardContent className="flex items-center justify-center py-12"> | ||
| <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
| </ConnectorAuthGuard> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <ConnectorAuthGuard connectorName="incident.io"> | ||
| <div className="container mx-auto py-8 px-4 max-w-2xl"> | ||
| <div className="flex items-center gap-4 mb-6"> | ||
| <div className="flex items-center justify-center h-12 w-12 rounded-lg bg-white dark:bg-white p-1.5"> | ||
| <Image src="/incidentio.svg" alt="incident.io" width={40} height={40} /> | ||
| </div> | ||
| <div> | ||
| <h1 className="text-3xl font-bold">incident.io</h1> | ||
| <p className="text-muted-foreground mt-0.5"> | ||
| Incident lifecycle tracking and automated RCA | ||
| </p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex items-center justify-center mb-8"> | ||
| <div className="flex items-center"> | ||
| <div className={`flex items-center justify-center w-10 h-10 rounded-full font-bold ${isConnected ? 'bg-gray-200 text-gray-600' : 'text-white'}`} style={isConnected ? undefined : { backgroundColor: '#F04438' }}> | ||
| 1 | ||
| </div> | ||
| <div className="w-24 h-1" style={{ backgroundColor: isConnected ? '#F04438' : '#e5e7eb' }}></div> | ||
| <div className={`flex items-center justify-center w-10 h-10 rounded-full font-bold ${isConnected ? 'text-white' : 'bg-gray-200 text-gray-600'}`} style={isConnected ? { backgroundColor: '#F04438' } : undefined}> | ||
| 2 | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex items-center justify-center mb-6 text-sm font-medium"> | ||
| <span className={isConnected ? 'text-muted-foreground' : 'text-foreground'} style={isConnected ? undefined : { color: '#F04438' }}> | ||
| Connect | ||
| </span> | ||
| <span className="mx-4 text-muted-foreground">→</span> | ||
| <span className={isConnected ? 'text-foreground' : 'text-muted-foreground'} style={isConnected ? { color: '#F04438' } : undefined}> | ||
| Configure Webhook | ||
| </span> | ||
| </div> | ||
|
|
||
| {isConnected ? ( | ||
| <IncidentIoWebhookStep | ||
| onDisconnect={handleDisconnect} | ||
| loading={loading} | ||
| /> | ||
| ) : ( | ||
| <Card> | ||
| <CardHeader> | ||
| <CardTitle>Connect to incident.io</CardTitle> | ||
| <CardDescription> | ||
| Create an API key at <strong>Settings → API keys</strong> in incident.io, then paste it below. | ||
| </CardDescription> | ||
| </CardHeader> | ||
| <CardContent> | ||
| <form className="space-y-4" onSubmit={handleConnect}> | ||
| <div className="space-y-2"> | ||
| <Label htmlFor="apiKey">API Key</Label> | ||
| <Input | ||
| id="apiKey" | ||
| type="password" | ||
| placeholder="Paste your incident.io API key" | ||
| value={apiKey} | ||
| onChange={(e) => setApiKey(e.target.value)} | ||
| required | ||
| /> | ||
| <p className="text-xs text-muted-foreground"> | ||
| The API key needs these permissions: <strong>View all incident data (including private incidents)</strong> and <strong>Create incidents</strong>. Keys are stored securely in Vault. | ||
| </p> | ||
| </div> | ||
|
|
||
| <Button type="submit" className="w-full" disabled={loading || !apiKey}> | ||
| {loading ? ( | ||
| <> | ||
| <Loader2 className="mr-2 h-4 w-4 animate-spin" /> | ||
| Connecting... | ||
| </> | ||
| ) : ( | ||
| "Connect incident.io" | ||
| )} | ||
| </Button> | ||
| </form> | ||
| </CardContent> | ||
| </Card> | ||
| )} | ||
| </div> | ||
| </ConnectorAuthGuard> | ||
| ); | ||
| } | ||
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.