@oneuptime/common 10.0.19 → 10.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/Server/API/GitHubAPI.ts +104 -12
  2. package/Server/API/TelemetryAPI.ts +208 -0
  3. package/Server/API/UserCallAPI.ts +29 -0
  4. package/Server/API/UserEmailAPI.ts +29 -0
  5. package/Server/API/UserSmsAPI.ts +29 -0
  6. package/Server/API/UserWhatsAppAPI.ts +29 -0
  7. package/Server/Services/LogAggregationService.ts +251 -0
  8. package/Server/Utils/VM/VMRunner.ts +45 -0
  9. package/Types/Log/LogQueryParser.ts +252 -0
  10. package/Types/Log/LogQueryToFilter.ts +131 -0
  11. package/UI/Components/CopyTextButton/CopyTextButton.tsx +3 -3
  12. package/UI/Components/LogsViewer/LogsViewer.tsx +166 -93
  13. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +58 -0
  14. package/UI/Components/LogsViewer/components/FacetSection.tsx +119 -0
  15. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +102 -0
  16. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +122 -0
  17. package/UI/Components/LogsViewer/components/LiveLogsToggle.tsx +4 -4
  18. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +22 -26
  19. package/UI/Components/LogsViewer/components/LogSearchBar.tsx +360 -0
  20. package/UI/Components/LogsViewer/components/LogSearchHelp.tsx +128 -0
  21. package/UI/Components/LogsViewer/components/LogSearchSuggestions.tsx +64 -0
  22. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +199 -0
  23. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +172 -0
  24. package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +27 -57
  25. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +268 -0
  26. package/UI/Components/LogsViewer/components/LogsPagination.tsx +12 -10
  27. package/UI/Components/LogsViewer/components/LogsTable.tsx +33 -32
  28. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +16 -18
  29. package/UI/Components/LogsViewer/components/severityColors.ts +31 -0
  30. package/UI/Components/LogsViewer/components/severityTheme.ts +25 -25
  31. package/UI/Components/LogsViewer/types.ts +20 -0
  32. package/build/dist/Server/API/GitHubAPI.js +40 -9
  33. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  34. package/build/dist/Server/API/TelemetryAPI.js +136 -0
  35. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  36. package/build/dist/Server/API/UserCallAPI.js +17 -0
  37. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  38. package/build/dist/Server/API/UserEmailAPI.js +17 -0
  39. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  40. package/build/dist/Server/API/UserSmsAPI.js +17 -0
  41. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  42. package/build/dist/Server/API/UserWhatsAppAPI.js +17 -0
  43. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  44. package/build/dist/Server/Services/LogAggregationService.js +163 -0
  45. package/build/dist/Server/Services/LogAggregationService.js.map +1 -0
  46. package/build/dist/Server/Utils/VM/VMRunner.js +31 -0
  47. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  48. package/build/dist/Types/Log/LogQueryParser.js +200 -0
  49. package/build/dist/Types/Log/LogQueryParser.js.map +1 -0
  50. package/build/dist/Types/Log/LogQueryToFilter.js +96 -0
  51. package/build/dist/Types/Log/LogQueryToFilter.js.map +1 -0
  52. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +3 -3
  53. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  54. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +64 -42
  55. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  56. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +24 -0
  57. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -0
  58. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +46 -0
  59. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -0
  60. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +35 -0
  61. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -0
  62. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +64 -0
  63. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -0
  64. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js +4 -4
  65. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js.map +1 -1
  66. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -21
  67. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  68. package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +230 -0
  69. package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -0
  70. package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js +84 -0
  71. package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js.map +1 -0
  72. package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js +27 -0
  73. package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js.map +1 -0
  74. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +100 -0
  75. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -0
  76. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +104 -0
  77. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -0
  78. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +14 -35
  79. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
  80. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +127 -0
  81. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -0
  82. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +9 -9
  83. package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
  84. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +31 -30
  85. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  86. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +7 -8
  87. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  88. package/build/dist/UI/Components/LogsViewer/components/severityColors.js +22 -0
  89. package/build/dist/UI/Components/LogsViewer/components/severityColors.js.map +1 -0
  90. package/build/dist/UI/Components/LogsViewer/components/severityTheme.js +25 -25
  91. package/build/dist/UI/Components/LogsViewer/components/severityTheme.js.map +1 -1
  92. package/package.json +1 -1
