@oneuptime/common 10.0.85 → 10.0.88
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.
- package/Models/DatabaseModels/EnterpriseLicense.ts +54 -0
- package/Models/DatabaseModels/GlobalConfig.ts +51 -0
- package/Server/API/EnterpriseLicenseAPI.ts +83 -0
- package/Server/API/GlobalConfigAPI.ts +59 -0
- package/Server/API/TelemetryAPI.ts +24 -0
- package/Server/EnvironmentConfig.ts +10 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Queue.ts +4 -4
- package/Server/Services/TelemetryAttributeService.ts +37 -3
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +174 -7
- package/Tests/Types/Date.test.ts +46 -0
- package/Types/Date.ts +9 -4
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +60 -21
- package/UI/Components/Dictionary/Dictionary.tsx +188 -26
- package/UI/Components/Dictionary/DictionaryFilterOperator.ts +357 -0
- package/UI/Components/Dictionary/DictionaryOfStrings.tsx +12 -7
- package/UI/Components/EditionLabel/EditionLabel.tsx +224 -10
- package/UI/Components/Filters/FilterViewer.tsx +81 -16
- package/UI/Components/Filters/FiltersForm.tsx +18 -3
- package/UI/Components/Filters/JSONFilter.tsx +11 -2
- package/UI/Components/Filters/Types/Filter.ts +3 -0
- package/UI/Components/Forms/Fields/FormField.tsx +6 -1
- package/UI/Components/Forms/Types/Field.ts +5 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +73 -4
- package/UI/Components/LogsViewer/components/LogSearchBar.tsx +77 -31
- package/UI/Components/LogsViewer/components/LogSearchSuggestions.tsx +44 -1
- package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +7 -5
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +6 -0
- package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +84 -25
- package/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.tsx +44 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +57 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +54 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +64 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +47 -0
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +9 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +3 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +3 -3
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +36 -7
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +135 -5
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Tests/Types/Date.test.js +40 -0
- package/build/dist/Tests/Types/Date.test.js.map +1 -1
- package/build/dist/Types/Date.js +7 -2
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +21 -10
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +109 -16
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js +263 -0
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/DictionaryOfStrings.js +10 -6
- package/build/dist/UI/Components/Dictionary/DictionaryOfStrings.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +124 -6
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +50 -12
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +5 -4
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/Filters/JSONFilter.js +1 -1
- package/build/dist/UI/Components/Filters/JSONFilter.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +54 -5
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +59 -29
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js +10 -2
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +2 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +59 -22
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js +10 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js.map +1 -1
- package/package.json +1 -1
|
@@ -101,4 +101,58 @@ export default class EnterpriseLicense extends BaseModel {
|
|
|
101
101
|
type: ColumnType.Number,
|
|
102
102
|
})
|
|
103
103
|
public annualContractValue?: number = undefined;
|
|
104
|
+
|
|
105
|
+
@ColumnAccessControl({
|
|
106
|
+
create: [],
|
|
107
|
+
read: [],
|
|
108
|
+
update: [],
|
|
109
|
+
})
|
|
110
|
+
@TableColumn({
|
|
111
|
+
required: false,
|
|
112
|
+
type: TableColumnType.Number,
|
|
113
|
+
title: "User Limit",
|
|
114
|
+
description:
|
|
115
|
+
"Maximum number of users allowed under this enterprise license.",
|
|
116
|
+
})
|
|
117
|
+
@Column({
|
|
118
|
+
nullable: true,
|
|
119
|
+
type: ColumnType.Number,
|
|
120
|
+
})
|
|
121
|
+
public userLimit?: number = undefined;
|
|
122
|
+
|
|
123
|
+
@ColumnAccessControl({
|
|
124
|
+
create: [],
|
|
125
|
+
read: [],
|
|
126
|
+
update: [],
|
|
127
|
+
})
|
|
128
|
+
@TableColumn({
|
|
129
|
+
required: false,
|
|
130
|
+
type: TableColumnType.Number,
|
|
131
|
+
title: "Current User Count",
|
|
132
|
+
description:
|
|
133
|
+
"Most recent user count reported by the customer's self-hosted installation.",
|
|
134
|
+
})
|
|
135
|
+
@Column({
|
|
136
|
+
nullable: true,
|
|
137
|
+
type: ColumnType.Number,
|
|
138
|
+
})
|
|
139
|
+
public currentUserCount?: number = undefined;
|
|
140
|
+
|
|
141
|
+
@ColumnAccessControl({
|
|
142
|
+
create: [],
|
|
143
|
+
read: [],
|
|
144
|
+
update: [],
|
|
145
|
+
})
|
|
146
|
+
@TableColumn({
|
|
147
|
+
required: false,
|
|
148
|
+
type: TableColumnType.Date,
|
|
149
|
+
title: "User Count Updated At",
|
|
150
|
+
description:
|
|
151
|
+
"Timestamp of the most recent user count report from the customer's self-hosted installation.",
|
|
152
|
+
})
|
|
153
|
+
@Column({
|
|
154
|
+
nullable: true,
|
|
155
|
+
type: ColumnType.Date,
|
|
156
|
+
})
|
|
157
|
+
public userCountUpdatedAt?: Date = undefined;
|
|
104
158
|
}
|
|
@@ -756,6 +756,57 @@ export default class GlobalConfig extends GlobalConfigModel {
|
|
|
756
756
|
})
|
|
757
757
|
public enterpriseLicenseToken?: string = undefined;
|
|
758
758
|
|
|
759
|
+
@ColumnAccessControl({
|
|
760
|
+
create: [],
|
|
761
|
+
read: [],
|
|
762
|
+
update: [],
|
|
763
|
+
})
|
|
764
|
+
@TableColumn({
|
|
765
|
+
type: TableColumnType.Number,
|
|
766
|
+
title: "Enterprise License User Limit",
|
|
767
|
+
description:
|
|
768
|
+
"Maximum number of users permitted under the validated enterprise license.",
|
|
769
|
+
})
|
|
770
|
+
@Column({
|
|
771
|
+
type: ColumnType.Number,
|
|
772
|
+
nullable: true,
|
|
773
|
+
})
|
|
774
|
+
public enterpriseLicenseUserLimit?: number = undefined;
|
|
775
|
+
|
|
776
|
+
@ColumnAccessControl({
|
|
777
|
+
create: [],
|
|
778
|
+
read: [],
|
|
779
|
+
update: [],
|
|
780
|
+
})
|
|
781
|
+
@TableColumn({
|
|
782
|
+
type: TableColumnType.Number,
|
|
783
|
+
title: "Enterprise License Current User Count",
|
|
784
|
+
description:
|
|
785
|
+
"User count last reported to OneUptime for the validated enterprise license.",
|
|
786
|
+
})
|
|
787
|
+
@Column({
|
|
788
|
+
type: ColumnType.Number,
|
|
789
|
+
nullable: true,
|
|
790
|
+
})
|
|
791
|
+
public enterpriseLicenseCurrentUserCount?: number = undefined;
|
|
792
|
+
|
|
793
|
+
@ColumnAccessControl({
|
|
794
|
+
create: [],
|
|
795
|
+
read: [],
|
|
796
|
+
update: [],
|
|
797
|
+
})
|
|
798
|
+
@TableColumn({
|
|
799
|
+
type: TableColumnType.Date,
|
|
800
|
+
title: "Enterprise License User Count Updated At",
|
|
801
|
+
description:
|
|
802
|
+
"Timestamp of the most recent user count report sent to OneUptime for the validated enterprise license.",
|
|
803
|
+
})
|
|
804
|
+
@Column({
|
|
805
|
+
type: ColumnType.Date,
|
|
806
|
+
nullable: true,
|
|
807
|
+
})
|
|
808
|
+
public enterpriseLicenseUserCountUpdatedAt?: Date = undefined;
|
|
809
|
+
|
|
759
810
|
@ColumnAccessControl({
|
|
760
811
|
create: [],
|
|
761
812
|
read: [],
|
|
@@ -6,6 +6,7 @@ import EnterpriseLicenseService, {
|
|
|
6
6
|
} from "../Services/EnterpriseLicenseService";
|
|
7
7
|
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
8
8
|
import JSONWebToken from "../Utils/JsonWebToken";
|
|
9
|
+
import OneUptimeDate from "../../Types/Date";
|
|
9
10
|
import Response from "../Utils/Response";
|
|
10
11
|
import {
|
|
11
12
|
ExpressRequest,
|
|
@@ -52,6 +53,9 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
52
53
|
companyName: true,
|
|
53
54
|
expiresAt: true,
|
|
54
55
|
licenseKey: true,
|
|
56
|
+
userLimit: true,
|
|
57
|
+
currentUserCount: true,
|
|
58
|
+
userCountUpdatedAt: true,
|
|
55
59
|
},
|
|
56
60
|
props: {
|
|
57
61
|
isRoot: true,
|
|
@@ -80,6 +84,8 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
80
84
|
companyName: license.companyName || "",
|
|
81
85
|
expiresAt: license.expiresAt.toISOString(),
|
|
82
86
|
licenseKey: license.licenseKey || "",
|
|
87
|
+
userLimit:
|
|
88
|
+
typeof license.userLimit === "number" ? license.userLimit : null,
|
|
83
89
|
};
|
|
84
90
|
|
|
85
91
|
const token: string = JSONWebToken.signJsonPayload(
|
|
@@ -91,6 +97,14 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
91
97
|
companyName: payload["companyName"] as string,
|
|
92
98
|
expiresAt: payload["expiresAt"] as string,
|
|
93
99
|
licenseKey: payload["licenseKey"] as string,
|
|
100
|
+
userLimit: payload["userLimit"],
|
|
101
|
+
currentUserCount:
|
|
102
|
+
typeof license.currentUserCount === "number"
|
|
103
|
+
? license.currentUserCount
|
|
104
|
+
: null,
|
|
105
|
+
userCountUpdatedAt: license.userCountUpdatedAt
|
|
106
|
+
? license.userCountUpdatedAt.toISOString()
|
|
107
|
+
: null,
|
|
94
108
|
token,
|
|
95
109
|
});
|
|
96
110
|
} catch (err) {
|
|
@@ -98,5 +112,74 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
|
|
|
98
112
|
}
|
|
99
113
|
},
|
|
100
114
|
);
|
|
115
|
+
|
|
116
|
+
this.router.post(
|
|
117
|
+
`${new this.entityType().getCrudApiPath()?.toString()}/report-user-count`,
|
|
118
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
119
|
+
try {
|
|
120
|
+
const licenseKey: string | undefined = (
|
|
121
|
+
req.body["licenseKey"] as string | undefined
|
|
122
|
+
)?.trim();
|
|
123
|
+
const rawUserCount: unknown = req.body["userCount"];
|
|
124
|
+
|
|
125
|
+
if (!licenseKey) {
|
|
126
|
+
throw new BadDataException("License key is required");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const userCount: number = Number(rawUserCount);
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
!Number.isFinite(userCount) ||
|
|
133
|
+
userCount < 0 ||
|
|
134
|
+
!Number.isInteger(userCount)
|
|
135
|
+
) {
|
|
136
|
+
throw new BadDataException(
|
|
137
|
+
"userCount must be a non-negative integer",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const license: EnterpriseLicense | null =
|
|
142
|
+
await EnterpriseLicenseService.findOneBy({
|
|
143
|
+
query: {
|
|
144
|
+
licenseKey: licenseKey,
|
|
145
|
+
},
|
|
146
|
+
select: {
|
|
147
|
+
_id: true,
|
|
148
|
+
userLimit: true,
|
|
149
|
+
},
|
|
150
|
+
props: {
|
|
151
|
+
isRoot: true,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (!license) {
|
|
156
|
+
throw new BadDataException("License key is invalid");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const reportedAt: Date = OneUptimeDate.getCurrentDate();
|
|
160
|
+
|
|
161
|
+
await EnterpriseLicenseService.updateOneById({
|
|
162
|
+
id: license.id!,
|
|
163
|
+
data: {
|
|
164
|
+
currentUserCount: userCount,
|
|
165
|
+
userCountUpdatedAt: reportedAt,
|
|
166
|
+
},
|
|
167
|
+
props: {
|
|
168
|
+
isRoot: true,
|
|
169
|
+
ignoreHooks: true,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
174
|
+
currentUserCount: userCount,
|
|
175
|
+
userCountUpdatedAt: reportedAt.toISOString(),
|
|
176
|
+
userLimit:
|
|
177
|
+
typeof license.userLimit === "number" ? license.userLimit : null,
|
|
178
|
+
});
|
|
179
|
+
} catch (err) {
|
|
180
|
+
next(err);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
);
|
|
101
184
|
}
|
|
102
185
|
}
|
|
@@ -67,6 +67,9 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
67
67
|
enterpriseLicenseExpiresAt: true,
|
|
68
68
|
enterpriseLicenseKey: true,
|
|
69
69
|
enterpriseLicenseToken: true,
|
|
70
|
+
enterpriseLicenseUserLimit: true,
|
|
71
|
+
enterpriseLicenseCurrentUserCount: true,
|
|
72
|
+
enterpriseLicenseUserCountUpdatedAt: true,
|
|
70
73
|
},
|
|
71
74
|
props: {
|
|
72
75
|
isRoot: true,
|
|
@@ -80,6 +83,17 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
80
83
|
: null,
|
|
81
84
|
licenseKey: config?.enterpriseLicenseKey || null,
|
|
82
85
|
token: config?.enterpriseLicenseToken || null,
|
|
86
|
+
userLimit:
|
|
87
|
+
typeof config?.enterpriseLicenseUserLimit === "number"
|
|
88
|
+
? config.enterpriseLicenseUserLimit
|
|
89
|
+
: null,
|
|
90
|
+
currentUserCount:
|
|
91
|
+
typeof config?.enterpriseLicenseCurrentUserCount === "number"
|
|
92
|
+
? config.enterpriseLicenseCurrentUserCount
|
|
93
|
+
: null,
|
|
94
|
+
userCountUpdatedAt: config?.enterpriseLicenseUserCountUpdatedAt
|
|
95
|
+
? config.enterpriseLicenseUserCountUpdatedAt.toISOString()
|
|
96
|
+
: null,
|
|
83
97
|
};
|
|
84
98
|
|
|
85
99
|
return Response.sendJsonObjectResponse(req, res, responseBody);
|
|
@@ -143,11 +157,38 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
143
157
|
licenseExpiry = parsedDate;
|
|
144
158
|
}
|
|
145
159
|
|
|
160
|
+
const userLimitRaw: unknown = payload["userLimit"];
|
|
161
|
+
const userLimit: number | null =
|
|
162
|
+
typeof userLimitRaw === "number" && Number.isFinite(userLimitRaw)
|
|
163
|
+
? userLimitRaw
|
|
164
|
+
: null;
|
|
165
|
+
|
|
166
|
+
const currentUserCountRaw: unknown = payload["currentUserCount"];
|
|
167
|
+
const currentUserCount: number | null =
|
|
168
|
+
typeof currentUserCountRaw === "number" &&
|
|
169
|
+
Number.isFinite(currentUserCountRaw)
|
|
170
|
+
? currentUserCountRaw
|
|
171
|
+
: null;
|
|
172
|
+
|
|
173
|
+
const userCountUpdatedAtRaw: string | undefined = payload[
|
|
174
|
+
"userCountUpdatedAt"
|
|
175
|
+
] as string | undefined;
|
|
176
|
+
let userCountUpdatedAt: Date | null = null;
|
|
177
|
+
if (userCountUpdatedAtRaw) {
|
|
178
|
+
const parsedReportedAt: Date = new Date(userCountUpdatedAtRaw);
|
|
179
|
+
if (!Number.isNaN(parsedReportedAt.getTime())) {
|
|
180
|
+
userCountUpdatedAt = parsedReportedAt;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
146
184
|
const updatePayload: PartialEntity<GlobalConfig> = {
|
|
147
185
|
enterpriseCompanyName: companyNameRaw || null,
|
|
148
186
|
enterpriseLicenseKey: licenseKeyRaw || null,
|
|
149
187
|
enterpriseLicenseExpiresAt: licenseExpiry || null,
|
|
150
188
|
enterpriseLicenseToken: licenseToken || null,
|
|
189
|
+
enterpriseLicenseUserLimit: userLimit,
|
|
190
|
+
enterpriseLicenseCurrentUserCount: currentUserCount,
|
|
191
|
+
enterpriseLicenseUserCountUpdatedAt: userCountUpdatedAt,
|
|
151
192
|
};
|
|
152
193
|
|
|
153
194
|
const globalConfigId: ObjectID = ObjectID.getZeroObjectID();
|
|
@@ -193,6 +234,19 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
193
234
|
newConfig.enterpriseLicenseExpiresAt = licenseExpiry;
|
|
194
235
|
}
|
|
195
236
|
|
|
237
|
+
if (userLimit !== null) {
|
|
238
|
+
newConfig.enterpriseLicenseUserLimit = userLimit;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (currentUserCount !== null) {
|
|
242
|
+
newConfig.enterpriseLicenseCurrentUserCount = currentUserCount;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (userCountUpdatedAt) {
|
|
246
|
+
newConfig.enterpriseLicenseUserCountUpdatedAt =
|
|
247
|
+
userCountUpdatedAt;
|
|
248
|
+
}
|
|
249
|
+
|
|
196
250
|
await GlobalConfigService.create({
|
|
197
251
|
data: newConfig,
|
|
198
252
|
props: {
|
|
@@ -207,6 +261,11 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
207
261
|
expiresAt: licenseExpiry ? licenseExpiry.toISOString() : null,
|
|
208
262
|
licenseKey: licenseKeyRaw || null,
|
|
209
263
|
token: licenseToken || null,
|
|
264
|
+
userLimit: userLimit,
|
|
265
|
+
currentUserCount: currentUserCount,
|
|
266
|
+
userCountUpdatedAt: userCountUpdatedAt
|
|
267
|
+
? userCountUpdatedAt.toISOString()
|
|
268
|
+
: null,
|
|
210
269
|
});
|
|
211
270
|
} catch (err) {
|
|
212
271
|
next(err);
|
|
@@ -80,6 +80,14 @@ router.post(
|
|
|
80
80
|
},
|
|
81
81
|
);
|
|
82
82
|
|
|
83
|
+
router.post(
|
|
84
|
+
"/telemetry/logs/get-attribute-values",
|
|
85
|
+
UserMiddleware.getUserMiddleware,
|
|
86
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
87
|
+
return getAttributeValues(req, res, next, TelemetryType.Log);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
83
91
|
router.post(
|
|
84
92
|
"/telemetry/traces/get-attributes",
|
|
85
93
|
UserMiddleware.getUserMiddleware,
|
|
@@ -96,6 +104,22 @@ router.post(
|
|
|
96
104
|
},
|
|
97
105
|
);
|
|
98
106
|
|
|
107
|
+
router.post(
|
|
108
|
+
"/telemetry/exceptions/get-attributes",
|
|
109
|
+
UserMiddleware.getUserMiddleware,
|
|
110
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
111
|
+
return getAttributes(req, res, next, TelemetryType.Exception);
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
router.post(
|
|
116
|
+
"/telemetry/exceptions/get-attribute-values",
|
|
117
|
+
UserMiddleware.getUserMiddleware,
|
|
118
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
119
|
+
return getAttributeValues(req, res, next, TelemetryType.Exception);
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
99
123
|
type GetAttributesFunction = (
|
|
100
124
|
req: ExpressRequest,
|
|
101
125
|
res: ExpressResponse,
|
|
@@ -162,6 +162,12 @@ export const ClusterKey: ObjectID = new ObjectID(
|
|
|
162
162
|
|
|
163
163
|
export const HasClusterKey: boolean = Boolean(process.env["ONEUPTIME_SECRET"]);
|
|
164
164
|
|
|
165
|
+
export const EnableQueueDashboard: boolean =
|
|
166
|
+
process.env["ENABLE_QUEUE_DASHBOARD"] === "true";
|
|
167
|
+
|
|
168
|
+
export const QueueDashboardSecret: string =
|
|
169
|
+
process.env["QUEUE_DASHBOARD_SECRET"] || "";
|
|
170
|
+
|
|
165
171
|
export const RegisterProbeKey: ObjectID = new ObjectID(
|
|
166
172
|
process.env["REGISTER_PROBE_KEY"] || "secret",
|
|
167
173
|
);
|
|
@@ -513,6 +519,10 @@ export const EnterpriseLicenseValidationUrl: URL = URL.fromString(
|
|
|
513
519
|
"https://oneuptime.com/api/enterprise-license/validate",
|
|
514
520
|
);
|
|
515
521
|
|
|
522
|
+
export const EnterpriseLicenseUserCountReportUrl: URL = URL.fromString(
|
|
523
|
+
"https://oneuptime.com/api/enterprise-license/report-user-count",
|
|
524
|
+
);
|
|
525
|
+
|
|
516
526
|
// Inbound Email Configuration for Incoming Email Monitor
|
|
517
527
|
export enum InboundEmailProviderType {
|
|
518
528
|
SendGrid = "SendGrid",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1777629313843 implements MigrationInterface {
|
|
4
|
+
public name: string = "MigrationName1777629313843";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseUserLimit" integer`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseCurrentUserCount" integer`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseUserCountUpdatedAt" TIMESTAMP WITH TIME ZONE`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "EnterpriseLicense" ADD "userLimit" integer`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "EnterpriseLicense" ADD "currentUserCount" integer`,
|
|
21
|
+
);
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "EnterpriseLicense" ADD "userCountUpdatedAt" TIMESTAMP WITH TIME ZONE`,
|
|
24
|
+
);
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`,
|
|
39
|
+
);
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "EnterpriseLicense" DROP COLUMN "userCountUpdatedAt"`,
|
|
42
|
+
);
|
|
43
|
+
await queryRunner.query(
|
|
44
|
+
`ALTER TABLE "EnterpriseLicense" DROP COLUMN "currentUserCount"`,
|
|
45
|
+
);
|
|
46
|
+
await queryRunner.query(
|
|
47
|
+
`ALTER TABLE "EnterpriseLicense" DROP COLUMN "userLimit"`,
|
|
48
|
+
);
|
|
49
|
+
await queryRunner.query(
|
|
50
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseUserCountUpdatedAt"`,
|
|
51
|
+
);
|
|
52
|
+
await queryRunner.query(
|
|
53
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseCurrentUserCount"`,
|
|
54
|
+
);
|
|
55
|
+
await queryRunner.query(
|
|
56
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseUserLimit"`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -295,6 +295,7 @@ import { AddTelemetryRetentionSettings1777018175127 } from "./1777018175127-AddT
|
|
|
295
295
|
import { AddMonitorTemplate1777201966799 } from "./1777201966799-AddMonitorTemplate";
|
|
296
296
|
import { MigrationName1777550162848 } from "./1777550162848-MigrationName";
|
|
297
297
|
import { MigrationName1777571961028 } from "./1777571961028-MigrationName";
|
|
298
|
+
import { MigrationName1777629313843 } from "./1777629313843-MigrationName";
|
|
298
299
|
export default [
|
|
299
300
|
InitialMigration,
|
|
300
301
|
MigrationName1717678334852,
|
|
@@ -593,4 +594,5 @@ export default [
|
|
|
593
594
|
AddMonitorTemplate1777201966799,
|
|
594
595
|
MigrationName1777550162848,
|
|
595
596
|
MigrationName1777571961028,
|
|
597
|
+
MigrationName1777629313843,
|
|
596
598
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { QueueDashboardSecret } from "../EnvironmentConfig";
|
|
2
2
|
import Dictionary from "../../Types/Dictionary";
|
|
3
3
|
import { JSONObject } from "../../Types/JSON";
|
|
4
4
|
import { Queue as BullQueue, Job, JobsOptions, RepeatableJob } from "bullmq";
|
|
@@ -153,7 +153,7 @@ export default class Queue {
|
|
|
153
153
|
|
|
154
154
|
@CaptureSpan()
|
|
155
155
|
public static getInspectorRoute(): string {
|
|
156
|
-
return "/worker/inspect/queue/:
|
|
156
|
+
return "/worker/inspect/queue/:dashboardSecret";
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
@CaptureSpan()
|
|
@@ -174,8 +174,8 @@ export default class Queue {
|
|
|
174
174
|
|
|
175
175
|
serverAdapter.setBasePath(
|
|
176
176
|
this.getInspectorRoute().replace(
|
|
177
|
-
"/:
|
|
178
|
-
"/" +
|
|
177
|
+
"/:dashboardSecret",
|
|
178
|
+
"/" + QueueDashboardSecret,
|
|
179
179
|
),
|
|
180
180
|
);
|
|
181
181
|
|
|
@@ -3,6 +3,7 @@ import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
|
|
3
3
|
import LogDatabaseService from "./LogService";
|
|
4
4
|
import MetricDatabaseService from "./MetricService";
|
|
5
5
|
import SpanDatabaseService from "./SpanService";
|
|
6
|
+
import ExceptionInstanceService from "./ExceptionInstanceService";
|
|
6
7
|
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
|
|
7
8
|
import { JSONObject } from "../../Types/JSON";
|
|
8
9
|
import ObjectID from "../../Types/ObjectID";
|
|
@@ -18,7 +19,12 @@ type TelemetrySource = {
|
|
|
18
19
|
service: AnalyticsDatabaseService<any>;
|
|
19
20
|
tableName: string;
|
|
20
21
|
attributesColumn: string;
|
|
21
|
-
|
|
22
|
+
/*
|
|
23
|
+
* Some tables (e.g. ExceptionInstance) don't have a separate
|
|
24
|
+
* attributeKeys array column — only the attributes map. Leave this
|
|
25
|
+
* undefined for those; the SQL falls back to mapKeys(attributes).
|
|
26
|
+
*/
|
|
27
|
+
attributeKeysColumn?: string | undefined;
|
|
22
28
|
timeColumn: string;
|
|
23
29
|
};
|
|
24
30
|
|
|
@@ -62,6 +68,14 @@ export class TelemetryAttributeService {
|
|
|
62
68
|
attributeKeysColumn: "attributeKeys",
|
|
63
69
|
timeColumn: "startTime",
|
|
64
70
|
};
|
|
71
|
+
case TelemetryType.Exception:
|
|
72
|
+
return {
|
|
73
|
+
service: ExceptionInstanceService,
|
|
74
|
+
tableName: ExceptionInstanceService.model.tableName,
|
|
75
|
+
attributesColumn: "attributes",
|
|
76
|
+
// ExceptionInstance has no attributeKeys column.
|
|
77
|
+
timeColumn: "time",
|
|
78
|
+
};
|
|
65
79
|
default:
|
|
66
80
|
return null;
|
|
67
81
|
}
|
|
@@ -225,14 +239,21 @@ export class TelemetryAttributeService {
|
|
|
225
239
|
projectId: ObjectID;
|
|
226
240
|
tableName: string;
|
|
227
241
|
attributesColumn: string;
|
|
228
|
-
attributeKeysColumn
|
|
242
|
+
attributeKeysColumn?: string | undefined;
|
|
229
243
|
timeColumn: string;
|
|
230
244
|
metricName?: string | undefined;
|
|
231
245
|
}): Statement {
|
|
232
246
|
const lookbackStartDate: Date =
|
|
233
247
|
TelemetryAttributeService.getLookbackStartDate();
|
|
234
248
|
|
|
235
|
-
|
|
249
|
+
/*
|
|
250
|
+
* If the source has a denormalized attributeKeys array column, prefer it
|
|
251
|
+
* (avoids materializing every row's map). Otherwise fall back to
|
|
252
|
+
* mapKeys(attributes) — slower but works for tables that don't carry
|
|
253
|
+
* the precomputed array (e.g. ExceptionInstance).
|
|
254
|
+
*/
|
|
255
|
+
const statement: Statement = data.attributeKeysColumn
|
|
256
|
+
? SQL`
|
|
236
257
|
WITH filtered AS (
|
|
237
258
|
SELECT arrayJoin(
|
|
238
259
|
if(
|
|
@@ -250,6 +271,19 @@ export class TelemetryAttributeService {
|
|
|
250
271
|
NOT empty(${data.attributeKeysColumn}) OR
|
|
251
272
|
NOT empty(${data.attributesColumn})
|
|
252
273
|
)
|
|
274
|
+
AND ${data.timeColumn} >= ${{
|
|
275
|
+
type: TableColumnType.Date,
|
|
276
|
+
value: lookbackStartDate,
|
|
277
|
+
}}`
|
|
278
|
+
: SQL`
|
|
279
|
+
WITH filtered AS (
|
|
280
|
+
SELECT arrayJoin(mapKeys(${data.attributesColumn})) AS attribute
|
|
281
|
+
FROM ${data.tableName}
|
|
282
|
+
WHERE projectId = ${{
|
|
283
|
+
type: TableColumnType.ObjectID,
|
|
284
|
+
value: data.projectId,
|
|
285
|
+
}}
|
|
286
|
+
AND NOT empty(${data.attributesColumn})
|
|
253
287
|
AND ${data.timeColumn} >= ${{
|
|
254
288
|
type: TableColumnType.Date,
|
|
255
289
|
value: lookbackStartDate,
|