@oneuptime/common 9.4.0 → 9.4.2

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 (31) hide show
  1. package/Models/DatabaseModels/StatusPageDomain.ts +2 -0
  2. package/Server/Infrastructure/Postgres/SchemaMigrations/1768825402472-MigrationName.ts +317 -0
  3. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -6
  4. package/Server/Services/DatabaseService.ts +23 -1
  5. package/Server/Services/DomainService.ts +26 -15
  6. package/Server/Services/MonitorProbeService.ts +96 -1
  7. package/Types/Domain.ts +23 -0
  8. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +2 -0
  9. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  10. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768825402472-MigrationName.js +116 -0
  11. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768825402472-MigrationName.js.map +1 -0
  12. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -6
  13. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  14. package/build/dist/Server/Services/DatabaseService.js +12 -0
  15. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  16. package/build/dist/Server/Services/DomainService.js +19 -8
  17. package/build/dist/Server/Services/DomainService.js.map +1 -1
  18. package/build/dist/Server/Services/MonitorProbeService.js +77 -0
  19. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  20. package/build/dist/Types/Domain.js +23 -1
  21. package/build/dist/Types/Domain.js.map +1 -1
  22. package/package.json +1 -1
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.ts +0 -22
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.ts +0 -787
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.ts +0 -29
  26. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.js +0 -34
  27. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.js.map +0 -1
  28. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.js +0 -270
  29. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.js.map +0 -1
  30. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.js +0 -16
  31. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.js.map +0 -1
@@ -318,6 +318,7 @@ export default class StatusPageDomain extends BaseModel {
318
318
  })
