@oneuptime/common 11.0.4 → 11.0.6
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/Server/Infrastructure/GlobalCache.ts +11 -0
- package/Server/Middleware/UserAuthorization.ts +94 -20
- package/Server/Services/CephClusterService.ts +22 -10
- package/Server/Services/CloudResourceService.ts +22 -10
- package/Server/Services/DockerHostService.ts +22 -10
- package/Server/Services/DockerSwarmClusterService.ts +22 -10
- package/Server/Services/HostService.ts +22 -10
- package/Server/Services/KubernetesClusterService.ts +22 -10
- package/Server/Services/PodmanHostService.ts +22 -10
- package/Server/Services/ProxmoxClusterService.ts +22 -10
- package/Server/Services/RumApplicationService.ts +22 -10
- package/Server/Services/ServerlessFunctionService.ts +22 -10
- package/Server/Utils/Cookie.ts +61 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +43 -0
- package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +151 -0
- package/Types/CookieName.ts +1 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -8
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -8
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +4 -8
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1 -1
- package/UI/Components/Charts/Line/LineChart.tsx +4 -8
- package/UI/Components/EditionLabel/EditionLabel.tsx +68 -16
- package/UI/Components/Modal/Modal.tsx +4 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +13 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +72 -16
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +19 -2
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +19 -2
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -2
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +19 -2
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +19 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +19 -2
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -2
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +19 -2
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -2
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +19 -2
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Utils/Cookie.js +61 -0
- package/build/dist/Server/Utils/Cookie.js.map +1 -1
- package/build/dist/Types/CookieName.js +1 -0
- package/build/dist/Types/CookieName.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -6
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -6
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +8 -8
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -6
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +31 -5
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +1 -1
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/package.json +1 -1
package/Server/Utils/Cookie.ts
CHANGED
|
@@ -98,6 +98,57 @@ export default class CookieUtil {
|
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/*
|
|
102
|
+
* Builds a Global SSO token. Unlike the per-project token, this is NOT bound
|
|
103
|
+
* to a single project — a Global SSO/OIDC login proves the user authenticated
|
|
104
|
+
* against the instance-wide IdP, so one token satisfies SSO enforcement for
|
|
105
|
+
* every project the user belongs to (including projects created after login).
|
|
106
|
+
* It carries the `ssoProviderId` / `ssoProviderType` discriminator so a
|
|
107
|
+
* project that pins a specific provider can still be enforced.
|
|
108
|
+
*/
|
|
109
|
+
@CaptureSpan()
|
|
110
|
+
public static getGlobalSSOToken(data: {
|
|
111
|
+
user: User;
|
|
112
|
+
ssoProviderId: ObjectID;
|
|
113
|
+
ssoProviderType: SsoProviderType;
|
|
114
|
+
}): string {
|
|
115
|
+
const { user } = data;
|
|
116
|
+
|
|
117
|
+
return JSONWebToken.sign({
|
|
118
|
+
data: {
|
|
119
|
+
userId: user.id!,
|
|
120
|
+
name: user.name!,
|
|
121
|
+
email: user.email,
|
|
122
|
+
isMasterAdmin: false,
|
|
123
|
+
isGeneralLogin: false,
|
|
124
|
+
ssoProviderId: data.ssoProviderId.toString(),
|
|
125
|
+
ssoProviderType: data.ssoProviderType.toString(),
|
|
126
|
+
},
|
|
127
|
+
expiresInSeconds: OneUptimeDate.getSecondsInDays(new PositiveNumber(30)),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@CaptureSpan()
|
|
132
|
+
public static setGlobalSSOCookie(data: {
|
|
133
|
+
user: User;
|
|
134
|
+
expressResponse: ExpressResponse;
|
|
135
|
+
ssoProviderId: ObjectID;
|
|
136
|
+
ssoProviderType: SsoProviderType;
|
|
137
|
+
}): void {
|
|
138
|
+
const { expressResponse: res } = data;
|
|
139
|
+
|
|
140
|
+
const globalSsoToken: string = CookieUtil.getGlobalSSOToken({
|
|
141
|
+
user: data.user,
|
|
142
|
+
ssoProviderId: data.ssoProviderId,
|
|
143
|
+
ssoProviderType: data.ssoProviderType,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
CookieUtil.setCookie(res, CookieUtil.getGlobalSSOKey(), globalSsoToken, {
|
|
147
|
+
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
|
148
|
+
httpOnly: true,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
101
152
|
@CaptureSpan()
|
|
102
153
|
public static setUserCookie(data: {
|
|
103
154
|
expressResponse: ExpressResponse;
|
|
@@ -444,6 +495,16 @@ export default class CookieUtil {
|
|
|
444
495
|
return `sso-`;
|
|
445
496
|
}
|
|
446
497
|
|
|
498
|
+
/*
|
|
499
|
+
* Fixed cookie name for the single Global SSO token. Deliberately does NOT
|
|
500
|
+
* start with the per-project `sso-` prefix so the project-scoped cookie
|
|
501
|
+
* parser in UserMiddleware.getSsoTokens never mis-keys it.
|
|
502
|
+
*/
|
|
503
|
+
@CaptureSpan()
|
|
504
|
+
public static getGlobalSSOKey(): string {
|
|
505
|
+
return CookieName.GlobalSSOToken;
|
|
506
|
+
}
|
|
507
|
+
|
|
447
508
|
// delete all cookies.
|
|
448
509
|
@CaptureSpan()
|
|
449
510
|
public static removeAllCookies(
|
|
@@ -17,6 +17,7 @@ type MockClient = {
|
|
|
17
17
|
set: jest.Mock;
|
|
18
18
|
expire: jest.Mock;
|
|
19
19
|
get: jest.Mock;
|
|
20
|
+
del: jest.Mock;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
describe("GlobalCache.setString", () => {
|
|
@@ -27,6 +28,7 @@ describe("GlobalCache.setString", () => {
|
|
|
27
28
|
set: jest.fn().mockResolvedValue("OK"),
|
|
28
29
|
expire: jest.fn().mockResolvedValue(1),
|
|
29
30
|
get: jest.fn(),
|
|
31
|
+
del: jest.fn().mockResolvedValue(1),
|
|
30
32
|
};
|
|
31
33
|
(Redis.getClient as jest.Mock).mockReturnValue(client);
|
|
32
34
|
(Redis.isConnected as jest.Mock).mockReturnValue(true);
|
|
@@ -98,3 +100,44 @@ describe("GlobalCache.setString", () => {
|
|
|
98
100
|
expect(client.expire).not.toHaveBeenCalled();
|
|
99
101
|
});
|
|
100
102
|
});
|
|
103
|
+
|
|
104
|
+
describe("GlobalCache.deleteKey", () => {
|
|
105
|
+
let client: MockClient;
|
|
106
|
+
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
client = {
|
|
109
|
+
set: jest.fn().mockResolvedValue("OK"),
|
|
110
|
+
expire: jest.fn().mockResolvedValue(1),
|
|
111
|
+
get: jest.fn(),
|
|
112
|
+
del: jest.fn().mockResolvedValue(1),
|
|
113
|
+
};
|
|
114
|
+
(Redis.getClient as jest.Mock).mockReturnValue(client);
|
|
115
|
+
(Redis.isConnected as jest.Mock).mockReturnValue(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
jest.clearAllMocks();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* deleteKey backs clearMaintenanceFence (OtelIngestBaseService): when
|
|
124
|
+
* the fenced maintenance work (updateLastSeen) fails, the fence is
|
|
125
|
+
* released so the next ingest batch retries instead of leaving the
|
|
126
|
+
* resource stranded as "disconnected" for the whole TTL window.
|
|
127
|
+
*/
|
|
128
|
+
test("deletes the namespaced key", async () => {
|
|
129
|
+
await GlobalCache.deleteKey("ns", "key");
|
|
130
|
+
|
|
131
|
+
expect(client.del).toHaveBeenCalledTimes(1);
|
|
132
|
+
expect(client.del).toHaveBeenCalledWith("ns-key");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("throws when the cache is not connected", async () => {
|
|
136
|
+
(Redis.isConnected as jest.Mock).mockReturnValue(false);
|
|
137
|
+
|
|
138
|
+
await expect(GlobalCache.deleteKey("ns", "key")).rejects.toThrow(
|
|
139
|
+
DatabaseNotConnectedException,
|
|
140
|
+
);
|
|
141
|
+
expect(client.del).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -161,3 +161,154 @@ describe("UserMiddleware.doesSsoTokenForProjectExist - requiredSsoProviderId", (
|
|
|
161
161
|
).toBe(false);
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* A single Global SSO token (minted by a Global SSO/OIDC login) is NOT bound to
|
|
167
|
+
* a project: it satisfies SSO enforcement for ANY project the user belongs to,
|
|
168
|
+
* including projects created after login. It still carries the provider
|
|
169
|
+
* discriminator so a project pinned to a specific provider is only satisfied by
|
|
170
|
+
* a matching token. The token is sourced from the `global-sso-token` cookie
|
|
171
|
+
* (web) or the `x-global-sso-token` header (mobile).
|
|
172
|
+
*/
|
|
173
|
+
describe("UserMiddleware.doesSsoTokenForProjectExist - global SSO token", () => {
|
|
174
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
175
|
+
const otherProjectId: ObjectID = ObjectID.generate();
|
|
176
|
+
const userId: ObjectID = ObjectID.generate();
|
|
177
|
+
const globalProviderId: ObjectID = ObjectID.generate();
|
|
178
|
+
const otherProviderId: ObjectID = ObjectID.generate();
|
|
179
|
+
|
|
180
|
+
const buildUser: () => User = (): User => {
|
|
181
|
+
const u: User = new User();
|
|
182
|
+
u.id = userId;
|
|
183
|
+
u.email = new Email("global-sso-user@oneuptime.com");
|
|
184
|
+
return u;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const buildRequestWithGlobalToken: (data: {
|
|
188
|
+
providerType: SsoProviderType;
|
|
189
|
+
providerId?: ObjectID;
|
|
190
|
+
viaHeader?: boolean;
|
|
191
|
+
}) => ExpressRequest = (data: {
|
|
192
|
+
providerType: SsoProviderType;
|
|
193
|
+
providerId?: ObjectID;
|
|
194
|
+
viaHeader?: boolean;
|
|
195
|
+
}): ExpressRequest => {
|
|
196
|
+
const token: string = CookieUtil.getGlobalSSOToken({
|
|
197
|
+
user: buildUser(),
|
|
198
|
+
ssoProviderId: data.providerId ?? globalProviderId,
|
|
199
|
+
ssoProviderType: data.providerType,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
if (data.viaHeader) {
|
|
203
|
+
return {
|
|
204
|
+
cookies: {},
|
|
205
|
+
headers: { "x-global-sso-token": token },
|
|
206
|
+
} as unknown as ExpressRequest;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
cookies: { [CookieUtil.getGlobalSSOKey()]: token },
|
|
211
|
+
headers: {},
|
|
212
|
+
} as unknown as ExpressRequest;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
test("global token (cookie), NO requiredProviderId -> true", () => {
|
|
216
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
217
|
+
providerType: SsoProviderType.GlobalSSO,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(
|
|
221
|
+
UserMiddleware.doesSsoTokenForProjectExist(req, projectId, userId),
|
|
222
|
+
).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("global token satisfies a project with NO per-project token (mid-session/new project) -> true", () => {
|
|
226
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
227
|
+
providerType: SsoProviderType.GlobalSSO,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// A different project the token was never minted for is still satisfied.
|
|
231
|
+
expect(
|
|
232
|
+
UserMiddleware.doesSsoTokenForProjectExist(req, otherProjectId, userId),
|
|
233
|
+
).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("global token, requiredProviderId EQUALS token's provider -> true", () => {
|
|
237
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
238
|
+
providerType: SsoProviderType.GlobalOIDC,
|
|
239
|
+
providerId: globalProviderId,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(
|
|
243
|
+
UserMiddleware.doesSsoTokenForProjectExist(
|
|
244
|
+
req,
|
|
245
|
+
projectId,
|
|
246
|
+
userId,
|
|
247
|
+
globalProviderId,
|
|
248
|
+
),
|
|
249
|
+
).toBe(true);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("global token, requiredProviderId DIFFERENT from token's provider -> false", () => {
|
|
253
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
254
|
+
providerType: SsoProviderType.GlobalSSO,
|
|
255
|
+
providerId: globalProviderId,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(
|
|
259
|
+
UserMiddleware.doesSsoTokenForProjectExist(
|
|
260
|
+
req,
|
|
261
|
+
projectId,
|
|
262
|
+
userId,
|
|
263
|
+
otherProviderId,
|
|
264
|
+
),
|
|
265
|
+
).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("global token, wrong userId -> false", () => {
|
|
269
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
270
|
+
providerType: SsoProviderType.GlobalSSO,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const differentUserId: ObjectID = ObjectID.generate();
|
|
274
|
+
|
|
275
|
+
expect(
|
|
276
|
+
UserMiddleware.doesSsoTokenForProjectExist(
|
|
277
|
+
req,
|
|
278
|
+
projectId,
|
|
279
|
+
differentUserId,
|
|
280
|
+
),
|
|
281
|
+
).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("global token via x-global-sso-token header (mobile) -> true", () => {
|
|
285
|
+
const req: ExpressRequest = buildRequestWithGlobalToken({
|
|
286
|
+
providerType: SsoProviderType.GlobalSSO,
|
|
287
|
+
viaHeader: true,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
expect(
|
|
291
|
+
UserMiddleware.doesSsoTokenForProjectExist(req, projectId, userId),
|
|
292
|
+
).toBe(true);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("project-typed token in the global cookie slot is ignored -> false", () => {
|
|
296
|
+
// Only Global provider types are accepted via the global-token path.
|
|
297
|
+
const token: string = CookieUtil.getSSOToken({
|
|
298
|
+
user: buildUser(),
|
|
299
|
+
projectId: projectId,
|
|
300
|
+
ssoProviderId: globalProviderId,
|
|
301
|
+
ssoProviderType: SsoProviderType.ProjectSSO,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const req: ExpressRequest = {
|
|
305
|
+
cookies: { [CookieUtil.getGlobalSSOKey()]: token },
|
|
306
|
+
headers: {},
|
|
307
|
+
} as unknown as ExpressRequest;
|
|
308
|
+
|
|
309
|
+
// No per-project cookie (sso-<projectId>) is present, so this must fail.
|
|
310
|
+
expect(
|
|
311
|
+
UserMiddleware.doesSsoTokenForProjectExist(req, otherProjectId, userId),
|
|
312
|
+
).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
});
|
package/Types/CookieName.ts
CHANGED
|
@@ -108,11 +108,6 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
108
108
|
});
|
|
109
109
|
}, [props.exemplarPoints, props.xAxis]);
|
|
110
110
|
|
|
111
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
112
|
-
const style: React.CSSProperties = props.heightInPx
|
|
113
|
-
? { height: `${props.heightInPx}px` }
|
|
114
|
-
: {};
|
|
115
|
-
|
|
116
111
|
const hasNoData: boolean =
|
|
117
112
|
!props.data ||
|
|
118
113
|
props.data.length === 0 ||
|
|
@@ -134,10 +129,11 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
134
129
|
typeof yAxisMaxOption === "number" ? { maxValue: yAxisMaxOption } : {};
|
|
135
130
|
|
|
136
131
|
return (
|
|
137
|
-
<div
|
|
132
|
+
<div
|
|
133
|
+
className="relative flex flex-1"
|
|
134
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
135
|
+
>
|
|
138
136
|
<AreaChart
|
|
139
|
-
className={className}
|
|
140
|
-
style={style}
|
|
141
137
|
data={records}
|
|
142
138
|
tickGap={30}
|
|
143
139
|
index={"Time"}
|
|
@@ -54,11 +54,6 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
54
54
|
setRecords(records);
|
|
55
55
|
}, [props.data]);
|
|
56
56
|
|
|
57
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
58
|
-
const style: React.CSSProperties = props.heightInPx
|
|
59
|
-
? { height: `${props.heightInPx}px` }
|
|
60
|
-
: {};
|
|
61
|
-
|
|
62
57
|
const hasNoData: boolean =
|
|
63
58
|
!props.data ||
|
|
64
59
|
props.data.length === 0 ||
|
|
@@ -67,10 +62,11 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
67
62
|
});
|
|
68
63
|
|
|
69
64
|
return (
|
|
70
|
-
<div
|
|
65
|
+
<div
|
|
66
|
+
className="relative flex flex-1"
|
|
67
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
68
|
+
>
|
|
71
69
|
<BarChart
|
|
72
|
-
className={className}
|
|
73
|
-
style={style}
|
|
74
70
|
data={records}
|
|
75
71
|
tickGap={30}
|
|
76
72
|
index={"Time"}
|
|
@@ -49,7 +49,6 @@ export interface Chart {
|
|
|
49
49
|
export interface ComponentProps {
|
|
50
50
|
charts: Array<Chart>;
|
|
51
51
|
hideCard?: boolean | undefined;
|
|
52
|
-
heightInPx?: number | undefined;
|
|
53
52
|
chartCssClass?: string | undefined;
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -80,7 +79,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
80
79
|
key={index}
|
|
81
80
|
{...(chart.props as LineChartProps)}
|
|
82
81
|
syncid={syncId}
|
|
83
|
-
heightInPx={props.heightInPx}
|
|
84
82
|
exemplarPoints={chart.exemplarPoints}
|
|
85
83
|
onExemplarClick={chart.onExemplarClick}
|
|
86
84
|
showLegend={showLegend}
|
|
@@ -92,7 +90,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
92
90
|
key={index}
|
|
93
91
|
{...(chart.props as BarChartProps)}
|
|
94
92
|
syncid={syncId}
|
|
95
|
-
heightInPx={props.heightInPx}
|
|
96
93
|
showLegend={showLegend}
|
|
97
94
|
/>
|
|
98
95
|
);
|
|
@@ -102,7 +99,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
102
99
|
key={index}
|
|
103
100
|
{...(chart.props as AreaChartProps)}
|
|
104
101
|
syncid={syncId}
|
|
105
|
-
heightInPx={props.heightInPx}
|
|
106
102
|
exemplarPoints={chart.exemplarPoints}
|
|
107
103
|
onExemplarClick={chart.onExemplarClick}
|
|
108
104
|
showLegend={showLegend}
|
|
@@ -244,14 +240,14 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
244
240
|
return (
|
|
245
241
|
<>
|
|
246
242
|
{renderMetricInfoModal()}
|
|
247
|
-
<div className="space-y-3">
|
|
243
|
+
<div className="space-y-3 flex-col flex flex-1 w-full">
|
|
248
244
|
{props.charts.map((chart: Chart, index: number) => {
|
|
249
245
|
return (
|
|
250
246
|
<div
|
|
251
247
|
key={index}
|
|
252
|
-
className={`bg-white ${props.chartCssClass || ""}`}
|
|
248
|
+
className={`bg-white ${props.chartCssClass || ""} flex-1 flex-col flex w-full`}
|
|
253
249
|
>
|
|
254
|
-
<div className="px-5 pt-4 pb-4">
|
|
250
|
+
<div className="px-5 pt-4 pb-4 flex flex-col flex-1">
|
|
255
251
|
<div className="mb-3 pb-3 border-b border-gray-100">
|
|
256
252
|
<div className="flex items-center">
|
|
257
253
|
<h3 className="text-sm font-semibold text-gray-800 tracking-tight">
|
|
@@ -265,10 +261,10 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
265
261
|
</p>
|
|
266
262
|
)}
|
|
267
263
|
</div>
|
|
264
|
+
{getChartContent(chart, index)}
|
|
268
265
|
{chart.seriesControls ? (
|
|
269
266
|
<div className="mb-3">{chart.seriesControls}</div>
|
|
270
267
|
) : null}
|
|
271
|
-
{getChartContent(chart, index)}
|
|
272
268
|
</div>
|
|
273
269
|
</div>
|
|
274
270
|
);
|
|
@@ -689,7 +689,7 @@ const AreaChart: React.ForwardRefExoticComponent<
|
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
return (
|
|
692
|
-
<div ref={ref} className={cx("
|
|
692
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
693
693
|
<ResponsiveContainer>
|
|
694
694
|
<RechartsAreaChart
|
|
695
695
|
data={data}
|
|
@@ -695,7 +695,7 @@ const LineChart: React.ForwardRefExoticComponent<
|
|
|
695
695
|
}
|
|
696
696
|
|
|
697
697
|
return (
|
|
698
|
-
<div ref={ref} className={cx("
|
|
698
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
699
699
|
<ResponsiveContainer>
|
|
700
700
|
<RechartsLineChart
|
|
701
701
|
data={data}
|
|
@@ -86,11 +86,6 @@ const LineChartElement: FunctionComponent<LineInternalProps> = (
|
|
|
86
86
|
});
|
|
87
87
|
}, [props.exemplarPoints, props.xAxis]);
|
|
88
88
|
|
|
89
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
90
|
-
const style: React.CSSProperties = props.heightInPx
|
|
91
|
-
? { height: `${props.heightInPx}px` }
|
|
92
|
-
: {};
|
|
93
|
-
|
|
94
89
|
const hasNoData: boolean =
|
|
95
90
|
!props.data ||
|
|
96
91
|
props.data.length === 0 ||
|
|
@@ -114,10 +109,11 @@ const LineChartElement: FunctionComponent<LineInternalProps> = (
|
|
|
114
109
|
typeof yAxisMaxOption === "number" ? { maxValue: yAxisMaxOption } : {};
|
|
115
110
|
|
|
116
111
|
return (
|
|
117
|
-
<div
|
|
112
|
+
<div
|
|
113
|
+
className="relative flex flex-1"
|
|
114
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
115
|
+
>
|
|
118
116
|
<LineChart
|
|
119
|
-
className={className}
|
|
120
|
-
style={style}
|
|
121
117
|
data={records}
|
|
122
118
|
tickGap={30}
|
|
123
119
|
index={"Time"}
|
|
@@ -45,6 +45,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
45
45
|
const [validationError, setValidationError] = useState<string>("");
|
|
46
46
|
const [successMessage, setSuccessMessage] = useState<string>("");
|
|
47
47
|
const [isValidating, setIsValidating] = useState<boolean>(false);
|
|
48
|
+
const [isChangingLicense, setIsChangingLicense] = useState<boolean>(false);
|
|
48
49
|
const licenseInputEditedRef: React.MutableRefObject<boolean> =
|
|
49
50
|
useRef<boolean>(false);
|
|
50
51
|
|
|
@@ -318,6 +319,22 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
318
319
|
setIsDialogOpen(false);
|
|
319
320
|
setValidationError("");
|
|
320
321
|
setSuccessMessage("");
|
|
322
|
+
setIsChangingLicense(false);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const handleStartChangingLicense: () => void = () => {
|
|
326
|
+
setIsChangingLicense(true);
|
|
327
|
+
setValidationError("");
|
|
328
|
+
setSuccessMessage("");
|
|
329
|
+
setLicenseKeyInput("");
|
|
330
|
+
licenseInputEditedRef.current = true;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const handleCancelChangingLicense: () => void = () => {
|
|
334
|
+
setIsChangingLicense(false);
|
|
335
|
+
setValidationError("");
|
|
336
|
+
licenseInputEditedRef.current = false;
|
|
337
|
+
setLicenseKeyInput(globalConfig?.enterpriseLicenseKey || "");
|
|
321
338
|
};
|
|
322
339
|
|
|
323
340
|
const handlePrimaryAction: () => void = () => {
|
|
@@ -371,6 +388,7 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
371
388
|
licenseInputEditedRef.current = false;
|
|
372
389
|
setLicenseKeyInput((payload["licenseKey"] as string) || trimmedKey);
|
|
373
390
|
setSuccessMessage("License validated successfully.");
|
|
391
|
+
setIsChangingLicense(false);
|
|
374
392
|
|
|
375
393
|
await fetchGlobalConfig();
|
|
376
394
|
} catch (err) {
|
|
@@ -396,8 +414,10 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
396
414
|
}
|
|
397
415
|
};
|
|
398
416
|
|
|
399
|
-
const
|
|
400
|
-
IS_ENTERPRISE_EDITION && !licenseValid;
|
|
417
|
+
const showLicenseKeyInput: boolean =
|
|
418
|
+
IS_ENTERPRISE_EDITION && (!licenseValid || isChangingLicense);
|
|
419
|
+
|
|
420
|
+
const shouldShowEnterpriseValidationButton: boolean = showLicenseKeyInput;
|
|
401
421
|
|
|
402
422
|
const modalSubmitButtonText: string | undefined = IS_ENTERPRISE_EDITION
|
|
403
423
|
? shouldShowEnterpriseValidationButton
|
|
@@ -611,6 +631,20 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
611
631
|
</div>
|
|
612
632
|
)}
|
|
613
633
|
|
|
634
|
+
{!configError &&
|
|
635
|
+
!isConfigLoading &&
|
|
636
|
+
licenseValid &&
|
|
637
|
+
!isChangingLicense && (
|
|
638
|
+
<div className="-ml-3">
|
|
639
|
+
<Button
|
|
640
|
+
title="Change license key"
|
|
641
|
+
icon={IconProp.Edit}
|
|
642
|
+
buttonStyle={ButtonStyleType.NORMAL}
|
|
643
|
+
onClick={handleStartChangingLicense}
|
|
644
|
+
/>
|
|
645
|
+
</div>
|
|
646
|
+
)}
|
|
647
|
+
|
|
614
648
|
{!configError &&
|
|
615
649
|
!isConfigLoading &&
|
|
616
650
|
!licenseValid &&
|
|
@@ -632,11 +666,13 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
632
666
|
<Alert type={AlertType.SUCCESS} title={successMessage} />
|
|
633
667
|
)}
|
|
634
668
|
|
|
635
|
-
{
|
|
669
|
+
{showLicenseKeyInput && (
|
|
636
670
|
<>
|
|
637
671
|
<div>
|
|
638
672
|
<label className="text-sm font-medium text-gray-700">
|
|
639
|
-
|
|
673
|
+
{isChangingLicense
|
|
674
|
+
? "New License Key"
|
|
675
|
+
: "License Key"}
|
|
640
676
|
</label>
|
|
641
677
|
<Input
|
|
642
678
|
value={licenseKeyInput}
|
|
@@ -656,18 +692,34 @@ const EditionLabel: FunctionComponent<ComponentProps> = (
|
|
|
656
692
|
/>
|
|
657
693
|
)}
|
|
658
694
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
695
|
+
{isChangingLicense ? (
|
|
696
|
+
<div className="flex items-center justify-between gap-3">
|
|
697
|
+
<p className="text-xs text-gray-500">
|
|
698
|
+
Enter the new enterprise license key and validate
|
|
699
|
+
it to replace the current one. Your existing
|
|
700
|
+
license stays active until the new key is
|
|
701
|
+
validated.
|
|
702
|
+
</p>
|
|
703
|
+
<Button
|
|
704
|
+
title="Cancel"
|
|
705
|
+
buttonStyle={ButtonStyleType.NORMAL}
|
|
706
|
+
onClick={handleCancelChangingLicense}
|
|
707
|
+
/>
|
|
708
|
+
</div>
|
|
709
|
+
) : (
|
|
710
|
+
<p className="text-xs text-gray-500">
|
|
711
|
+
You have installed Enterprise Edition of OneUptime.
|
|
712
|
+
You need to validate your license key. Need a
|
|
713
|
+
license key? Contact our sales team at{" "}
|
|
714
|
+
<a
|
|
715
|
+
href="mailto:sales@oneuptime.com"
|
|
716
|
+
className="font-medium text-indigo-600 hover:text-indigo-700"
|
|
717
|
+
>
|
|
718
|
+
sales@oneuptime.com
|
|
719
|
+
</a>
|
|
720
|
+
.
|
|
721
|
+
</p>
|
|
722
|
+
)}
|
|
671
723
|
</>
|
|
672
724
|
)}
|
|
673
725
|
</>
|
|
@@ -188,7 +188,10 @@ const Modal: FunctionComponent<ComponentProps> = (
|
|
|
188
188
|
)}
|
|
189
189
|
</div>
|
|
190
190
|
{props.rightElement && (
|
|
191
|
-
<div
|
|
191
|
+
<div
|
|
192
|
+
data-testid="right-element"
|
|
193
|
+
className="mt-4 md:mt-0 lg:mr-4"
|
|
194
|
+
>
|
|
192
195
|
{props.rightElement}
|
|
193
196
|
</div>
|
|
194
197
|
)}
|
|
@@ -101,6 +101,13 @@ export default class GlobalCache {
|
|
|
101
101
|
*/
|
|
102
102
|
await client.set(`${namespace}-${key}`, value, "EX", expiresInSeconds);
|
|
103
103
|
}
|
|
104
|
+
static async deleteKey(namespace, key) {
|
|
105
|
+
const client = Redis.getClient();
|
|
106
|
+
if (!client || !Redis.isConnected()) {
|
|
107
|
+
throw new DatabaseNotConnectedException("Cache is not connected");
|
|
108
|
+
}
|
|
109
|
+
await client.del(`${namespace}-${key}`);
|
|
110
|
+
}
|
|
104
111
|
}
|
|
105
112
|
__decorate([
|
|
106
113
|
CaptureSpan(),
|
|
@@ -144,4 +151,10 @@ __decorate([
|
|
|
144
151
|
__metadata("design:paramtypes", [String, String, String, Object]),
|
|
145
152
|
__metadata("design:returntype", Promise)
|
|
146
153
|
], GlobalCache, "setString", null);
|
|
154
|
+
__decorate([
|
|
155
|
+
CaptureSpan(),
|
|
156
|
+
__metadata("design:type", Function),
|
|
157
|
+
__metadata("design:paramtypes", [String, String]),
|
|
158
|
+
__metadata("design:returntype", Promise)
|
|
159
|
+
], GlobalCache, "deleteKey", null);
|
|
147
160
|
//# sourceMappingURL=GlobalCache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlobalCache.js","sourceRoot":"","sources":["../../../../Server/Infrastructure/GlobalCache.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"GlobalCache.js","sourceRoot":"","sources":["../../../../Server/Infrastructure/GlobalCache.ts"],"names":[],"mappings":"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"}
|