feat: add sentry webhook integration
This commit is contained in:
parent
8534ab7ba0
commit
546055e555
@ -3,6 +3,7 @@ import { LuGithub, LuPlug } from 'react-icons/lu';
|
|||||||
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||||
import { CodeBlock } from '../CodeBlock';
|
import { CodeBlock } from '../CodeBlock';
|
||||||
import { useTranslation } from '@i18next-toolkit/react';
|
import { useTranslation } from '@i18next-toolkit/react';
|
||||||
|
import { SiSentry } from 'react-icons/si';
|
||||||
|
|
||||||
export const FeedIntegration: React.FC<{
|
export const FeedIntegration: React.FC<{
|
||||||
feedId: string;
|
feedId: string;
|
||||||
@ -29,6 +30,22 @@ export const FeedIntegration: React.FC<{
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FeedIntegrationItem
|
||||||
|
icon={<SiSentry size={32} />}
|
||||||
|
label="Sentry"
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-bold">{t('Receive Webhooks')}</div>
|
||||||
|
|
||||||
|
<div>{t('Add sentry webhook with url')}:</div>
|
||||||
|
|
||||||
|
<CodeBlock
|
||||||
|
code={`${window.location.origin}/open/feed/${props.feedId}/sentry`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<FeedIntegrationItem
|
<FeedIntegrationItem
|
||||||
icon={<LuPlug size={32} />}
|
icon={<LuPlug size={32} />}
|
||||||
label="Custom"
|
label="Custom"
|
||||||
|
@ -27,7 +27,11 @@ const app = express();
|
|||||||
|
|
||||||
app.set('trust proxy', true);
|
app.set('trust proxy', true);
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
app.use(express.json());
|
app.use(
|
||||||
|
express.json({
|
||||||
|
limit: '10mb',
|
||||||
|
})
|
||||||
|
);
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(morgan('tiny'));
|
app.use(morgan('tiny'));
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
@ -6,7 +6,7 @@ import { OPENAPI_TAG } from '../../../utils/const.js';
|
|||||||
import { createFeedEvent } from '../../../model/feed/event.js';
|
import { createFeedEvent } from '../../../model/feed/event.js';
|
||||||
import { tencentCloudAlarmSchema } from '../../../model/_schema/feed.js';
|
import { tencentCloudAlarmSchema } from '../../../model/_schema/feed.js';
|
||||||
import { logger } from '../../../utils/logger.js';
|
import { logger } from '../../../utils/logger.js';
|
||||||
import { get, map } from 'lodash-es';
|
import { compact, fromPairs, get, map } from 'lodash-es';
|
||||||
|
|
||||||
export const feedIntegrationRouter = router({
|
export const feedIntegrationRouter = router({
|
||||||
github: publicProcedure
|
github: publicProcedure
|
||||||
@ -109,7 +109,6 @@ export const feedIntegrationRouter = router({
|
|||||||
return 'ok';
|
return 'ok';
|
||||||
} else if (eventType === 'issues') {
|
} else if (eventType === 'issues') {
|
||||||
const action = get(data, 'action') as 'opened' | 'closed';
|
const action = get(data, 'action') as 'opened' | 'closed';
|
||||||
const starCount = get(data, 'repository.stargazers_count');
|
|
||||||
const fullName = get(data, 'repository.full_name');
|
const fullName = get(data, 'repository.full_name');
|
||||||
const repoUrl = get(data, 'repository.html_url');
|
const repoUrl = get(data, 'repository.html_url');
|
||||||
const senderId = String(get(data, 'sender.id'));
|
const senderId = String(get(data, 'sender.id'));
|
||||||
@ -234,6 +233,73 @@ export const feedIntegrationRouter = router({
|
|||||||
return 'ok';
|
return 'ok';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 'Not supported yet';
|
||||||
|
}),
|
||||||
|
sentry: publicProcedure
|
||||||
|
.meta(
|
||||||
|
buildFeedPublicOpenapi({
|
||||||
|
method: 'POST',
|
||||||
|
path: '/{channelId}/sentry',
|
||||||
|
summary: 'integrate with sentry webhook',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.input(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
channelId: z.string(),
|
||||||
|
})
|
||||||
|
.passthrough()
|
||||||
|
)
|
||||||
|
.output(z.string())
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const { channelId, ...data } = input;
|
||||||
|
const eventType = ctx.req.headers['sentry-hook-resource'];
|
||||||
|
|
||||||
|
const workspaceId = await prisma.feedChannel
|
||||||
|
.findFirst({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
workspaceId: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res?.workspaceId);
|
||||||
|
|
||||||
|
if (!workspaceId) {
|
||||||
|
throw new Error('Not found Workspace');
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = get(data, 'action');
|
||||||
|
|
||||||
|
if (eventType === 'event_alert' && action === 'triggered') {
|
||||||
|
const message = get(data, 'data.event.message');
|
||||||
|
const tags = fromPairs<string>(get(data as any, 'data.event.tags'));
|
||||||
|
const url = String(get(data, 'data.event.web_url'));
|
||||||
|
const senderId = String(get(data, 'data.actor.id'));
|
||||||
|
const senderName = String(get(data, 'data.actor.name'));
|
||||||
|
|
||||||
|
await createFeedEvent(workspaceId, {
|
||||||
|
channelId: channelId,
|
||||||
|
eventName: 'alert',
|
||||||
|
eventContent: `${message}`,
|
||||||
|
tags: compact([
|
||||||
|
tags['environment'],
|
||||||
|
tags['release'],
|
||||||
|
tags['browser'],
|
||||||
|
tags['os'],
|
||||||
|
tags['device'],
|
||||||
|
]),
|
||||||
|
source: 'sentry',
|
||||||
|
senderId,
|
||||||
|
senderName,
|
||||||
|
important: false,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
|
||||||
return 'Not supported yet';
|
return 'Not supported yet';
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user