@@ -8,6 +8,21 @@ const apicache = require("../modules/apicache");
88const StatusPage = require ( "../model/status_page" ) ;
99const { UptimeKumaServer } = require ( "../uptime-kuma-server" ) ;
1010
11+ /**
12+ * Validates incident data
13+ * @param {object } incident - The incident object
14+ * @returns {void }
15+ * @throws {Error } If validation fails
16+ */
17+ function validateIncident ( incident ) {
18+ if ( ! incident . title || incident . title . trim ( ) === "" ) {
19+ throw new Error ( "Please input title" ) ;
20+ }
21+ if ( ! incident . content || incident . content . trim ( ) === "" ) {
22+ throw new Error ( "Please input content" ) ;
23+ }
24+ }
25+
1126/**
1227 * Socket handlers for status page
1328 * @param {Socket } socket Socket.io instance to add listeners on
@@ -25,8 +40,6 @@ module.exports.statusPageSocketHandler = (socket) => {
2540 throw new Error ( "slug is not found" ) ;
2641 }
2742
28- await R . exec ( "UPDATE incident SET pin = 0 WHERE status_page_id = ? " , [ statusPageID ] ) ;
29-
3043 let incidentBean ;
3144
3245 if ( incident . id ) {
@@ -44,12 +57,13 @@ module.exports.statusPageSocketHandler = (socket) => {
4457 incidentBean . content = incident . content ;
4558 incidentBean . style = incident . style ;
4659 incidentBean . pin = true ;
60+ incidentBean . active = true ;
4761 incidentBean . status_page_id = statusPageID ;
4862
4963 if ( incident . id ) {
50- incidentBean . lastUpdatedDate = R . isoDateTime ( dayjs . utc ( ) ) ;
64+ incidentBean . last_updated_date = R . isoDateTime ( dayjs . utc ( ) ) ;
5165 } else {
52- incidentBean . createdDate = R . isoDateTime ( dayjs . utc ( ) ) ;
66+ incidentBean . created_date = R . isoDateTime ( dayjs . utc ( ) ) ;
5367 }
5468
5569 await R . store ( incidentBean ) ;
@@ -85,6 +99,171 @@ module.exports.statusPageSocketHandler = (socket) => {
8599 }
86100 } ) ;
87101
102+ socket . on ( "getIncidentHistory" , async ( slug , cursor , callback ) => {
103+ try {
104+ let statusPageID = await StatusPage . slugToID ( slug ) ;
105+ if ( ! statusPageID ) {
106+ throw new Error ( "slug is not found" ) ;
107+ }
108+
109+ const isPublic = ! socket . userID ;
110+ const result = await StatusPage . getIncidentHistory ( statusPageID , cursor , isPublic ) ;
111+ callback ( {
112+ ok : true ,
113+ ...result ,
114+ } ) ;
115+ } catch ( error ) {
116+ callback ( {
117+ ok : false ,
118+ msg : error . message ,
119+ } ) ;
120+ }
121+ } ) ;
122+
123+ socket . on ( "editIncident" , async ( slug , incidentID , incident , callback ) => {
124+ try {
125+ checkLogin ( socket ) ;
126+
127+ let statusPageID = await StatusPage . slugToID ( slug ) ;
128+ if ( ! statusPageID ) {
129+ callback ( {
130+ ok : false ,
131+ msg : "slug is not found" ,
132+ msgi18n : true ,
133+ } ) ;
134+ return ;
135+ }
136+
137+ let bean = await R . findOne ( "incident" , " id = ? AND status_page_id = ? " , [ incidentID , statusPageID ] ) ;
138+ if ( ! bean ) {
139+ callback ( {
140+ ok : false ,
141+ msg : "Incident not found or access denied" ,
142+ msgi18n : true ,
143+ } ) ;
144+ return ;
145+ }
146+
147+ try {
148+ validateIncident ( incident ) ;
149+ } catch ( e ) {
150+ callback ( {
151+ ok : false ,
152+ msg : e . message ,
153+ msgi18n : true ,
154+ } ) ;
155+ return ;
156+ }
157+
158+ const validStyles = [ "info" , "warning" , "danger" , "primary" , "light" , "dark" ] ;
159+ if ( ! validStyles . includes ( incident . style ) ) {
160+ incident . style = "warning" ;
161+ }
162+
163+ bean . title = incident . title ;
164+ bean . content = incident . content ;
165+ bean . style = incident . style ;
166+ bean . pin = incident . pin !== false ;
167+ bean . lastUpdatedDate = R . isoDateTime ( dayjs . utc ( ) ) ;
168+
169+ await R . store ( bean ) ;
170+
171+ callback ( {
172+ ok : true ,
173+ msg : "Saved." ,
174+ msgi18n : true ,
175+ incident : bean . toPublicJSON ( ) ,
176+ } ) ;
177+ } catch ( error ) {
178+ callback ( {
179+ ok : false ,
180+ msg : error . message ,
181+ msgi18n : true ,
182+ } ) ;
183+ }
184+ } ) ;
185+
186+ socket . on ( "deleteIncident" , async ( slug , incidentID , callback ) => {
187+ try {
188+ checkLogin ( socket ) ;
189+
190+ let statusPageID = await StatusPage . slugToID ( slug ) ;
191+ if ( ! statusPageID ) {
192+ callback ( {
193+ ok : false ,
194+ msg : "slug is not found" ,
195+ msgi18n : true ,
196+ } ) ;
197+ return ;
198+ }
199+
200+ let bean = await R . findOne ( "incident" , " id = ? AND status_page_id = ? " , [ incidentID , statusPageID ] ) ;
201+ if ( ! bean ) {
202+ callback ( {
203+ ok : false ,
204+ msg : "Incident not found or access denied" ,
205+ msgi18n : true ,
206+ } ) ;
207+ return ;
208+ }
209+
210+ await R . trash ( bean ) ;
211+
212+ callback ( {
213+ ok : true ,
214+ msg : "successDeleted" ,
215+ msgi18n : true ,
216+ } ) ;
217+ } catch ( error ) {
218+ callback ( {
219+ ok : false ,
220+ msg : error . message ,
221+ msgi18n : true ,
222+ } ) ;
223+ }
224+ } ) ;
225+
226+ socket . on ( "resolveIncident" , async ( slug , incidentID , callback ) => {
227+ try {
228+ checkLogin ( socket ) ;
229+
230+ let statusPageID = await StatusPage . slugToID ( slug ) ;
231+ if ( ! statusPageID ) {
232+ callback ( {
233+ ok : false ,
234+ msg : "slug is not found" ,
235+ msgi18n : true ,
236+ } ) ;
237+ return ;
238+ }
239+
240+ let bean = await R . findOne ( "incident" , " id = ? AND status_page_id = ? " , [ incidentID , statusPageID ] ) ;
241+ if ( ! bean ) {
242+ callback ( {
243+ ok : false ,
244+ msg : "Incident not found or access denied" ,
245+ msgi18n : true ,
246+ } ) ;
247+ return ;
248+ }
249+
250+ await bean . resolve ( ) ;
251+
252+ callback ( {
253+ ok : true ,
254+ msg : "Resolved" ,
255+ msgi18n : true ,
256+ incident : bean . toPublicJSON ( ) ,
257+ } ) ;
258+ } catch ( error ) {
259+ callback ( {
260+ ok : false ,
261+ msg : error . message ,
262+ msgi18n : true ,
263+ } ) ;
264+ }
265+ } ) ;
266+
88267 socket . on ( "getStatusPage" , async ( slug , callback ) => {
89268 try {
90269 checkLogin ( socket ) ;
0 commit comments