319
319
  @TableColumn({
320
320
  required: true,
321
+ computed: true,
321
322
  type: TableColumnType.ShortText,
322
323
  title: "Full Domain",
323
324
  description:
@@ -448,6 +449,7 @@ export default class StatusPageDomain extends BaseModel {
448
449
  })
449
450
  @TableColumn({
450
451
  required: true,
452
+ computed: true,
451
453
  type: TableColumnType.ShortText,
452
454
  title: "CNAME Verification Token",
453
455
  description: "CNAME Verification Token",
@@ -0,0 +1,317 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1768825402472 implements MigrationInterface {
4
+ public name = "MigrationName1768825402472";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `CREATE TABLE "IncomingCallPolicy" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "name" character varying(100) NOT NULL, "description" character varying(500), "slug" character varying(100) NOT NULL, "routingPhoneNumber" character varying(30), "callProviderPhoneNumberId" character varying(100), "phoneNumberCountryCode" character varying(100), "phoneNumberAreaCode" character varying(100), "phoneNumberPurchasedAt" TIMESTAMP WITH TIME ZONE, "greetingMessage" character varying(500) DEFAULT 'Please wait while we connect you to the on-call engineer.', "noAnswerMessage" character varying(500) DEFAULT 'No one is available. Please try again later.', "noOneAvailableMessage" character varying(500) DEFAULT 'We are sorry, but no on-call engineer is currently available. Please try again later or contact support.', "isEnabled" boolean NOT NULL DEFAULT true, "repeatPolicyIfNoOneAnswers" boolean NOT NULL DEFAULT false, "repeatPolicyIfNoOneAnswersTimes" integer NOT NULL DEFAULT '1', "projectCallSMSConfigId" uuid, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "UQ_5242e2cffd7f4050e6189e569d5" UNIQUE ("slug"), CONSTRAINT "PK_1cce87f0549b0284e23492d0910" PRIMARY KEY ("_id"))`,
9
+ );
10
+ await queryRunner.query(
11
+ `CREATE INDEX "IDX_3c52bf3d9f9aca2ac74848fc0f" ON "IncomingCallPolicy" ("projectId") `,
12
+ );
13
+ await queryRunner.query(
14
+ `CREATE INDEX "IDX_f2f54dca4c8c0bbdea4c37edb2" ON "IncomingCallPolicy" ("name") `,
15
+ );
16
+ await queryRunner.query(
17
+ `CREATE INDEX "IDX_5242e2cffd7f4050e6189e569d" ON "IncomingCallPolicy" ("slug") `,
18
+ );
19
+ await queryRunner.query(
20
+ `CREATE INDEX "IDX_6680fc89e547525f8051bb2599" ON "IncomingCallPolicy" ("routingPhoneNumber") `,
21
+ );
22
+ await queryRunner.query(
23
+ `CREATE TABLE "IncomingCallPolicyEscalationRule" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "incomingCallPolicyId" uuid NOT NULL, "name" character varying(100), "description" character varying(500), "order" integer NOT NULL, "escalateAfterSeconds" integer NOT NULL DEFAULT '30', "onCallDutyPolicyScheduleId" uuid, "userId" uuid, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "PK_162e367fb58da2aea22a14057e1" PRIMARY KEY ("_id"))`,
24
+ );
25
+ await queryRunner.query(
26
+ `CREATE INDEX "IDX_3ff7e49f61d10020a7298c3dc6" ON "IncomingCallPolicyEscalationRule" ("projectId") `,
27
+ );
28
+ await queryRunner.query(
29
+ `CREATE INDEX "IDX_d9358a64a7d4a3f48804844e69" ON "IncomingCallPolicyEscalationRule" ("incomingCallPolicyId") `,
30
+ );
31
+ await queryRunner.query(
32
+ `CREATE INDEX "IDX_2940b1f9392441bcff1a33ebc4" ON "IncomingCallPolicyEscalationRule" ("order") `,
33
+ );
34
+ await queryRunner.query(
35
+ `CREATE TABLE "IncomingCallLog" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "incomingCallPolicyId" uuid NOT NULL, "callerPhoneNumber" character varying(30), "routingPhoneNumber" character varying(30), "callProviderCallId" character varying(100), "status" character varying(100) NOT NULL DEFAULT 'Initiated', "statusMessage" character varying(500), "callDurationInSeconds" integer DEFAULT '0', "callCostInUSDCents" integer DEFAULT '0', "incomingCallCostInUSDCents" integer DEFAULT '0', "outgoingCallCostInUSDCents" integer DEFAULT '0', "startedAt" TIMESTAMP WITH TIME ZONE, "endedAt" TIMESTAMP WITH TIME ZONE, "answeredByUserId" uuid, "currentEscalationRuleOrder" integer DEFAULT '1', "repeatCount" integer DEFAULT '0', CONSTRAINT "PK_c1fd81ebc16e88b441dd8d19108" PRIMARY KEY ("_id"))`,
36
+ );
37
+ await queryRunner.query(
38
+ `CREATE INDEX "IDX_915f3d5f9bf60430bbd89a1efc" ON "IncomingCallLog" ("projectId") `,
39
+ );
40
+ await queryRunner.query(
41
+ `CREATE INDEX "IDX_18e2254401cd580a906808d690" ON "IncomingCallLog" ("incomingCallPolicyId") `,
42
+ );
43
+ await queryRunner.query(
44
+ `CREATE INDEX "IDX_4769731b4e40dd238410b47337" ON "IncomingCallLog" ("callProviderCallId") `,
45
+ );
46
+ await queryRunner.query(
47
+ `CREATE INDEX "IDX_bac28bb0cfff30703cb79e4a9c" ON "IncomingCallLog" ("status") `,
48
+ );
49
+ await queryRunner.query(
50
+ `CREATE INDEX "IDX_0821fb47819572b56836b06102" ON "IncomingCallLog" ("startedAt") `,
51
+ );
52
+ await queryRunner.query(
53
+ `CREATE TABLE "IncomingCallLogItem" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "incomingCallLogId" uuid NOT NULL, "incomingCallPolicyEscalationRuleId" uuid, "userId" uuid, "userPhoneNumber" character varying(30), "status" character varying(100) NOT NULL DEFAULT 'Ringing', "statusMessage" character varying(500), "dialDurationInSeconds" integer DEFAULT '0', "callCostInUSDCents" integer DEFAULT '0', "startedAt" TIMESTAMP WITH TIME ZONE, "endedAt" TIMESTAMP WITH TIME ZONE, "isAnswered" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_00c152a3a8f417339c18000d197" PRIMARY KEY ("_id"))`,
54
+ );
55
+ await queryRunner.query(
56
+ `CREATE INDEX "IDX_4d0e67775a87ffdf8b1413d5bc" ON "IncomingCallLogItem" ("projectId") `,
57
+ );
58
+ await queryRunner.query(
59
+ `CREATE INDEX "IDX_d11a949952998081bed04512a7" ON "IncomingCallLogItem" ("incomingCallLogId") `,
60
+ );
61
+ await queryRunner.query(
62
+ `CREATE INDEX "IDX_cb196303d93ee4044d0a4f076e" ON "IncomingCallLogItem" ("userId") `,
63
+ );
64
+ await queryRunner.query(
65
+ `CREATE INDEX "IDX_5ecd45b8e5c05322459b01f37e" ON "IncomingCallLogItem" ("status") `,
66
+ );
67
+ await queryRunner.query(
68
+ `CREATE INDEX "IDX_c2f339131713708082a01cd880" ON "IncomingCallLogItem" ("startedAt") `,
69
+ );
70
+ await queryRunner.query(
71
+ `CREATE TABLE "UserIncomingCallNumber" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "phone" character varying(30) NOT NULL, "userId" uuid, "createdByUserId" uuid, "deletedByUserId" uuid, "isVerified" boolean NOT NULL DEFAULT false, "verificationCode" character varying(100) NOT NULL, CONSTRAINT "PK_6e487c7ce740c2f21f83904afbe" PRIMARY KEY ("_id"))`,
72
+ );
73
+ await queryRunner.query(
74
+ `CREATE INDEX "IDX_af7375987850451a60f0002ae4" ON "UserIncomingCallNumber" ("projectId") `,
75
+ );
76
+ await queryRunner.query(
77
+ `CREATE INDEX "IDX_3b7f38fb56ffd49e972205cb48" ON "UserIncomingCallNumber" ("userId") `,
78
+ );
79
+ await queryRunner.query(
80
+ `CREATE TABLE "IncomingCallPolicyLabel" ("incomingCallPolicyId" uuid NOT NULL, "labelId" uuid NOT NULL, CONSTRAINT "PK_dbe36f4c556e85705e2ff19e4a5" PRIMARY KEY ("incomingCallPolicyId", "labelId"))`,
81
+ );
82
+ await queryRunner.query(
83
+ `CREATE INDEX "IDX_fb7de9bbc395452347629e9131" ON "IncomingCallPolicyLabel" ("incomingCallPolicyId") `,
84
+ );
85
+ await queryRunner.query(
86
+ `CREATE INDEX "IDX_fb6335122b06a052c29d3d6d8b" ON "IncomingCallPolicyLabel" ("labelId") `,
87
+ );
88
+ await queryRunner.query(
89
+ `ALTER TABLE "User" DROP COLUMN "alertPhoneNumber"`,
90
+ );
91
+ await queryRunner.query(
92
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
93
+ );
94
+ await queryRunner.query(
95
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
96
+ );
97
+ await queryRunner.query(
98
+ `ALTER TABLE "IncomingCallPolicy" ADD CONSTRAINT "FK_3c52bf3d9f9aca2ac74848fc0f7" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
99
+ );
100
+ await queryRunner.query(
101
+ `ALTER TABLE "IncomingCallPolicy" ADD CONSTRAINT "FK_08a214983b56f6102a4fdecfa65" FOREIGN KEY ("projectCallSMSConfigId") REFERENCES "ProjectCallSMSConfig"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
102
+ );
103
+ await queryRunner.query(
104
+ `ALTER TABLE "IncomingCallPolicy" ADD CONSTRAINT "FK_fb8001e77d8907b8e089afa6193" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
105
+ );
106
+ await queryRunner.query(
107
+ `ALTER TABLE "IncomingCallPolicy" ADD CONSTRAINT "FK_197d35d96152cbaa101afa670bd" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
108
+ );
109
+ await queryRunner.query(
110
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_3ff7e49f61d10020a7298c3dc64" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
111
+ );
112
+ await queryRunner.query(
113
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_d9358a64a7d4a3f48804844e698" FOREIGN KEY ("incomingCallPolicyId") REFERENCES "IncomingCallPolicy"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
114
+ );
115
+ await queryRunner.query(
116
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_5e2f646a84077a5fd4969ef090b" FOREIGN KEY ("onCallDutyPolicyScheduleId") REFERENCES "OnCallDutyPolicySchedule"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
117
+ );
118
+ await queryRunner.query(
119
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_c0c7ea3cb722a94e074c4486b29" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
120
+ );
121
+ await queryRunner.query(
122
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_c0726b90365656bd869ad821b3b" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
123
+ );
124
+ await queryRunner.query(
125
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" ADD CONSTRAINT "FK_c434cfc9ca11de56e1b5a316559" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
126
+ );
127
+ await queryRunner.query(
128
+ `ALTER TABLE "IncomingCallLog" ADD CONSTRAINT "FK_915f3d5f9bf60430bbd89a1efce" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
129
+ );
130
+ await queryRunner.query(
131
+ `ALTER TABLE "IncomingCallLog" ADD CONSTRAINT "FK_18e2254401cd580a906808d6908" FOREIGN KEY ("incomingCallPolicyId") REFERENCES "IncomingCallPolicy"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
132
+ );
133
+ await queryRunner.query(
134
+ `ALTER TABLE "IncomingCallLog" ADD CONSTRAINT "FK_fb2531b7d8c44e4cad026fb8662" FOREIGN KEY ("answeredByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
135
+ );
136
+ await queryRunner.query(
137
+ `ALTER TABLE "IncomingCallLogItem" ADD CONSTRAINT "FK_4d0e67775a87ffdf8b1413d5bcb" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
138
+ );
139
+ await queryRunner.query(
140
+ `ALTER TABLE "IncomingCallLogItem" ADD CONSTRAINT "FK_d11a949952998081bed04512a77" FOREIGN KEY ("incomingCallLogId") REFERENCES "IncomingCallLog"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
141
+ );
142
+ await queryRunner.query(
143
+ `ALTER TABLE "IncomingCallLogItem" ADD CONSTRAINT "FK_b1fbb2661c86de72ffd61f807b9" FOREIGN KEY ("incomingCallPolicyEscalationRuleId") REFERENCES "IncomingCallPolicyEscalationRule"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
144
+ );
145
+ await queryRunner.query(
146
+ `ALTER TABLE "IncomingCallLogItem" ADD CONSTRAINT "FK_cb196303d93ee4044d0a4f076e5" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
147
+ );
148
+ await queryRunner.query(
149
+ `ALTER TABLE "UserIncomingCallNumber" ADD CONSTRAINT "FK_af7375987850451a60f0002ae43" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
150
+ );
151
+ await queryRunner.query(
152
+ `ALTER TABLE "UserIncomingCallNumber" ADD CONSTRAINT "FK_3b7f38fb56ffd49e972205cb483" FOREIGN KEY ("userId") REFERENCES "User"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
153
+ );
154
+ await queryRunner.query(
155
+ `ALTER TABLE "UserIncomingCallNumber" ADD CONSTRAINT "FK_4565a75d2ff8c3f363ac2a9c056" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
156
+ );
157
+ await queryRunner.query(
158
+ `ALTER TABLE "UserIncomingCallNumber" ADD CONSTRAINT "FK_cab048905c4eb8f87aff75feaa7" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
159
+ );
160
+ await queryRunner.query(
161
+ `ALTER TABLE "IncomingCallPolicyLabel" ADD CONSTRAINT "FK_fb7de9bbc395452347629e91318" FOREIGN KEY ("incomingCallPolicyId") REFERENCES "IncomingCallPolicy"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
162
+ );
163
+ await queryRunner.query(
164
+ `ALTER TABLE "IncomingCallPolicyLabel" ADD CONSTRAINT "FK_fb6335122b06a052c29d3d6d8b5" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
165
+ );
166
+ }
167
+
168
+ public async down(queryRunner: QueryRunner): Promise<void> {
169
+ await queryRunner.query(
170
+ `ALTER TABLE "IncomingCallPolicyLabel" DROP CONSTRAINT "FK_fb6335122b06a052c29d3d6d8b5"`,
171
+ );
172
+ await queryRunner.query(
173
+ `ALTER TABLE "IncomingCallPolicyLabel" DROP CONSTRAINT "FK_fb7de9bbc395452347629e91318"`,
174
+ );
175
+ await queryRunner.query(
176
+ `ALTER TABLE "UserIncomingCallNumber" DROP CONSTRAINT "FK_cab048905c4eb8f87aff75feaa7"`,
177
+ );
178
+ await queryRunner.query(
179
+ `ALTER TABLE "UserIncomingCallNumber" DROP CONSTRAINT "FK_4565a75d2ff8c3f363ac2a9c056"`,
180
+ );
181
+ await queryRunner.query(
182
+ `ALTER TABLE "UserIncomingCallNumber" DROP CONSTRAINT "FK_3b7f38fb56ffd49e972205cb483"`,
183
+ );
184
+ await queryRunner.query(
185
+ `ALTER TABLE "UserIncomingCallNumber" DROP CONSTRAINT "FK_af7375987850451a60f0002ae43"`,
186
+ );
187
+ await queryRunner.query(
188
+ `ALTER TABLE "IncomingCallLogItem" DROP CONSTRAINT "FK_cb196303d93ee4044d0a4f076e5"`,
189
+ );
190
+ await queryRunner.query(
191
+ `ALTER TABLE "IncomingCallLogItem" DROP CONSTRAINT "FK_b1fbb2661c86de72ffd61f807b9"`,
192
+ );
193
+ await queryRunner.query(
194
+ `ALTER TABLE "IncomingCallLogItem" DROP CONSTRAINT "FK_d11a949952998081bed04512a77"`,
195
+ );
196
+ await queryRunner.query(
197
+ `ALTER TABLE "IncomingCallLogItem" DROP CONSTRAINT "FK_4d0e67775a87ffdf8b1413d5bcb"`,
198
+ );
199
+ await queryRunner.query(
200
+ `ALTER TABLE "IncomingCallLog" DROP CONSTRAINT "FK_fb2531b7d8c44e4cad026fb8662"`,
201
+ );
202
+ await queryRunner.query(
203
+ `ALTER TABLE "IncomingCallLog" DROP CONSTRAINT "FK_18e2254401cd580a906808d6908"`,
204
+ );
205
+ await queryRunner.query(
206
+ `ALTER TABLE "IncomingCallLog" DROP CONSTRAINT "FK_915f3d5f9bf60430bbd89a1efce"`,
207
+ );
208
+ await queryRunner.query(
209
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_c434cfc9ca11de56e1b5a316559"`,
210
+ );
211
+ await queryRunner.query(
212
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_c0726b90365656bd869ad821b3b"`,
213
+ );
214
+ await queryRunner.query(
215
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_c0c7ea3cb722a94e074c4486b29"`,
216
+ );
217
+ await queryRunner.query(
218
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_5e2f646a84077a5fd4969ef090b"`,
219
+ );
220
+ await queryRunner.query(
221
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_d9358a64a7d4a3f48804844e698"`,
222
+ );
223
+ await queryRunner.query(
224
+ `ALTER TABLE "IncomingCallPolicyEscalationRule" DROP CONSTRAINT "FK_3ff7e49f61d10020a7298c3dc64"`,
225
+ );
226
+ await queryRunner.query(
227
+ `ALTER TABLE "IncomingCallPolicy" DROP CONSTRAINT "FK_197d35d96152cbaa101afa670bd"`,
228
+ );
229
+ await queryRunner.query(
230
+ `ALTER TABLE "IncomingCallPolicy" DROP CONSTRAINT "FK_fb8001e77d8907b8e089afa6193"`,
231
+ );
232
+ await queryRunner.query(
233
+ `ALTER TABLE "IncomingCallPolicy" DROP CONSTRAINT "FK_08a214983b56f6102a4fdecfa65"`,
234
+ );
235
+ await queryRunner.query(
236
+ `ALTER TABLE "IncomingCallPolicy" DROP CONSTRAINT "FK_3c52bf3d9f9aca2ac74848fc0f7"`,
237
+ );
238
+ await queryRunner.query(
239
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
240
+ );
241
+ await queryRunner.query(
242
+ `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
243
+ );
244
+ await queryRunner.query(
245
+ `ALTER TABLE "User" ADD "alertPhoneNumber" character varying(30)`,
246
+ );
247
+ await queryRunner.query(
248
+ `DROP INDEX "public"."IDX_fb6335122b06a052c29d3d6d8b"`,
249
+ );
250
+ await queryRunner.query(
251
+ `DROP INDEX "public"."IDX_fb7de9bbc395452347629e9131"`,
252
+ );
253
+ await queryRunner.query(`DROP TABLE "IncomingCallPolicyLabel"`);
254
+ await queryRunner.query(
255
+ `DROP INDEX "public"."IDX_3b7f38fb56ffd49e972205cb48"`,
256
+ );
257
+ await queryRunner.query(
258
+ `DROP INDEX "public"."IDX_af7375987850451a60f0002ae4"`,
259
+ );
260
+ await queryRunner.query(`DROP TABLE "UserIncomingCallNumber"`);
261
+ await queryRunner.query(
262
+ `DROP INDEX "public"."IDX_c2f339131713708082a01cd880"`,
263
+ );
264
+ await queryRunner.query(
265
+ `DROP INDEX "public"."IDX_5ecd45b8e5c05322459b01f37e"`,
266
+ );
267
+ await queryRunner.query(
268
+ `DROP INDEX "public"."IDX_cb196303d93ee4044d0a4f076e"`,
269
+ );
270
+ await queryRunner.query(
271
+ `DROP INDEX "public"."IDX_d11a949952998081bed04512a7"`,
272
+ );
273
+ await queryRunner.query(
274
+ `DROP INDEX "public"."IDX_4d0e67775a87ffdf8b1413d5bc"`,
275
+ );
276
+ await queryRunner.query(`DROP TABLE "IncomingCallLogItem"`);
277
+ await queryRunner.query(
278
+ `DROP INDEX "public"."IDX_0821fb47819572b56836b06102"`,
279
+ );
280
+ await queryRunner.query(
281
+ `DROP INDEX "public"."IDX_bac28bb0cfff30703cb79e4a9c"`,
282
+ );
283
+ await queryRunner.query(
284
+ `DROP INDEX "public"."IDX_4769731b4e40dd238410b47337"`,
285
+ );
286
+ await queryRunner.query(
287
+ `DROP INDEX "public"."IDX_18e2254401cd580a906808d690"`,
288
+ );
289
+ await queryRunner.query(
290
+ `DROP INDEX "public"."IDX_915f3d5f9bf60430bbd89a1efc"`,
291
+ );
292
+ await queryRunner.query(`DROP TABLE "IncomingCallLog"`);
293
+ await queryRunner.query(
294
+ `DROP INDEX "public"."IDX_2940b1f9392441bcff1a33ebc4"`,
295
+ );
296
+ await queryRunner.query(
297
+ `DROP INDEX "public"."IDX_d9358a64a7d4a3f48804844e69"`,
298
+ );
299
+ await queryRunner.query(
300
+ `DROP INDEX "public"."IDX_3ff7e49f61d10020a7298c3dc6"`,
301
+ );
302
+ await queryRunner.query(`DROP TABLE "IncomingCallPolicyEscalationRule"`);
303
+ await queryRunner.query(
304
+ `DROP INDEX "public"."IDX_6680fc89e547525f8051bb2599"`,
305
+ );
306
+ await queryRunner.query(
307
+ `DROP INDEX "public"."IDX_5242e2cffd7f4050e6189e569d"`,
308
+ );
309
+ await queryRunner.query(
310
+ `DROP INDEX "public"."IDX_f2f54dca4c8c0bbdea4c37edb2"`,
311
+ );
312
+ await queryRunner.query(
313
+ `DROP INDEX "public"."IDX_3c52bf3d9f9aca2ac74848fc0f"`,
314
+ );
315
+ await queryRunner.query(`DROP TABLE "IncomingCallPolicy"`);
316
+ }
317
+ }
@@ -222,9 +222,7 @@ import { AddOAuthProviderType1768217403078 } from "./1768217403078-AddOAuthProvi
222
222
  import { AddIncomingEmailMonitor1768335589018 } from "./1768335589018-AddIncomingEmailMonitor";
223
223
  import { MigrationName1768422356713 } from "./1768422356713-MigrationName";
224
224
  import { MigrationName1768583966447 } from "./1768583966447-MigrationName";
225
- import { RemoveAlertPhoneNumberFromUser1768647802022 } from "./1768647802022-RemoveAlertPhoneNumberFromUser";
226
- import { MigrationName1768649699509 } from "./1768649699509-MigrationName";
227
- import { MigrationName1768682071562 } from "./1768682071562-MigrationName";
225
+ import { MigrationName1768825402472 } from "./1768825402472-MigrationName";
228
226
 
229
227
  export default [
230
228
  InitialMigration,
@@ -451,7 +449,5 @@ export default [
451
449
  AddIncomingEmailMonitor1768335589018,
452
450
  MigrationName1768422356713,
453
451
  MigrationName1768583966447,
454
- RemoveAlertPhoneNumberFromUser1768647802022,
455
- MigrationName1768649699509,
456
- MigrationName1768682071562,
452
+ MigrationName1768825402472,
457
453
  ];
@@ -59,7 +59,12 @@ import Text from "../../Types/Text";
59
59
  import Typeof from "../../Types/Typeof";
60
60
  import API from "../../Utils/API";
61
61
  import Slug from "../../Utils/Slug";
62
- import { DataSource, Repository, SelectQueryBuilder } from "typeorm";
62
+ import {
63
+ DataSource,
64
+ EntityManager,
65
+ Repository,
66
+ SelectQueryBuilder,
67
+ } from "typeorm";
63
68
  import { FindWhere } from "../../Types/BaseDatabase/Query";
64
69
  import Realtime from "../Utils/Realtime";
65
70
  import ModelEventType from "../../Types/Realtime/ModelEventType";
@@ -129,6 +134,22 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
129
134
  throw new DatabaseNotConnectedException();
130
135
  }
131
136
 
137
+ public async executeTransaction<TResult>(
138
+ runInTransaction: (entityManager: EntityManager) => Promise<TResult>,
139
+ ): Promise<TResult> {
140
+ if (!PostgresAppInstance.isConnected()) {
141
+ throw new DatabaseNotConnectedException();
142
+ }
143
+
144
+ const dataSource: DataSource | null = PostgresAppInstance.getDataSource();
145
+
146
+ if (!dataSource) {
147
+ throw new DatabaseNotConnectedException();
148
+ }
149
+
150
+ return await dataSource.transaction(runInTransaction);
151
+ }
152
+
132
153
  protected isValid(data: TBaseModel): boolean {
133
154
  if (!data) {
134
155
  throw new BadDataException("Data cannot be null");
@@ -1691,3 +1712,4 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
1691
1712
  }
1692
1713
 
1693
1714
  export default DatabaseService;
1715
+ export { EntityManager };
@@ -35,10 +35,18 @@ export class Service extends DatabaseService<Model> {
35
35
  createBy.data.domain = new Domain(domain.trim().toLowerCase());
36
36
  }
37
37
 
38
+ /*
39
+ * Prevent setting isVerified during creation, EXCEPT for test domains
40
+ * Test domains can be auto-verified since they are reserved TLDs that can't have real DNS records
41
+ */
38
42
  if (!createBy.props.isRoot && createBy.data.isVerified) {
39
- throw new BadDataException(
40
- "Domain cannot be verified during creation. Please verify the domain after creation. Please set isVerified to false.",
41
- );
43
+ const domainStr: string = createBy.data.domain?.toString() || "";
44
+
45
+ if (!Domain.isTestDomain(domainStr)) {
46
+ throw new BadDataException(
47
+ "Domain cannot be verified during creation. Please verify the domain after creation. Please set isVerified to false.",
48
+ );
49
+ }
42
50
  }
43
51
 
44
52
  createBy.data.domainVerificationText =
@@ -96,19 +104,22 @@ export class Service extends DatabaseService<Model> {
96
104
  );
97
105
  }
98
106
 
99
- const isVerified: boolean = await Domain.verifyTxtRecord(
100
- domain,
101
- verificationText,
102
- );
103
-
104
- if (!isVerified) {
105
- throw new BadDataException(
106
- "Verification TXT record " +
107
- verificationText +
108
- " not found in domain " +
109
- domain +
110
- ". Please add a TXT record to verify the domain. If you have already added the TXT record, please wait for few hours to let DNS to propagate.",
107
+ // Skip DNS verification for test domains (reserved TLDs for testing)
108
+ if (!Domain.isTestDomain(domain)) {
109
+ const isVerified: boolean = await Domain.verifyTxtRecord(
110
+ domain,
111
+ verificationText,
111
112
  );
113
+
114
+ if (!isVerified) {
115
+ throw new BadDataException(
116
+ "Verification TXT record " +
117
+ verificationText +
118
+ " not found in domain " +
119
+ domain +
120
+ ". Please add a TXT record to verify the domain. If you have already added the TXT record, please wait for few hours to let DNS to propagate.",
121
+ );
122
+ }
112
123
  }
113
124
  }
114
125
  }
@@ -1,7 +1,7 @@
1
1
  import ObjectID from "../../Types/ObjectID";
2
2
  import CreateBy from "../Types/Database/CreateBy";
3
3
  import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
4
- import DatabaseService from "./DatabaseService";
4
+ import DatabaseService, { EntityManager } from "./DatabaseService";
5
5
  import OneUptimeDate from "../../Types/Date";
6
6
  import BadDataException from "../../Types/Exception/BadDataException";
7
7
  import MonitorProbe from "../../Models/DatabaseModels/MonitorProbe";
@@ -69,6 +69,101 @@ export class Service extends DatabaseService<MonitorProbe> {
69
69
  }
70
70
  }
71
71
 
72
+ /**
73
+ * Atomically claims monitor probes for a specific probe instance.
74
+ * Uses PostgreSQL's FOR UPDATE SKIP LOCKED to prevent multiple probe instances
75
+ * from picking up the same monitors simultaneously.
76
+ *
77
+ * @param data - Object containing probeId, limit, and nextPingAt
78
+ * @returns Array of claimed MonitorProbe IDs
79
+ */
80
+ public async claimMonitorProbesForProbing(data: {
81
+ probeId: ObjectID;
82
+ limit: number;
83
+ }): Promise<Array<ObjectID>> {
84
+ const currentDate: Date = OneUptimeDate.getCurrentDate();
85
+
86
+ /*
87
+ * Use a transaction with FOR UPDATE SKIP LOCKED to atomically claim monitors
88
+ * This prevents multiple probe instances from picking up the same monitors
89
+ */
90
+ const claimedIds: Array<ObjectID> = await this.executeTransaction(
91
+ async (transactionalEntityManager: EntityManager) => {
92
+ /*
93
+ * First, select and lock the monitor probes that need to be processed
94
+ * FOR UPDATE SKIP LOCKED ensures that:
95
+ * 1. Rows are locked for this transaction
96
+ * 2. Rows already locked by other transactions are skipped
97
+ */
98
+ const selectQuery: string = `
99
+ SELECT mp."_id"
100
+ FROM "MonitorProbe" mp
101
+ INNER JOIN "Monitor" m ON mp."monitorId" = m."_id"
102
+ INNER JOIN "Project" p ON mp."projectId" = p."_id"
103
+ WHERE mp."probeId" = $1
104
+ AND mp."isEnabled" = true
105
+ AND mp."deletedAt" IS NULL
106
+ AND (mp."nextPingAt" IS NULL OR mp."nextPingAt" <= $2)
107
+ AND m."disableActiveMonitoring" = false
108
+ AND m."disableActiveMonitoringBecauseOfManualIncident" = false
109
+ AND m."disableActiveMonitoringBecauseOfScheduledMaintenanceEvent" = false
110
+ AND m."deletedAt" IS NULL
111
+ AND p."deletedAt" IS NULL
112
+ AND (p."paymentProviderSubscriptionStatus" IS NULL
113
+ OR p."paymentProviderSubscriptionStatus" IN ('active', 'trialing'))
114
+ AND (p."paymentProviderMeteredSubscriptionStatus" IS NULL
115
+ OR p."paymentProviderMeteredSubscriptionStatus" IN ('active', 'trialing'))
116
+ ORDER BY mp."nextPingAt" ASC NULLS FIRST
117
+ LIMIT $3
118
+ FOR UPDATE OF mp SKIP LOCKED
119
+ `;
120
+
121
+ const selectedRows: Array<{ _id: string }> =
122
+ await transactionalEntityManager.query(selectQuery, [
123
+ data.probeId.toString(),
124
+ currentDate,
125
+ data.limit,
126
+ ]);
127
+
128
+ if (selectedRows.length === 0) {
129
+ return [];
130
+ }
131
+
132
+ const ids: Array<string> = selectedRows.map((row: { _id: string }) => {
133
+ return row._id;
134
+ });
135
+
136
+ /*
137
+ * Update the claimed monitors to set nextPingAt to 1 minute from now
138
+ * This is a temporary value; the actual nextPingAt will be calculated
139
+ * based on the monitor's interval after the probe fetches the full details
140
+ */
141
+ const tempNextPingAt: Date = OneUptimeDate.addRemoveMinutes(
142
+ currentDate,
143
+ 1,
144
+ );
145
+
146
+ const updateQuery: string = `
147
+ UPDATE "MonitorProbe"
148
+ SET "lastPingAt" = $1, "nextPingAt" = $2
149
+ WHERE "_id" = ANY($3::uuid[])
150
+ `;
151
+
152
+ await transactionalEntityManager.query(updateQuery, [
153
+ currentDate,
154
+ tempNextPingAt,
155
+ ids,
156
+ ]);
157
+
158
+ return ids.map((id: string) => {
159
+ return new ObjectID(id);
160
+ });
161
+ },
162
+ );
163
+
164
+ return claimedIds;
165
+ }
166
+
72
167
  protected override async onBeforeCreate(
73
168
  createBy: CreateBy<MonitorProbe>,
74
169
  ): Promise<OnCreate<MonitorProbe>> {
package/Types/Domain.ts CHANGED
@@ -5,6 +5,29 @@ import { FindOperator } from "typeorm/find-options/FindOperator";
5
5
  import Zod, { ZodSchema } from "../Utils/Schema/Zod";
6
6
 
7
7
  export default class Domain extends DatabaseProperty {
8
+ /*
9
+ * Reserved TLDs for testing and documentation (per IANA)
10
+ * These domains can never have real DNS records, so they're safe for testing
11
+ */
12
+ public static readonly TEST_DOMAIN_SUFFIXES: string[] = [
13
+ ".example.com",
14
+ ".example.org",
15
+ ".example.net",
16
+ ".test",
17
+ ];
18
+
19
+ /**
20
+ * Checks if a domain is a test/reserved domain that can skip DNS verification.
21
+ * Test domains include .example.com, .example.org, .example.net, and .test TLDs.
22
+ * These are reserved by IANA for documentation and testing purposes.
23
+ */
24
+ public static isTestDomain(domain: string): boolean {
25
+ const domainLower: string = domain.toLowerCase().trim();
26
+ return Domain.TEST_DOMAIN_SUFFIXES.some((suffix: string) => {
27
+ return domainLower.endsWith(suffix);
28
+ });
29
+ }
30
+
8
31
  private _domain: string = "";
9
32
  public get domain(): string {
10
33
  return this._domain;
@@ -307,6 +307,7 @@ __decorate([
307
307
  }),
308
308
  TableColumn({
309
309
  required: true,
310
+ computed: true,
310
311
  type: TableColumnType.ShortText,
311
312
  title: "Full Domain",
312
313
  description: "Full domain of your status page (like status.acmeinc.com). This is autogenerated and is derived from subdomain and domain.",
@@ -425,6 +426,7 @@ __decorate([
425
426
  }),
426
427
  TableColumn({
427
428
  required: true,
429
+ computed: true,
428
430
  type: TableColumnType.ShortText,
429
431
  title: "CNAME Verification Token",
430
432
  description: "CNAME Verification Token",