@@ -2,8 +2,10 @@ import { NextFunction, Request, RequestHandler, Response } from 'express';
22import { SlackService } from '@/services/slack.service' ;
33import { SentryService } from '@/services/sentry.service' ;
44import { SentryWebhookData , SlackMessage } from '@/types' ;
5+ import { SentryActionData , SentryApiAction } from '@/types/models/Sentry.type' ;
56import logger from '@/configs/logger.config' ;
67import { formatSentryIssueForSlack , createStatusUpdateMessage } from '@/utils/slack.util' ;
8+ import { getNewStatusFromAction } from '@/utils/sentry.util' ;
79
810export class WebhookController {
911 constructor (
@@ -37,6 +39,51 @@ export class WebhookController {
3739 }
3840 } ;
3941
42+ handleSlackInteractive : RequestHandler = async (
43+ req : Request ,
44+ res : Response ,
45+ next : NextFunction ,
46+ ) : Promise < void > => {
47+ try {
48+ const payload = JSON . parse ( req . body . payload ) ;
49+
50+ if ( payload . type === 'interactive_message' && payload . actions && payload . actions [ 0 ] ) {
51+ const action = payload . actions [ 0 ] ;
52+
53+ if ( action . name === 'sentry_action' ) {
54+ const [ actionType , issueId , organizationSlug , projectSlug ] = action . value . split ( ':' ) ;
55+
56+ const actionData : SentryActionData = {
57+ action : actionType as SentryApiAction ,
58+ issueId,
59+ organizationSlug,
60+ projectSlug,
61+ } ;
62+
63+ if ( actionData . issueId && actionData . organizationSlug && actionData . projectSlug ) {
64+ logger . info ( 'Processing Sentry action:' , actionData ) ;
65+
66+ const result = await this . sentryService . handleIssueAction ( actionData ) ;
67+
68+ if ( result . success ) {
69+ const updatedMessage = this . createSuccessMessage ( actionData , payload . original_message || { } ) ;
70+ res . json ( updatedMessage ) ;
71+ } else {
72+ const errorMessage = this . createErrorMessage ( result . error || 'Unknown error' , payload . original_message || { } ) ;
73+ res . json ( errorMessage ) ;
74+ }
75+ return ;
76+ }
77+ }
78+ }
79+
80+ res . json ( { text : '❌ 잘못된 요청입니다.' } ) ;
81+ } catch ( error ) {
82+ logger . error ( 'Slack Interactive 처리 실패:' , error instanceof Error ? error . message : '알 수 없는 오류' ) ;
83+ next ( error ) ;
84+ }
85+ } ;
86+
4087 private async formatSentryDataForSlack ( sentryData : SentryWebhookData ) : Promise < SlackMessage | null > {
4188 const { action, data } = sentryData ;
4289
@@ -106,4 +153,52 @@ export class WebhookController {
106153
107154 return formatSentryIssueForSlack ( sentryData , this . sentryService . hasSentryToken ( ) ) ;
108155 }
156+
157+ private createSuccessMessage ( actionData : SentryActionData , originalMessage : unknown ) : unknown {
158+ const { action } = actionData ;
159+
160+ const updatedMessage = JSON . parse ( JSON . stringify ( originalMessage ) ) ;
161+
162+ if ( updatedMessage . attachments && updatedMessage . attachments [ 0 ] ) {
163+ const newStatus = getNewStatusFromAction ( action ) ;
164+ const statusColors = {
165+ 'resolved' : 'good' ,
166+ 'ignored' : 'warning' ,
167+ 'archived' : '#808080' ,
168+ 'unresolved' : 'danger' ,
169+ } ;
170+
171+ updatedMessage . attachments [ 0 ] . color = statusColors [ newStatus as keyof typeof statusColors ] || 'good' ;
172+
173+ const statusMapping = {
174+ 'resolved' : 'RESOLVED' ,
175+ 'ignored' : 'IGNORED' ,
176+ 'archived' : 'ARCHIVED' ,
177+ 'unresolved' : 'UNRESOLVED' ,
178+ } ;
179+
180+ const statusText = statusMapping [ newStatus as keyof typeof statusMapping ] || newStatus . toUpperCase ( ) ;
181+ updatedMessage . attachments [ 0 ] . footer = `✅ ${ statusText } | 처리 완료: ${ new Date ( ) . toLocaleString ( 'ko-KR' , { timeZone : 'Asia/Seoul' } ) } ` ;
182+
183+ delete updatedMessage . attachments [ 0 ] . actions ;
184+ }
185+
186+ return updatedMessage ;
187+ }
188+
189+ private createErrorMessage ( error : string , originalMessage : unknown ) : unknown {
190+ const updatedMessage = JSON . parse ( JSON . stringify ( originalMessage ) ) ;
191+
192+ if ( updatedMessage . attachments && updatedMessage . attachments [ 0 ] ) {
193+ updatedMessage . attachments [ 0 ] . fields . push ( {
194+ title : '❌ 오류 발생' ,
195+ value : error ,
196+ short : false ,
197+ } ) ;
198+
199+ updatedMessage . attachments [ 0 ] . color = 'danger' ;
200+ }
201+
202+ return updatedMessage ;
203+ }
109204}
0 commit comments