@oneuptime/common 7.0.4263 → 7.0.4298

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 (75) hide show
  1. package/Models/DatabaseModels/StatusPage.ts +39 -0
  2. package/Server/API/OpenAPI.ts +29 -0
  3. package/Server/API/StatusPageAPI.ts +85 -114
  4. package/Server/EnvironmentConfig.ts +1 -8
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1748456937826-MigrationName.ts +15 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Services/BillingInvoiceService.ts +5 -3
  8. package/Server/Services/DomainService.ts +16 -0
  9. package/Server/Services/StatusPageDomainService.ts +114 -41
  10. package/Server/Services/StatusPageService.ts +89 -24
  11. package/Server/Types/ConfigLogLevel.ts +9 -0
  12. package/Server/Types/Domain.ts +25 -0
  13. package/Server/Utils/Logger.ts +2 -1
  14. package/Server/Utils/OpenAPI.ts +370 -0
  15. package/Tests/Server/API/BaseAPI.test.ts +4 -0
  16. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +7 -6
  17. package/Types/Billing/SubscriptionStatus.ts +1 -0
  18. package/Types/Exception/ExceptionCode.ts +1 -0
  19. package/Types/Exception/ForbiddenException.ts +8 -0
  20. package/Types/IP/IP.ts +88 -0
  21. package/UI/Components/ModelTable/BaseModelTable.tsx +0 -2
  22. package/UI/Utils/API/API.ts +15 -5
  23. package/UI/Utils/Cookie.ts +9 -0
  24. package/UI/Utils/User.ts +1 -0
  25. package/Utils/Schema/ModelSchema.ts +247 -0
  26. package/build/dist/Models/DatabaseModels/StatusPage.js +40 -0
  27. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  28. package/build/dist/Server/API/OpenAPI.js +14 -0
  29. package/build/dist/Server/API/OpenAPI.js.map +1 -0
  30. package/build/dist/Server/API/StatusPageAPI.js +78 -62
  31. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  32. package/build/dist/Server/EnvironmentConfig.js +1 -8
  33. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  34. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1748456937826-MigrationName.js +12 -0
  35. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1748456937826-MigrationName.js.map +1 -0
  36. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  37. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  38. package/build/dist/Server/Services/BillingInvoiceService.js +3 -3
  39. package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
  40. package/build/dist/Server/Services/DomainService.js +13 -0
  41. package/build/dist/Server/Services/DomainService.js.map +1 -1
  42. package/build/dist/Server/Services/StatusPageDomainService.js +87 -30
  43. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  44. package/build/dist/Server/Services/StatusPageService.js +66 -21
  45. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  46. package/build/dist/Server/Types/ConfigLogLevel.js +10 -0
  47. package/build/dist/Server/Types/ConfigLogLevel.js.map +1 -0
  48. package/build/dist/Server/Types/Domain.js +22 -0
  49. package/build/dist/Server/Types/Domain.js.map +1 -1
  50. package/build/dist/Server/Utils/Logger.js +2 -1
  51. package/build/dist/Server/Utils/Logger.js.map +1 -1
  52. package/build/dist/Server/Utils/OpenAPI.js +334 -0
  53. package/build/dist/Server/Utils/OpenAPI.js.map +1 -0
  54. package/build/dist/Tests/Server/API/BaseAPI.test.js +4 -0
  55. package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
  56. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +7 -6
  57. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
  58. package/build/dist/Types/Billing/SubscriptionStatus.js +1 -0
  59. package/build/dist/Types/Billing/SubscriptionStatus.js.map +1 -1
  60. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  61. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  62. package/build/dist/Types/Exception/ForbiddenException.js +8 -0
  63. package/build/dist/Types/Exception/ForbiddenException.js.map +1 -0
  64. package/build/dist/Types/IP/IP.js +66 -0
  65. package/build/dist/Types/IP/IP.js.map +1 -1
  66. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  67. package/build/dist/UI/Utils/API/API.js +11 -5
  68. package/build/dist/UI/Utils/API/API.js.map +1 -1
  69. package/build/dist/UI/Utils/Cookie.js +8 -0
  70. package/build/dist/UI/Utils/Cookie.js.map +1 -1
  71. package/build/dist/UI/Utils/User.js +1 -0
  72. package/build/dist/UI/Utils/User.js.map +1 -1
  73. package/build/dist/Utils/Schema/ModelSchema.js +181 -0
  74. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -0
  75. package/package.json +4 -2