@@ -2,12 +2,19 @@ import Express, {
2
2
  ExpressRequest,
3
3
  ExpressResponse,
4
4
  ExpressRouter,
5
+ OneUptimeRequest,
5
6
  } from "../Utils/Express";
6
7
  import Response from "../Utils/Response";
7
8
  import BadDataException from "../../Types/Exception/BadDataException";
9
+ import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
10
+ import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
8
11
  import logger from "../Utils/Logger";
9
12
  import { JSONObject } from "../../Types/JSON";
10
- import { DashboardClientUrl, GitHubAppName } from "../EnvironmentConfig";
13
+ import {
14
+ DashboardClientUrl,
15
+ GitHubAppName,
16
+ HomeClientUrl,
17
+ } from "../EnvironmentConfig";
11
18
  import ObjectID from "../../Types/ObjectID";
12
19
  import GitHubUtil, {
13
20
  GitHubRepository,
@@ -15,11 +22,14 @@ import GitHubUtil, {
15
22
  } from "../Utils/CodeRepository/GitHub/GitHub";
16
23
  import CodeRepositoryService from "../Services/CodeRepositoryService";
17
24
  import ProjectService from "../Services/ProjectService";
25
+ import AccessTokenService from "../Services/AccessTokenService";
18
26
  import CodeRepository from "../../Models/DatabaseModels/CodeRepository";
19
27
  import CodeRepositoryType from "../../Types/CodeRepository/CodeRepositoryType";
20
28
  import URL from "../../Types/API/URL";
21
29
  import UserMiddleware from "../Middleware/UserAuthorization";
30
+ import JSONWebToken from "../Utils/JsonWebToken";
22
31
  import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
32
+ import { UserTenantAccessPermission } from "../../Types/Permission";
23
33
 
24
34
  export default class GitHubAPI {
25
35
  public getRouter(): ExpressRouter {
@@ -45,20 +55,22 @@ export default class GitHubAPI {
45
55
  );
46
56
  }
47
57
 
48
- // Decode the state parameter to get projectId and userId
58
+ // Verify and decode the signed state token
49
59
  let projectId: string | undefined;
50
60
  let userId: string | undefined;
51
61
 
52
62
  try {
53
- const decodedState: { projectId?: string; userId?: string } =
54
- JSON.parse(Buffer.from(state, "base64").toString("utf-8"));
55
- projectId = decodedState.projectId;
56
- userId = decodedState.userId;
63
+ const decodedState: JSONObject =
64
+ JSONWebToken.decodeJsonPayload(state);
65
+ projectId = decodedState["projectId"] as string | undefined;
66
+ userId = decodedState["userId"] as string | undefined;
57
67
  } catch {
58
68
  return Response.sendErrorResponse(
59
69
  req,
60
70
  res,
61
- new BadDataException("Invalid state parameter"),
71
+ new BadDataException(
72
+ "Invalid or expired state parameter. Please restart the GitHub App installation.",
73
+ ),
62
74
  );
63
75
  }
64
76
 
@@ -78,6 +90,23 @@ export default class GitHubAPI {
78
90
  );
79
91
  }
80
92
 
93
+ // Verify the user is a member of this project
94
+ const userTenantAccessPermission: UserTenantAccessPermission | null =
95
+ await AccessTokenService.getUserTenantAccessPermission(
96
+ new ObjectID(userId),
97
+ new ObjectID(projectId),
98
+ );
99
+
100
+ if (!userTenantAccessPermission) {
101
+ return Response.sendErrorResponse(
102
+ req,
103
+ res,
104
+ new NotAuthorizedException(
105
+ "You do not have access to this project.",
106
+ ),
107
+ );
108
+ }
109
+
81
110
  // GitHub sends installation_id in query params after app installation
82
111
  const installationId: string | undefined =
83
112
  req.query["installation_id"]?.toString();
@@ -153,13 +182,16 @@ export default class GitHubAPI {
153
182
 
154
183
  /*
155
184
  * Redirect to GitHub App installation page
156
- * The state parameter helps us track the installation
185
+ * The state parameter is a signed JWT to prevent tampering
186
+ * It expires in 1 hour to limit the window for replay attacks
157
187
  */
158
- const state: string = Buffer.from(
159
- JSON.stringify({ projectId, userId }),
160
- ).toString("base64");
188
+ const state: string = JSONWebToken.signJsonPayload(
189
+ { projectId, userId },
190
+ 3600, // 1 hour expiry
191
+ );
161
192
 
162
- const installUrl: string = `https://github.com/apps/${GitHubAppName}/installations/new?state=${state}`;
193
+ const callbackUrl: string = `${HomeClientUrl.toString()}api/github/auth/callback`;
194
+ const installUrl: string = `https://github.com/apps/${GitHubAppName}/installations/new?state=${encodeURIComponent(state)}&redirect_uri=${encodeURIComponent(callbackUrl)}`;
163
195
 
164
196
  return Response.redirect(req, res, URL.fromString(installUrl));
165
197
  } catch (error) {
@@ -182,6 +214,19 @@ export default class GitHubAPI {
182
214
  UserMiddleware.getUserMiddleware,
183
215
  async (req: ExpressRequest, res: ExpressResponse) => {
184
216
  try {
217
+ const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
218
+
219
+ // Require authentication
220
+ if (!oneuptimeRequest.userAuthorization) {
221
+ return Response.sendErrorResponse(
222
+ req,
223
+ res,
224
+ new NotAuthenticatedException(
225
+ "Authentication is required to list repositories.",
226
+ ),
227
+ );
228
+ }
229
+
185
230
  const projectId: string | undefined =
186
231
  req.params["projectId"]?.toString();
187
232
  const installationId: string | undefined =
@@ -203,6 +248,23 @@ export default class GitHubAPI {
203
248
  );
204
249
  }
205
250
 
251
+ // Verify user has access to this project
252
+ const userTenantAccessPermission: UserTenantAccessPermission | null =
253
+ await AccessTokenService.getUserTenantAccessPermission(
254
+ oneuptimeRequest.userAuthorization.userId,
255
+ new ObjectID(projectId),
256
+ );
257
+
258
+ if (!userTenantAccessPermission) {
259
+ return Response.sendErrorResponse(
260
+ req,
261
+ res,
262
+ new NotAuthorizedException(
263
+ "You do not have access to this project.",
264
+ ),
265
+ );
266
+ }
267
+
206
268
  const repositories: Array<GitHubRepository> =
207
269
  await GitHubUtil.listRepositoriesForInstallation(installationId);
208
270
 
@@ -263,6 +325,19 @@ export default class GitHubAPI {
263
325
  UserMiddleware.getUserMiddleware,
264
326
  async (req: ExpressRequest, res: ExpressResponse) => {
265
327
  try {
328
+ const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
329
+
330
+ // Require authentication
331
+ if (!oneuptimeRequest.userAuthorization) {
332
+ return Response.sendErrorResponse(
333
+ req,
334
+ res,
335
+ new NotAuthenticatedException(
336
+ "Authentication is required to connect a repository.",
337
+ ),
338
+ );
339
+ }
340
+
266
341
  const body: JSONObject = req.body;
267
342
 
268
343
  const projectId: string | undefined = body["projectId"]?.toString();
@@ -296,6 +371,23 @@ export default class GitHubAPI {
296
371
  );
297
372
  }
298
373
 
374
+ // Verify user has access to this project
375
+ const userTenantAccessPermission: UserTenantAccessPermission | null =
376
+ await AccessTokenService.getUserTenantAccessPermission(
377
+ oneuptimeRequest.userAuthorization.userId,
378
+ new ObjectID(projectId),
379
+ );
380
+
381
+ if (!userTenantAccessPermission) {
382
+ return Response.sendErrorResponse(
383
+ req,
384
+ res,
385
+ new NotAuthorizedException(
386
+ "You do not have access to this project.",
387
+ ),
388
+ );
389
+ }
390
+
299
391
  if (!repositoryName) {
300
392
  return Response.sendErrorResponse(
301
393
  req,
@@ -11,6 +11,15 @@ import CommonAPI from "./CommonAPI";
11
11
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
12
12
  import TelemetryType from "../../Types/Telemetry/TelemetryType";
13
13
  import TelemetryAttributeService from "../Services/TelemetryAttributeService";
14
+ import LogAggregationService, {
15
+ HistogramBucket,
16
+ HistogramRequest,
17
+ FacetValue,
18
+ FacetRequest,
19
+ } from "../Services/LogAggregationService";
20
+ import ObjectID from "../../Types/ObjectID";
21
+ import OneUptimeDate from "../../Types/Date";
22
+ import { JSONObject } from "../../Types/JSON";
14
23
 
15
24
  const router: ExpressRouter = Express.getRouter();
16
25
 
@@ -85,4 +94,203 @@ const getAttributes: GetAttributesFunction = async (
85
94
  }
86
95
  };
87
96
 
97
+ // --- Log Histogram Endpoint ---
98
+
99
+ router.post(
100
+ "/telemetry/logs/histogram",
101
+ UserMiddleware.getUserMiddleware,
102
+ async (
103
+ req: ExpressRequest,
104
+ res: ExpressResponse,
105
+ next: NextFunction,
106
+ ): Promise<void> => {
107
+ try {
108
+ const databaseProps: DatabaseCommonInteractionProps =
109
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
110
+
111
+ if (!databaseProps?.tenantId) {
112
+ return Response.sendErrorResponse(
113
+ req,
114
+ res,
115
+ new BadDataException("Invalid Project ID"),
116
+ );
117
+ }
118
+
119
+ const body: JSONObject = req.body as JSONObject;
120
+
121
+ const startTime: Date = body["startTime"]
122
+ ? OneUptimeDate.fromString(body["startTime"] as string)
123
+ : OneUptimeDate.addRemoveHours(OneUptimeDate.getCurrentDate(), -1);
124
+
125
+ const endTime: Date = body["endTime"]
126
+ ? OneUptimeDate.fromString(body["endTime"] as string)
127
+ : OneUptimeDate.getCurrentDate();
128
+
129
+ const bucketSizeInMinutes: number =
130
+ (body["bucketSizeInMinutes"] as number) ||
131
+ computeDefaultBucketSize(startTime, endTime);
132
+
133
+ const serviceIds: Array<ObjectID> | undefined = body["serviceIds"]
134
+ ? (body["serviceIds"] as Array<string>).map((id: string) => {
135
+ return new ObjectID(id);
136
+ })
137
+ : undefined;
138
+
139
+ const severityTexts: Array<string> | undefined = body["severityTexts"]
140
+ ? (body["severityTexts"] as Array<string>)
141
+ : undefined;
142
+
143
+ const bodySearchText: string | undefined = body["bodySearchText"]
144
+ ? (body["bodySearchText"] as string)
145
+ : undefined;
146
+
147
+ const traceIds: Array<string> | undefined = body["traceIds"]
148
+ ? (body["traceIds"] as Array<string>)
149
+ : undefined;
150
+
151
+ const spanIds: Array<string> | undefined = body["spanIds"]
152
+ ? (body["spanIds"] as Array<string>)
153
+ : undefined;
154
+
155
+ const request: HistogramRequest = {
156
+ projectId: databaseProps.tenantId,
157
+ startTime,
158
+ endTime,
159
+ bucketSizeInMinutes,
160
+ serviceIds,
161
+ severityTexts,
162
+ bodySearchText,
163
+ traceIds,
164
+ spanIds,
165
+ };
166
+
167
+ const buckets: Array<HistogramBucket> =
168
+ await LogAggregationService.getHistogram(request);
169
+
170
+ return Response.sendJsonObjectResponse(req, res, {
171
+ buckets: buckets as unknown as JSONObject,
172
+ });
173
+ } catch (err: unknown) {
174
+ next(err);
175
+ }
176
+ },
177
+ );
178
+
179
+ // --- Log Facets Endpoint ---
180
+
181
+ router.post(
182
+ "/telemetry/logs/facets",
183
+ UserMiddleware.getUserMiddleware,
184
+ async (
185
+ req: ExpressRequest,
186
+ res: ExpressResponse,
187
+ next: NextFunction,
188
+ ): Promise<void> => {
189
+ try {
190
+ const databaseProps: DatabaseCommonInteractionProps =
191
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
192
+
193
+ if (!databaseProps?.tenantId) {
194
+ return Response.sendErrorResponse(
195
+ req,
196
+ res,
197
+ new BadDataException("Invalid Project ID"),
198
+ );
199
+ }
200
+
201
+ const body: JSONObject = req.body as JSONObject;
202
+
203
+ const facetKeys: Array<string> = body["facetKeys"]
204
+ ? (body["facetKeys"] as Array<string>)
205
+ : ["severityText", "serviceId"];
206
+
207
+ const startTime: Date = body["startTime"]
208
+ ? OneUptimeDate.fromString(body["startTime"] as string)
209
+ : OneUptimeDate.addRemoveHours(OneUptimeDate.getCurrentDate(), -1);
210
+
211
+ const endTime: Date = body["endTime"]
212
+ ? OneUptimeDate.fromString(body["endTime"] as string)
213
+ : OneUptimeDate.getCurrentDate();
214
+
215
+ const limit: number = (body["limit"] as number) || 10;
216
+
217
+ const serviceIds: Array<ObjectID> | undefined = body["serviceIds"]
218
+ ? (body["serviceIds"] as Array<string>).map((id: string) => {
219
+ return new ObjectID(id);
220
+ })
221
+ : undefined;
222
+
223
+ const severityTexts: Array<string> | undefined = body["severityTexts"]
224
+ ? (body["severityTexts"] as Array<string>)
225
+ : undefined;
226
+
227
+ const bodySearchText: string | undefined = body["bodySearchText"]
228
+ ? (body["bodySearchText"] as string)
229
+ : undefined;
230
+
231
+ const traceIds: Array<string> | undefined = body["traceIds"]
232
+ ? (body["traceIds"] as Array<string>)
233
+ : undefined;
234
+
235
+ const spanIds: Array<string> | undefined = body["spanIds"]
236
+ ? (body["spanIds"] as Array<string>)
237
+ : undefined;
238
+
239
+ const facets: Record<string, Array<FacetValue>> = {};
240
+
241
+ for (const facetKey of facetKeys) {
242
+ const request: FacetRequest = {
243
+ projectId: databaseProps.tenantId,
244
+ startTime,
245
+ endTime,
246
+ facetKey,
247
+ limit,
248
+ serviceIds,
249
+ severityTexts,
250
+ bodySearchText,
251
+ traceIds,
252
+ spanIds,
253
+ };
254
+
255
+ facets[facetKey] = await LogAggregationService.getFacetValues(request);
256
+ }
257
+
258
+ return Response.sendJsonObjectResponse(req, res, {
259
+ facets: facets as unknown as JSONObject,
260
+ });
261
+ } catch (err: unknown) {
262
+ next(err);
263
+ }
264
+ },
265
+ );
266
+
267
+ // --- Helpers ---
268
+
269
+ function computeDefaultBucketSize(startTime: Date, endTime: Date): number {
270
+ const diffMs: number = endTime.getTime() - startTime.getTime();
271
+ const diffMinutes: number = diffMs / (1000 * 60);
272
+
273
+ if (diffMinutes <= 60) {
274
+ return 1;
275
+ }
276
+
277
+ if (diffMinutes <= 360) {
278
+ return 5;
279
+ }
280
+
281
+ if (diffMinutes <= 1440) {
282
+ return 15;
283
+ }
284
+
285
+ if (diffMinutes <= 10080) {
286
+ return 60;
287
+ }
288
+
289
+ if (diffMinutes <= 43200) {
290
+ return 360;
291
+ }
292
+
293
+ return 1440;
294
+ }
295
+
88
296
  export default router;
@@ -136,6 +136,35 @@ export default class UserCallAPI extends BaseAPI<
136
136
  );
137
137
  }
138
138
 
139
+ const item: UserCall | null = await this.service.findOneById({
140
+ id: req.body["itemId"],
141
+ props: {
142
+ isRoot: true,
143
+ },
144
+ select: {
145
+ userId: true,
146
+ },
147
+ });
148
+
149
+ if (!item) {
150
+ return Response.sendErrorResponse(
151
+ req,
152
+ res,
153
+ new BadDataException("Item not found"),
154
+ );
155
+ }
156
+
157
+ if (
158
+ item.userId?.toString() !==
159
+ (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
160
+ ) {
161
+ return Response.sendErrorResponse(
162
+ req,
163
+ res,
164
+ new BadDataException("Invalid user ID"),
165
+ );
166
+ }
167
+
139
168
  await this.service.resendVerificationCode(req.body.itemId);
140
169
 
141
170
  return Response.sendEmptySuccessResponse(req, res);
@@ -137,6 +137,35 @@ export default class UserEmailAPI extends BaseAPI<
137
137
  );
138
138
  }
139
139
 
140
+ const item: UserEmail | null = await this.service.findOneById({
141
+ id: req.body["itemId"],
142
+ props: {
143
+ isRoot: true,
144
+ },
145
+ select: {
146
+ userId: true,
147
+ },
148
+ });
149
+
150
+ if (!item) {
151
+ return Response.sendErrorResponse(
152
+ req,
153
+ res,
154
+ new BadDataException("Item not found"),
155
+ );
156
+ }
157
+
158
+ if (
159
+ item.userId?.toString() !==
160
+ (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
161
+ ) {
162
+ return Response.sendErrorResponse(
163
+ req,
164
+ res,
165
+ new BadDataException("Invalid user ID"),
166
+ );
167
+ }
168
+
140
169
  await this.service.resendVerificationCode(req.body.itemId);
141
170
 
142
171
  return Response.sendEmptySuccessResponse(req, res);
@@ -132,6 +132,35 @@ export default class UserSMSAPI extends BaseAPI<UserSMS, UserSMSServiceType> {
132
132
  );
133
133
  }
134
134
 
135
+ const item: UserSMS | null = await this.service.findOneById({
136
+ id: req.body["itemId"],
137
+ props: {
138
+ isRoot: true,
139
+ },
140
+ select: {
141
+ userId: true,
142
+ },
143
+ });
144
+
145
+ if (!item) {
146
+ return Response.sendErrorResponse(
147
+ req,
148
+ res,
149
+ new BadDataException("Item not found"),
150
+ );
151
+ }
152
+
153
+ if (
154
+ item.userId?.toString() !==
155
+ (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
156
+ ) {
157
+ return Response.sendErrorResponse(
158
+ req,
159
+ res,
160
+ new BadDataException("Invalid user ID"),
161
+ );
162
+ }
163
+
135
164
  await this.service.resendVerificationCode(req.body.itemId);
136
165
 
137
166
  return Response.sendEmptySuccessResponse(req, res);
@@ -143,6 +143,35 @@ export default class UserWhatsAppAPI extends BaseAPI<
143
143
  );
144
144
  }
145
145
 
146
+ const item: UserWhatsApp | null = await this.service.findOneById({
147
+ id: req.body["itemId"],
148
+ props: {
149
+ isRoot: true,
150
+ },
151
+ select: {
152
+ userId: true,
153
+ },
154
+ });
155
+
156
+ if (!item) {
157
+ return Response.sendErrorResponse(
158
+ req,
159
+ res,
160
+ new BadDataException("Item not found"),
161
+ );
162
+ }
163
+
164
+ if (
165
+ item.userId?.toString() !==
166
+ (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
167
+ ) {
168
+ return Response.sendErrorResponse(
169
+ req,
170
+ res,
171
+ new BadDataException("Invalid user ID"),
172
+ );
173
+ }
174
+
146
175
  await this.service.resendVerificationCode(req.body.itemId);
147
176
 
148
177
  return Response.sendEmptySuccessResponse(req, res);