@@ -0,0 +1,370 @@
1
+ import {
2
+ OpenAPIRegistry,
3
+ OpenApiGeneratorV3,
4
+ } from "@asteasolutions/zod-to-openapi";
5
+ import DatabaseBaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
6
+ import Models from "../../Models/DatabaseModels/Index";
7
+ import { JSONObject } from "../../Types/JSON";
8
+
9
+ export default class OpenAPIUtil {
10
+ public static generateOpenAPISpec(): JSONObject {
11
+ const registry: OpenAPIRegistry = new OpenAPIRegistry();
12
+
13
+ // Register schemas and paths for all models
14
+ for (const ModelClass of Models) {
15
+ const model: DatabaseBaseModel = new ModelClass();
16
+ const modelName: string = model.constructor.name;
17
+ const basePath: string = `/api/${modelName.toLowerCase()}`;
18
+ // Use a plain object for paths
19
+ const paths: Record<string, Record<string, any>> = {};
20
+
21
+ // List endpoints (POST and GET)
22
+ paths[`${basePath}/get-list`] = {
23
+ post: this.generateListApiSpec({ modelType: ModelClass }),
24
+ get: this.generateListApiSpec({ modelType: ModelClass }),
25
+ };
26
+ // Count endpoint
27
+ paths[`${basePath}/count`] = {
28
+ post: this.generateCountApiSpec({ modelType: ModelClass }),
29
+ };
30
+ // Create endpoint
31
+ paths[basePath] = {
32
+ post: this.generateCreateApiSpec({ modelType: ModelClass }),
33
+ };
34
+ // Get item endpoints (POST and GET)
35
+ paths[`${basePath}/{id}/get-item`] = {
36
+ post: this.generateGetApiSpec({ modelType: ModelClass }),
37
+ get: this.generateGetApiSpec({ modelType: ModelClass }),
38
+ };
39
+ // Update endpoints (PUT, POST, GET)
40
+ if (!paths[`${basePath}/{id}`]) {
41
+ paths[`${basePath}/{id}`] = {};
42
+ }
43
+
44
+ paths[`${basePath}/{id}`]!["put"] = this.generateUpdateApiSpec({
45
+ modelType: ModelClass,
46
+ });
47
+ paths[`${basePath}/{id}/update-item`] = {
48
+ post: this.generateUpdateApiSpec({ modelType: ModelClass }),
49
+ get: this.generateUpdateApiSpec({ modelType: ModelClass }),
50
+ };
51
+ // Delete endpoints (DELETE, POST, GET)
52
+ if (!paths[`${basePath}/{id}`]) {
53
+ paths[`${basePath}/{id}`] = {};
54
+ }
55
+ paths[`${basePath}/{id}`]!["delete"] = this.generateDeleteApiSpec({
56
+ modelType: ModelClass,
57
+ });
58
+ paths[`${basePath}/{id}/delete-item`] = {
59
+ post: this.generateDeleteApiSpec({ modelType: ModelClass }),
60
+ get: this.generateDeleteApiSpec({ modelType: ModelClass }),
61
+ };
62
+
63
+ // Register the paths in the registry
64
+ for (const path in paths) {
65
+ if (paths[path]) {
66
+ const methods: Record<string, any> | undefined = paths[path];
67
+ if (typeof methods === "object" && methods !== null) {
68
+ for (const method in methods) {
69
+ if (methods[method]) {
70
+ const spec: any = methods[method];
71
+ registry.registerPath({
72
+ method: method as any,
73
+ path,
74
+ ...spec,
75
+ });
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ const generator: OpenApiGeneratorV3 = new OpenApiGeneratorV3(
84
+ registry.definitions,
85
+ );
86
+
87
+ const components: Pick<any, "components"> = generator.generateComponents();
88
+
89
+ return {
90
+ openapi: "3.0.0",
91
+ info: {
92
+ title: "API Documentation",
93
+ version: "1.0.0",
94
+ description: "API documentation generated from models",
95
+ },
96
+ components: components,
97
+ } as unknown as JSONObject;
98
+ }
99
+
100
+ public static generateListApiSpec(data: {
101
+ modelType: new () => DatabaseBaseModel;
102
+ }): JSONObject {
103
+ const modelType: new () => DatabaseBaseModel = data.modelType;
104
+ const model: DatabaseBaseModel = new modelType();
105
+ return {
106
+ summary: `List ${model.constructor.name}`,
107
+ description: `Endpoint to list all ${model.constructor.name} items`,
108
+ requestBody: {
109
+ required: false,
110
+ content: {
111
+ "application/json": {
112
+ schema: {
113
+ type: "object",
114
+ properties: {
115
+ query: { type: "object" },
116
+ select: { type: "object" },
117
+ sort: { type: "object" },
118
+ groupBy: { type: "object" },
119
+ },
120
+ },
121
+ },
122
+ },
123
+ },
124
+ responses: {
125
+ "200": {
126
+ description: "Successful response",
127
+ content: {
128
+ "application/json": {
129
+ schema: {
130
+ type: "object",
131
+ properties: {
132
+ data: {
133
+ type: "array",
134
+ items: {
135
+ $ref: `#/components/schemas/${model.constructor.name}`,
136
+ },
137
+ },
138
+ count: { type: "number" },
139
+ },
140
+ },
141
+ },
142
+ },
143
+ },
144
+ },
145
+ };
146
+ }
147
+
148
+ public static generateCountApiSpec(data: {
149
+ modelType: new () => DatabaseBaseModel;
150
+ }): JSONObject {
151
+ const modelType: new () => DatabaseBaseModel = data.modelType;
152
+ const model: DatabaseBaseModel = new modelType();
153
+ return {
154
+ summary: `Count ${model.constructor.name}`,
155
+ description: `Endpoint to count ${model.constructor.name} items`,
156
+ requestBody: {
157
+ required: false,
158
+ content: {
159
+ "application/json": {
160
+ schema: {
161
+ type: "object",
162
+ properties: {
163
+ query: { type: "object" },
164
+ },
165
+ },
166
+ },
167
+ },
168
+ },
169
+ responses: {
170
+ "200": {
171
+ description: "Successful response",
172
+ content: {
173
+ "application/json": {
174
+ schema: {
175
+ type: "object",
176
+ properties: {
177
+ count: { type: "number" },
178
+ },
179
+ },
180
+ },
181
+ },
182
+ },
183
+ },
184
+ };
185
+ }
186
+
187
+ public static generateCreateApiSpec(data: {
188
+ modelType: new () => DatabaseBaseModel;
189
+ }): JSONObject {
190
+ const modelType: new () => DatabaseBaseModel = data.modelType;
191
+ const model: DatabaseBaseModel = new modelType();
192
+ return {
193
+ summary: `Create ${model.constructor.name}`,
194
+ description: `Endpoint to create a new ${model.constructor.name}`,
195
+ requestBody: {
196
+ required: true,
197
+ content: {
198
+ "application/json": {
199
+ schema: {
200
+ type: "object",
201
+ properties: {
202
+ data: {
203
+ $ref: `#/components/schemas/${model.constructor.name}Input`,
204
+ },
205
+ miscDataProps: { type: "object" },
206
+ },
207
+ required: ["data"],
208
+ },
209
+ },
210
+ },
211
+ },
212
+ responses: {
213
+ "201": {
214
+ description: "Created successfully",
215
+ content: {
216
+ "application/json": {
217
+ schema: {
218
+ type: "object",
219
+ properties: {
220
+ data: {
221
+ $ref: `#/components/schemas/${model.constructor.name}`,
222
+ },
223
+ },
224
+ },
225
+ },
226
+ },
227
+ },
228
+ "400": {
229
+ description: "Bad request",
230
+ },
231
+ },
232
+ };
233
+ }
234
+
235
+ public static generateGetApiSpec(data: {
236
+ modelType: new () => DatabaseBaseModel;
237
+ }): JSONObject {
238
+ const modelType: new () => DatabaseBaseModel = data.modelType;
239
+ const model: DatabaseBaseModel = new modelType();
240
+ return {
241
+ summary: `Get ${model.constructor.name}`,
242
+ description: `Endpoint to retrieve a single ${model.constructor.name} by ID`,
243
+ parameters: [
244
+ {
245
+ name: "id",
246
+ in: "path",
247
+ required: true,
248
+ schema: {
249
+ type: "string",
250
+ format: "uuid",
251
+ },
252
+ description: `ID of the ${model.constructor.name} to retrieve`,
253
+ },
254
+ ],
255
+ responses: {
256
+ "200": {
257
+ description: "Successful response",
258
+ content: {
259
+ "application/json": {
260
+ schema: {
261
+ type: "object",
262
+ properties: {
263
+ data: {
264
+ $ref: `#/components/schemas/${model.constructor.name}`,
265
+ },
266
+ },
267
+ },
268
+ },
269
+ },
270
+ },
271
+ "404": {
272
+ description: "Resource not found",
273
+ },
274
+ },
275
+ };
276
+ }
277
+
278
+ public static generateUpdateApiSpec(data: {
279
+ modelType: new () => DatabaseBaseModel;
280
+ }): JSONObject {
281
+ const modelType: new () => DatabaseBaseModel = data.modelType;
282
+ const model: DatabaseBaseModel = new modelType();
283
+ return {
284
+ summary: `Update ${model.constructor.name}`,
285
+ description: `Endpoint to update an existing ${model.constructor.name}`,
286
+ parameters: [
287
+ {
288
+ name: "id",
289
+ in: "path",
290
+ required: true,
291
+ schema: {
292
+ type: "string",
293
+ format: "uuid",
294
+ },
295
+ description: `ID of the ${model.constructor.name} to update`,
296
+ },
297
+ ],
298
+ requestBody: {
299
+ required: true,
300
+ content: {
301
+ "application/json": {
302
+ schema: {
303
+ type: "object",
304
+ properties: {
305
+ data: {
306
+ $ref: `#/components/schemas/${model.constructor.name}UpdateInput`,
307
+ },
308
+ },
309
+ required: ["data"],
310
+ },
311
+ },
312
+ },
313
+ },
314
+ responses: {
315
+ "200": {
316
+ description: "Updated successfully",
317
+ content: {
318
+ "application/json": {
319
+ schema: {
320
+ type: "object",
321
+ properties: {
322
+ data: {
323
+ $ref: `#/components/schemas/${model.constructor.name}`,
324
+ },
325
+ },
326
+ },
327
+ },
328
+ },
329
+ },
330
+ "404": {
331
+ description: "Resource not found",
332
+ },
333
+ "400": {
334
+ description: "Bad request",
335
+ },
336
+ },
337
+ };
338
+ }
339
+
340
+ public static generateDeleteApiSpec(data: {
341
+ modelType: new () => DatabaseBaseModel;
342
+ }): JSONObject {
343
+ const modelType: new () => DatabaseBaseModel = data.modelType;
344
+ const model: DatabaseBaseModel = new modelType();
345
+ return {
346
+ summary: `Delete ${model.constructor.name}`,
347
+ description: `Endpoint to delete a ${model.constructor.name}`,
348
+ parameters: [
349
+ {
350
+ name: "id",
351
+ in: "path",
352
+ required: true,
353
+ schema: {
354
+ type: "string",
355
+ format: "uuid",
356
+ },
357
+ description: `ID of the ${model.constructor.name} to delete`,
358
+ },
359
+ ],
360
+ responses: {
361
+ "204": {
362
+ description: "Deleted successfully",
363
+ },
364
+ "404": {
365
+ description: "Resource not found",
366
+ },
367
+ },
368
+ };
369
+ }
370
+ }
@@ -25,6 +25,7 @@ import { UserPermission } from "../../../Types/Permission";
25
25
  import PositiveNumber from "../../../Types/PositiveNumber";
26
26
  import UserType from "../../../Types/UserType";
27
27
  import getJestMockFunction, { MockFunction } from "../../MockType";
28
+ import ConfigLogLevel from "../../../Server/Types/ConfigLogLevel";
28
29
 
29
30
  jest.mock("../../../Server/Utils/Express", () => {
30
31
  return {
@@ -104,6 +105,9 @@ jest.mock("../../../Server/Services/ProjectService", () => {
104
105
  jest.mock("../../../Server/EnvironmentConfig", () => {
105
106
  return {
106
107
  IsBillingEnabled: true,
108
+ LogLevel: ConfigLogLevel.INFO, // Or any other appropriate default for tests
109
+ ConfigLogLevel: ConfigLogLevel,
110
+ DisableTelemetry: true,
107
111
  };
108
112
  });
109
113
 
@@ -342,12 +342,13 @@ describe("StatementGenerator", () => {
342
342
  /* eslint-disable prettier/prettier */
343
343
  const expectedStatement: Statement = SQL`
344
344
  CREATE TABLE IF NOT EXISTS ${'oneuptime'}.${'<table-name>'}
345
- (
346
- <columns-create-statement>
347
- )
348
- ENGINE = MergeTree
349
- PRIMARY KEY (${'column_ObjectID'})
350
- ORDER BY (${'column_ObjectID'})
345
+ (
346
+ <columns-create-statement>
347
+ )
348
+ ENGINE = MergeTree
349
+ PARTITION BY (column_ObjectID)
350
+ PRIMARY KEY (${'column_ObjectID'})
351
+ ORDER BY (${'column_ObjectID'})
351
352
  `;
352
353
  /* eslint-enable prettier/prettier */
353
354
 
@@ -6,6 +6,7 @@ enum SubscriptionStatus {
6
6
  PastDue = "past_due",
7
7
  Canceled = "canceled",
8
8
  Unpaid = "unpaid",
9
+ Expired = "expired",
9
10
  }
10
11
 
11
12
  export class SubscriptionStatusUtil {
@@ -7,6 +7,7 @@ enum ExceptionCode {
7
7
  WebRequestException = 6,
8
8
  BadDataException = 400,
9
9
  BadRequestException = 400,
10
+ ForbiddenException = 403,
10
11
  UnabletoReachServerException = 415,
11
12
  ServerException = 500,
12
13
  NotAuthorizedException = 403,
@@ -0,0 +1,8 @@
1
+ import Exception from "./Exception";
2
+ import ExceptionCode from "./ExceptionCode";
3
+
4
+ export default class ForbiddenException extends Exception {
5
+ public constructor(message: string) {
6
+ super(ExceptionCode.ForbiddenException, message);
7
+ }
8
+ }
package/Types/IP/IP.ts CHANGED
@@ -11,6 +11,94 @@ export default class IP extends DatabaseProperty {
11
11
  public get ip(): string {
12
12
  return this._ip;
13
13
  }
14
+
15
+ public static isInWhitelist(data: {
16
+ ips: Array<string>;
17
+ whitelist: string[];
18
+ }): boolean {
19
+ for (const ip of data.ips) {
20
+ // If whitelist is empty, return false
21
+ if (!data.whitelist || data.whitelist.length === 0) {
22
+ return false;
23
+ }
24
+
25
+ // Check if IP is valid
26
+ if (!IP.isIP(ip)) {
27
+ throw new BadDataException("Invalid IP address");
28
+ }
29
+
30
+ // Check each whitelist entry
31
+ for (const entry of data.whitelist) {
32
+ // Skip empty entries
33
+ if (!entry || entry.trim() === "") {
34
+ continue;
35
+ }
36
+
37
+ // Direct IP match
38
+ if (entry === ip) {
39
+ return true;
40
+ }
41
+
42
+ // CIDR notation check (IPv4 only for now)
43
+ if (entry.includes("/") && IP.isIPv4(ip)) {
44
+ try {
45
+ const [network, prefixStr] = entry.split("/");
46
+
47
+ if (!network || !prefixStr) {
48
+ continue;
49
+ }
50
+
51
+ if (!IP.isIPv4(network)) {
52
+ continue;
53
+ }
54
+
55
+ const prefix: number = parseInt(prefixStr, 10);
56
+ if (isNaN(prefix) || prefix < 0 || prefix > 32) {
57
+ continue;
58
+ }
59
+
60
+ // Convert IPs to integers for comparison
61
+ const ipInt: number = this._ipv4ToInt(ip);
62
+ const networkInt: number = this._ipv4ToInt(network);
63
+
64
+ // Create mask from prefix
65
+ const mask: number = ~((1 << (32 - prefix)) - 1) >>> 0;
66
+
67
+ // Check if IP is in network
68
+ if ((ipInt & mask) === (networkInt & mask)) {
69
+ return true;
70
+ }
71
+ } catch (error) {
72
+ continue;
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ return false;
79
+ }
80
+
81
+ // Helper method to convert IPv4 to integer
82
+ private static _ipv4ToInt(ip: string): number {
83
+ const octets: number[] = ip.split(".").map(Number);
84
+
85
+ if (
86
+ octets.length !== 4 ||
87
+ octets.some((octet: number) => {
88
+ return isNaN(octet) || octet < 0 || octet > 255;
89
+ })
90
+ ) {
91
+ throw new BadDataException("Invalid IPv4 address");
92
+ }
93
+
94
+ return (
95
+ ((octets[0]! << 24) >>> 0) +
96
+ ((octets[1]! << 16) >>> 0) +
97
+ ((octets[2]! << 8) >>> 0) +
98
+ octets[3]!
99
+ );
100
+ }
101
+
14
102
  public set ip(value: string) {
15
103
  if (IP.isIPv4(value)) {
16
104
  this._ip = value;
@@ -4,8 +4,6 @@ import { GetReactElementFunction } from "../../Types/FunctionTypes";
4
4
  import SelectEntityField from "../../Types/SelectEntityField";
5
5
  import API from "../../Utils/API/API";
6
6
 
7
-
8
-
9
7
  import Query from "../../../Types/BaseDatabase/Query";
10
8
  import GroupBy from "../../../Types/BaseDatabase/GroupBy";
11
9
  import Sort from "../../../Types/BaseDatabase/Sort";
@@ -17,7 +17,6 @@ import {
17
17
  UserTenantAccessPermission,
18
18
  } from "../../../Types/Permission";
19
19
  import API from "../../../Utils/API";
20
- import Cookies from "universal-cookie";
21
20
 
22
21
  class BaseAPI extends API {
23
22
  public constructor(protocol: Protocol, hostname: Hostname, route?: Route) {
@@ -97,16 +96,14 @@ class BaseAPI extends API {
97
96
  ): HTTPErrorResponse | APIException {
98
97
  // 405 Status - Tenant not found. If Project was deleted.
99
98
  // 401 Status - User is not logged in.
99
+ // 403 Status - Forbidden. If the IP address is not whitelisted (for example).
100
100
  if (
101
101
  error instanceof HTTPErrorResponse &&
102
102
  (error.statusCode === 401 || error.statusCode === 405)
103
103
  ) {
104
104
  const loginRoute: Route = this.getLoginRoute();
105
105
 
106
- User.clear();
107
- const cookies: Cookies = new Cookies();
108
- cookies.remove("admin-data", { path: "/" });
109
- cookies.remove("data", { path: "/" });
106
+ User.logout();
110
107
 
111
108
  if (Navigation.getQueryStringByName("token")) {
112
109
  Navigation.navigate(loginRoute.addRouteParam("sso", "true"), {
@@ -119,6 +116,15 @@ class BaseAPI extends API {
119
116
  }
120
117
  }
121
118
 
119
+ if (
120
+ error instanceof HTTPErrorResponse &&
121
+ error.statusCode === 403 &&
122
+ Navigation.getCurrentRoute().toString() !==
123
+ this.getForbiddenRoute().toString()
124
+ ) {
125
+ Navigation.navigate(this.getForbiddenRoute(), { forceNavigate: true });
126
+ }
127
+
122
128
  return error;
123
129
  }
124
130
 
@@ -126,6 +132,10 @@ class BaseAPI extends API {
126
132
  return new Route("/accounts/login");
127
133
  }
128
134
 
135
+ protected static getForbiddenRoute(): Route {
136
+ return new Route("/accounts/forbidden");
137
+ }
138
+
129
139
  public static getFriendlyMessage(
130
140
  err: HTTPErrorResponse | Exception | unknown,
131
141
  ): string {
@@ -9,6 +9,15 @@ import UniversalCookies, { CookieSetOptions } from "universal-cookie";
9
9
  import CookieName from "../../Types/CookieName";
10
10
 
11
11
  export default class Cookie {
12
+ public static clearAllCookies(): void {
13
+ const cookies: UniversalCookies = new UniversalCookies();
14
+
15
+ // Remove all cookies defined in CookieName enum
16
+ Object.values(CookieName).forEach((cookieName: string) => {
17
+ cookies.remove(cookieName, { path: "/" });
18
+ });
19
+ }
20
+
12
21
  public static setItem(
13
22
  key: CookieName | string,
14
23
  value: JSONValue | Email | URL,
package/UI/Utils/User.ts CHANGED
@@ -174,6 +174,7 @@ export default class UserUtil {
174
174
  await API.post(URL.fromString(IDENTITY_URL.toString()).addRoute("/logout"));
175
175
  LocalStorage.clear();
176
176
  SessionStorage.clear();
177
+ Cookie.clearAllCookies();
177
178
  }
178
179
 
179
180
  public static getUtmParams(): Dictionary<string> {