@gitpod/gitpod-protocol 0.1.5-pd-fix-gosimple.0 → 0.1.5-pd-ide-metrics.30
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/data/gitpod-schema.json +16 -0
- package/lib/admin-protocol.d.ts +4 -0
- package/lib/admin-protocol.d.ts.map +1 -1
- package/lib/admin-protocol.js.map +1 -1
- package/lib/attribution.d.ts +20 -0
- package/lib/attribution.d.ts.map +1 -0
- package/lib/attribution.js +40 -0
- package/lib/attribution.js.map +1 -0
- package/lib/blocked-repositories-protocol.d.ts +13 -0
- package/lib/blocked-repositories-protocol.d.ts.map +1 -0
- package/lib/blocked-repositories-protocol.js +8 -0
- package/lib/blocked-repositories-protocol.js.map +1 -0
- package/lib/context-url.js +1 -1
- package/lib/context-url.js.map +1 -1
- package/lib/experiments/always-default.d.ts +8 -0
- package/lib/experiments/always-default.d.ts.map +1 -0
- package/lib/experiments/always-default.js +20 -0
- package/lib/experiments/always-default.js.map +1 -0
- package/lib/experiments/configcat-server.d.ts +8 -0
- package/lib/experiments/configcat-server.d.ts.map +1 -0
- package/lib/experiments/configcat-server.js +35 -0
- package/lib/experiments/configcat-server.js.map +1 -0
- package/lib/experiments/configcat.d.ts +22 -0
- package/lib/experiments/configcat.d.ts.map +1 -0
- package/lib/experiments/configcat.js +53 -0
- package/lib/experiments/configcat.js.map +1 -0
- package/lib/experiments/types.d.ts +23 -0
- package/lib/experiments/types.d.ts.map +1 -0
- package/lib/experiments/types.js +10 -0
- package/lib/experiments/types.js.map +1 -0
- package/lib/gitpod-service.d.ts +27 -2
- package/lib/gitpod-service.d.ts.map +1 -1
- package/lib/gitpod-service.js +1 -1
- package/lib/gitpod-service.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/installation-admin-protocol.d.ts +3 -0
- package/lib/installation-admin-protocol.d.ts.map +1 -1
- package/lib/installation-admin-protocol.js +1 -0
- package/lib/installation-admin-protocol.js.map +1 -1
- package/lib/messaging/client-call-metrics.d.ts +1 -17
- package/lib/messaging/client-call-metrics.d.ts.map +1 -1
- package/lib/messaging/client-call-metrics.js +1 -2
- package/lib/messaging/client-call-metrics.js.map +1 -1
- package/lib/messaging/error.d.ts +1 -0
- package/lib/messaging/error.d.ts.map +1 -1
- package/lib/messaging/error.js +2 -0
- package/lib/messaging/error.js.map +1 -1
- package/lib/protocol.d.ts +58 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +93 -5
- package/lib/protocol.js.map +1 -1
- package/lib/protocol.spec.d.ts +7 -0
- package/lib/protocol.spec.d.ts.map +1 -0
- package/lib/protocol.spec.js +127 -0
- package/lib/protocol.spec.js.map +1 -0
- package/lib/team-subscription-protocol.d.ts +1 -0
- package/lib/team-subscription-protocol.d.ts.map +1 -1
- package/lib/team-subscription-protocol.js.map +1 -1
- package/lib/teams-projects-protocol.d.ts +14 -0
- package/lib/teams-projects-protocol.d.ts.map +1 -1
- package/lib/teams-projects-protocol.js.map +1 -1
- package/lib/usage.d.ts +31 -0
- package/lib/usage.d.ts.map +1 -0
- package/lib/usage.js +14 -0
- package/lib/usage.js.map +1 -0
- package/lib/util/debug-app.d.ts +25 -0
- package/lib/util/debug-app.d.ts.map +1 -0
- package/lib/util/debug-app.js +100 -0
- package/lib/util/debug-app.js.map +1 -0
- package/lib/util/gitpod-host-url.d.ts +1 -0
- package/lib/util/gitpod-host-url.d.ts.map +1 -1
- package/lib/util/gitpod-host-url.js +3 -0
- package/lib/util/gitpod-host-url.js.map +1 -1
- package/lib/util/grpc.d.ts +19 -0
- package/lib/util/grpc.d.ts.map +1 -1
- package/lib/util/grpc.js +73 -1
- package/lib/util/grpc.js.map +1 -1
- package/lib/util/logging.d.ts.map +1 -1
- package/lib/util/logging.js +8 -1
- package/lib/util/logging.js.map +1 -1
- package/lib/util/timeutil.d.ts +1 -0
- package/lib/util/timeutil.d.ts.map +1 -1
- package/lib/util/timeutil.js +7 -1
- package/lib/util/timeutil.js.map +1 -1
- package/lib/webhook-event.d.ts +44 -0
- package/lib/webhook-event.d.ts.map +1 -0
- package/lib/webhook-event.js +8 -0
- package/lib/webhook-event.js.map +1 -0
- package/lib/workspace-class.d.ts +14 -0
- package/lib/workspace-class.d.ts.map +1 -0
- package/lib/workspace-class.js +8 -0
- package/lib/workspace-class.js.map +1 -0
- package/lib/workspace-cluster.d.ts +5 -7
- package/lib/workspace-cluster.d.ts.map +1 -1
- package/lib/workspace-cluster.js.map +1 -1
- package/lib/workspace-instance.d.ts +11 -0
- package/lib/workspace-instance.d.ts.map +1 -1
- package/lib/wsready.d.ts +3 -3
- package/lib/wsready.d.ts.map +1 -1
- package/lib/wsready.js +0 -2
- package/lib/wsready.js.map +1 -1
- package/package.json +12 -6
- package/pkg-yarn.lock +8 -5
- package/provenance-bundle.jsonl +1 -1
- package/src/admin-protocol.ts +7 -0
- package/src/attribution.ts +48 -0
- package/src/blocked-repositories-protocol.ts +13 -0
- package/src/context-url.ts +1 -1
- package/src/experiments/always-default.ts +24 -0
- package/src/experiments/configcat-server.ts +38 -0
- package/src/experiments/configcat.ts +58 -0
- package/src/experiments/types.ts +35 -0
- package/src/gitpod-service.ts +37 -1
- package/src/index.ts +1 -0
- package/src/installation-admin-protocol.ts +3 -0
- package/src/messaging/client-call-metrics.ts +1 -20
- package/src/messaging/error.ts +3 -0
- package/src/protocol.spec.ts +97 -0
- package/src/protocol.ts +150 -7
- package/src/team-subscription-protocol.ts +1 -0
- package/src/teams-projects-protocol.ts +15 -0
- package/src/usage.ts +50 -0
- package/src/util/debug-app.ts +81 -0
- package/src/util/gitpod-host-url.ts +4 -0
- package/src/util/grpc.ts +89 -0
- package/src/util/logging.ts +11 -2
- package/src/util/timeutil.ts +6 -0
- package/src/webhook-event.ts +55 -0
- package/src/workspace-class.ts +14 -0
- package/src/workspace-cluster.ts +2 -4
- package/src/workspace-instance.ts +17 -0
- package/src/wsready.ts +5 -4
package/src/gitpod-service.ts
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
GuessGitTokenScopesParams,
|
|
25
25
|
GuessedGitTokenScopes,
|
|
26
26
|
ProjectEnvVar,
|
|
27
|
+
PrebuiltWorkspace,
|
|
28
|
+
UserSSHPublicKeyValue,
|
|
29
|
+
SSHPublicKeyValue,
|
|
27
30
|
} from "./protocol";
|
|
28
31
|
import {
|
|
29
32
|
Team,
|
|
@@ -34,6 +37,7 @@ import {
|
|
|
34
37
|
PrebuildWithStatus,
|
|
35
38
|
StartPrebuildResult,
|
|
36
39
|
PartialProject,
|
|
40
|
+
PrebuildEvent,
|
|
37
41
|
} from "./teams-projects-protocol";
|
|
38
42
|
import { JsonRpcProxy, JsonRpcServer } from "./messaging/proxy-factory";
|
|
39
43
|
import { Disposable, CancellationTokenSource } from "vscode-jsonrpc";
|
|
@@ -56,6 +60,9 @@ import {
|
|
|
56
60
|
import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics";
|
|
57
61
|
import { IDEServer } from "./ide-protocol";
|
|
58
62
|
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
|
|
63
|
+
import { Currency } from "./plans";
|
|
64
|
+
import { BillableSession, BillableSessionRequest } from "./usage";
|
|
65
|
+
import { SupportedWorkspaceClass } from "./workspace-class";
|
|
59
66
|
|
|
60
67
|
export interface GitpodClient {
|
|
61
68
|
onInstanceUpdate(instance: WorkspaceInstance): void;
|
|
@@ -147,6 +154,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
147
154
|
setEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
148
155
|
deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
149
156
|
|
|
157
|
+
// User SSH Keys
|
|
158
|
+
hasSSHPublicKey(): Promise<boolean>;
|
|
159
|
+
getSSHPublicKeys(): Promise<UserSSHPublicKeyValue[]>;
|
|
160
|
+
addSSHPublicKey(value: SSHPublicKeyValue): Promise<UserSSHPublicKeyValue>;
|
|
161
|
+
deleteSSHPublicKey(id: string): Promise<void>;
|
|
162
|
+
|
|
150
163
|
// Teams
|
|
151
164
|
getTeams(): Promise<Team[]>;
|
|
152
165
|
getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
|
|
@@ -170,7 +183,10 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
170
183
|
getTeamProjects(teamId: string): Promise<Project[]>;
|
|
171
184
|
getUserProjects(): Promise<Project[]>;
|
|
172
185
|
getProjectOverview(projectId: string): Promise<Project.Overview | undefined>;
|
|
186
|
+
getPrebuildEvents(projectId: string): Promise<PrebuildEvent[]>;
|
|
173
187
|
findPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
|
|
188
|
+
findPrebuildByWorkspaceID(workspaceId: string): Promise<PrebuiltWorkspace | undefined>;
|
|
189
|
+
getPrebuild(prebuildId: string): Promise<PrebuildWithStatus | undefined>;
|
|
174
190
|
triggerPrebuild(projectId: string, branchName: string | null): Promise<StartPrebuildResult>;
|
|
175
191
|
cancelPrebuild(projectId: string, prebuildId: string): Promise<void>;
|
|
176
192
|
fetchProjectRepositoryConfiguration(projectId: string): Promise<string | undefined>;
|
|
@@ -270,12 +286,31 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
270
286
|
|
|
271
287
|
getGithubUpgradeUrls(): Promise<GithubUpgradeURL[]>;
|
|
272
288
|
|
|
289
|
+
getStripePublishableKey(): Promise<string>;
|
|
290
|
+
getStripeSetupIntentClientSecret(): Promise<string>;
|
|
291
|
+
findStripeSubscriptionIdForTeam(teamId: string): Promise<string | undefined>;
|
|
292
|
+
subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
|
|
293
|
+
getStripePortalUrlForTeam(teamId: string): Promise<string>;
|
|
294
|
+
getSpendingLimitForTeam(teamId: string): Promise<number | undefined>;
|
|
295
|
+
setSpendingLimitForTeam(teamId: string, spendingLimit: number): Promise<void>;
|
|
296
|
+
|
|
297
|
+
listBilledUsage(req: BillableSessionRequest): Promise<BillableSession[]>;
|
|
298
|
+
|
|
299
|
+
setUsageAttribution(usageAttribution: string): Promise<void>;
|
|
300
|
+
|
|
273
301
|
/**
|
|
274
302
|
* Analytics
|
|
275
303
|
*/
|
|
276
304
|
trackEvent(event: RemoteTrackMessage): Promise<void>;
|
|
277
305
|
trackLocation(event: RemotePageMessage): Promise<void>;
|
|
278
306
|
identifyUser(event: RemoteIdentifyMessage): Promise<void>;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Frontend notifications
|
|
310
|
+
*/
|
|
311
|
+
getNotifications(): Promise<string[]>;
|
|
312
|
+
|
|
313
|
+
getSupportedWorkspaceClasses(): Promise<SupportedWorkspaceClass[]>;
|
|
279
314
|
}
|
|
280
315
|
|
|
281
316
|
export interface RateLimiterError {
|
|
@@ -357,7 +392,7 @@ export const createServerMock = function <C extends GitpodClient, S extends Gitp
|
|
|
357
392
|
get: (target: S, property: keyof S) => {
|
|
358
393
|
const result = target[property];
|
|
359
394
|
if (!result) {
|
|
360
|
-
throw new Error(`Method ${property} not implemented`);
|
|
395
|
+
throw new Error(`Method ${String(property)} not implemented`);
|
|
361
396
|
}
|
|
362
397
|
return result;
|
|
363
398
|
},
|
|
@@ -373,6 +408,7 @@ export interface SetWorkspaceTimeoutResult {
|
|
|
373
408
|
|
|
374
409
|
export interface GetWorkspaceTimeoutResult {
|
|
375
410
|
duration: WorkspaceTimeoutDuration;
|
|
411
|
+
durationRaw: string;
|
|
376
412
|
canChange: boolean;
|
|
377
413
|
}
|
|
378
414
|
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
8
8
|
|
|
9
9
|
const InstallationAdminSettingsPrototype = {
|
|
10
10
|
sendTelemetry: true,
|
|
11
|
+
sendCustomerID: true,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype;
|
|
@@ -28,6 +29,8 @@ export interface TelemetryData {
|
|
|
28
29
|
totalUsers: number;
|
|
29
30
|
totalWorkspaces: number;
|
|
30
31
|
totalInstances: number;
|
|
32
|
+
licenseType: string;
|
|
33
|
+
customerID?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export namespace InstallationAdmin {
|
|
@@ -6,26 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { injectable } from "inversify";
|
|
8
8
|
import * as prometheusClient from "prom-client";
|
|
9
|
-
|
|
10
|
-
type GrpcMethodType = "unary" | "client_stream" | "server_stream" | "bidi_stream";
|
|
11
|
-
export interface IGrpcCallMetricsLabels {
|
|
12
|
-
service: string;
|
|
13
|
-
method: string;
|
|
14
|
-
type: GrpcMethodType;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface IGrpcCallMetricsLabelsWithCode extends IGrpcCallMetricsLabels {
|
|
18
|
-
code: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const IClientCallMetrics = Symbol("IClientCallMetrics");
|
|
22
|
-
|
|
23
|
-
export interface IClientCallMetrics {
|
|
24
|
-
started(labels: IGrpcCallMetricsLabels): void;
|
|
25
|
-
sent(labels: IGrpcCallMetricsLabels): void;
|
|
26
|
-
received(labels: IGrpcCallMetricsLabels): void;
|
|
27
|
-
handled(labels: IGrpcCallMetricsLabelsWithCode): void;
|
|
28
|
-
}
|
|
9
|
+
import { IClientCallMetrics, IGrpcCallMetricsLabels, IGrpcCallMetricsLabelsWithCode } from "../util/grpc";
|
|
29
10
|
|
|
30
11
|
@injectable()
|
|
31
12
|
export class PrometheusClientCallMetrics implements IClientCallMetrics {
|
package/src/messaging/error.ts
CHANGED
|
@@ -62,6 +62,9 @@ export namespace ErrorCodes {
|
|
|
62
62
|
// 490 Too Many Running Workspace
|
|
63
63
|
export const TOO_MANY_RUNNING_WORKSPACES = 490;
|
|
64
64
|
|
|
65
|
+
// 500 Internal Server Error
|
|
66
|
+
export const INTERNAL_SERVER_ERROR = 500;
|
|
67
|
+
|
|
65
68
|
// 501 EE Feature
|
|
66
69
|
export const EE_FEATURE = 501;
|
|
67
70
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { suite, test } from "mocha-typescript";
|
|
8
|
+
import * as chai from "chai";
|
|
9
|
+
import { SSHPublicKeyValue } from ".";
|
|
10
|
+
|
|
11
|
+
const expect = chai.expect;
|
|
12
|
+
|
|
13
|
+
@suite
|
|
14
|
+
class TestSSHPublicKeyValue {
|
|
15
|
+
private key =
|
|
16
|
+
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDCnrN9UdK1bNGPmZfenTWXLuYYDjlYvZE8S+WOfP08WpR1GETzX5ZvgYOEZGwEE8KUPHC9cge4Hvo/ydIS9aqbZ5MiVGJ8cAIq1Ic89SjlDWU6fl8TwIqOPCi2imAASlEDP4q8vMLK1N6UOW1EVbxyL3uybGd10ysC1t1FxFPveIGNsYE/MOQeuEWS16AplpXYXIfVRSlgAskeBft2w8Ud3B4gNe8ECLA/FXu96UpvZkdtOarA3JZ9Z27GveNJg9Mtmmw0+US0KXiO9x9NyH7G8+mqVDwDY+nNvaFA5gtQxkkl/uY2oz9k/B4Rjlj3jOiUXe5uQs3XUm5m8g9a9fh62DabLpA2fEvtfg+a/VqNe52dNa5YjupwvBd6Inb5uMW/TYjNl6bNHPlXFKw/nwLOVzukpkjxMZUKS6+4BGkpoasj6y2rTU/wkpbdD8J7yjI1p6J9aKkC6KksIWgN7xGmHkv2PCGDqMHTNbnQyowtNKMgA/667vAYJ0qW7HAHBFXJRs6uRi/DI3+c1QV2s4wPCpEHDIYApovQ0fbON4WDPoGMyHd7kPh9xB/bX7Dj0uMXImu1pdTd62fQ/1XXX64+vjAAXS/P9RSCD0RCRt/K3LPKl2m7GPI3y1niaE52XhxZw+ms9ays6NasNVMw/ZC+f02Ti+L5FBEVf8230RVVRQ== notfound@gitpod.io";
|
|
17
|
+
|
|
18
|
+
@test public testValidate() {
|
|
19
|
+
const key = this.key;
|
|
20
|
+
const [t, k, e] = key.split(" ");
|
|
21
|
+
expect(
|
|
22
|
+
SSHPublicKeyValue.getData({
|
|
23
|
+
key,
|
|
24
|
+
name: "NiceName",
|
|
25
|
+
}),
|
|
26
|
+
).to.deep.equal({ type: t, key: k, email: e });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test public testValidateWithDiffType() {
|
|
30
|
+
const key = this.key;
|
|
31
|
+
const [_, k, e] = key.split(" ");
|
|
32
|
+
expect(
|
|
33
|
+
SSHPublicKeyValue.getData({
|
|
34
|
+
key: key.replace("ssh-rsa", "sk-ecdsa-sha2-nistp256@openssh.com"),
|
|
35
|
+
name: "NiceName",
|
|
36
|
+
}),
|
|
37
|
+
).to.deep.equal({ type: "sk-ecdsa-sha2-nistp256@openssh.com", key: k, email: e });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@test public testValidateWithoutEmail() {
|
|
41
|
+
const key = this.key;
|
|
42
|
+
const [t, k, _] = key.split(" ");
|
|
43
|
+
expect(
|
|
44
|
+
SSHPublicKeyValue.getData({
|
|
45
|
+
key: key.replace(" notfound@gitpod.io", ""),
|
|
46
|
+
name: "NiceName",
|
|
47
|
+
}),
|
|
48
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test public testValidateWithoutEmailButEndsWithSpaces() {
|
|
52
|
+
const key = this.key;
|
|
53
|
+
const [t, k, _] = key.split(" ");
|
|
54
|
+
expect(
|
|
55
|
+
SSHPublicKeyValue.getData({
|
|
56
|
+
key: key.replace("notfound@gitpod.io", " "),
|
|
57
|
+
name: "NiceName",
|
|
58
|
+
}),
|
|
59
|
+
).to.deep.equal({ type: t, key: k, email: undefined });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@test public testValidateWithError() {
|
|
63
|
+
expect(() =>
|
|
64
|
+
SSHPublicKeyValue.getData({
|
|
65
|
+
key: "Hello World",
|
|
66
|
+
name: "NiceName",
|
|
67
|
+
}),
|
|
68
|
+
).throw("Key is invalid");
|
|
69
|
+
|
|
70
|
+
expect(() =>
|
|
71
|
+
SSHPublicKeyValue.getData({
|
|
72
|
+
key: "",
|
|
73
|
+
name: "NiceName",
|
|
74
|
+
}),
|
|
75
|
+
).throw("Key is invalid");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@test public testGetFingerprint() {
|
|
79
|
+
const key = this.key;
|
|
80
|
+
expect(
|
|
81
|
+
SSHPublicKeyValue.getFingerprint({
|
|
82
|
+
key,
|
|
83
|
+
name: "NiceName",
|
|
84
|
+
}),
|
|
85
|
+
).to.equal("ykjP/b5aqoa3envmXzWpPMCGgEFMu3QvubfSTNrJCMA=");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@test public testGetFingerprintWithIncorrectPublicKey() {
|
|
89
|
+
expect(() =>
|
|
90
|
+
SSHPublicKeyValue.getFingerprint({
|
|
91
|
+
key: "Hello World",
|
|
92
|
+
name: "NiceName",
|
|
93
|
+
}),
|
|
94
|
+
).to.throw("Key is invalid");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
module.exports = new TestSSHPublicKeyValue(); // Only to circumvent no usage warning :-/
|
package/src/protocol.ts
CHANGED
|
@@ -45,6 +45,9 @@ export interface User {
|
|
|
45
45
|
markedDeleted?: boolean;
|
|
46
46
|
|
|
47
47
|
additionalData?: AdditionalUserData;
|
|
48
|
+
|
|
49
|
+
// Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
|
|
50
|
+
usageAttributionId?: string;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export namespace User {
|
|
@@ -70,11 +73,14 @@ export namespace User {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
|
-
*
|
|
76
|
+
* Returns the stored email or if it doesn't exist returns the primaryEmail of the first identity this user signed up with.
|
|
74
77
|
* @param user
|
|
75
78
|
* @returns A primaryEmail, or undefined if there is none.
|
|
76
79
|
*/
|
|
77
80
|
export function getPrimaryEmail(user: User): string | undefined {
|
|
81
|
+
if (user.additionalData?.profile?.emailAddress) {
|
|
82
|
+
return user.additionalData?.profile?.emailAddress;
|
|
83
|
+
}
|
|
78
84
|
const identities = user.identities.filter((i) => !!i.primaryEmail);
|
|
79
85
|
if (identities.length <= 0) {
|
|
80
86
|
return undefined;
|
|
@@ -136,6 +142,50 @@ export namespace User {
|
|
|
136
142
|
}
|
|
137
143
|
user.additionalData.ideSettings = newIDESettings;
|
|
138
144
|
}
|
|
145
|
+
|
|
146
|
+
export function getProfile(user: User): Profile {
|
|
147
|
+
return {
|
|
148
|
+
name: User.getName(user!) || "",
|
|
149
|
+
email: User.getPrimaryEmail(user!) || "",
|
|
150
|
+
company: user?.additionalData?.profile?.companyName,
|
|
151
|
+
avatarURL: user?.avatarUrl,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function setProfile(user: User, profile: Profile): User {
|
|
156
|
+
user.fullName = profile.name;
|
|
157
|
+
user.avatarUrl = profile.avatarURL;
|
|
158
|
+
|
|
159
|
+
if (!user.additionalData) {
|
|
160
|
+
user.additionalData = {};
|
|
161
|
+
}
|
|
162
|
+
if (!user.additionalData.profile) {
|
|
163
|
+
user.additionalData.profile = {};
|
|
164
|
+
}
|
|
165
|
+
user.additionalData.profile.emailAddress = profile.email;
|
|
166
|
+
user.additionalData.profile.companyName = profile.company;
|
|
167
|
+
user.additionalData.profile.lastUpdatedDetailsNudge = new Date().toISOString();
|
|
168
|
+
|
|
169
|
+
return user;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// The actual Profile of a User
|
|
173
|
+
export interface Profile {
|
|
174
|
+
name: string;
|
|
175
|
+
email: string;
|
|
176
|
+
company?: string;
|
|
177
|
+
avatarURL?: string;
|
|
178
|
+
}
|
|
179
|
+
export namespace Profile {
|
|
180
|
+
export function hasChanges(before: Profile, after: Profile) {
|
|
181
|
+
return (
|
|
182
|
+
before.name !== after.name ||
|
|
183
|
+
before.email !== after.email ||
|
|
184
|
+
before.company !== after.company ||
|
|
185
|
+
before.avatarURL !== after.avatarURL
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
139
189
|
}
|
|
140
190
|
|
|
141
191
|
export interface AdditionalUserData {
|
|
@@ -150,9 +200,22 @@ export interface AdditionalUserData {
|
|
|
150
200
|
oauthClientsApproved?: { [key: string]: string };
|
|
151
201
|
// to remember GH Orgs the user installed/updated the GH App for
|
|
152
202
|
knownGitHubOrgs?: string[];
|
|
153
|
-
|
|
154
203
|
// Git clone URL pointing to the user's dotfile repo
|
|
155
204
|
dotfileRepo?: string;
|
|
205
|
+
// preferred workspace classes
|
|
206
|
+
workspaceClasses?: WorkspaceClasses;
|
|
207
|
+
// additional user profile data
|
|
208
|
+
profile?: ProfileDetails;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// The format in which we store User Profiles in
|
|
212
|
+
export interface ProfileDetails {
|
|
213
|
+
// when was the last time the user updated their profile information or has been nudged to do so.
|
|
214
|
+
lastUpdatedDetailsNudge?: string;
|
|
215
|
+
// the user's company name
|
|
216
|
+
companyName?: string;
|
|
217
|
+
// the user's email
|
|
218
|
+
emailAddress?: string;
|
|
156
219
|
}
|
|
157
220
|
|
|
158
221
|
export interface EmailNotificationSettings {
|
|
@@ -171,6 +234,11 @@ export type IDESettings = {
|
|
|
171
234
|
useLatestVersion?: boolean;
|
|
172
235
|
};
|
|
173
236
|
|
|
237
|
+
export interface WorkspaceClasses {
|
|
238
|
+
regular: string;
|
|
239
|
+
prebuild: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
174
242
|
export interface UserPlatform {
|
|
175
243
|
uid: string;
|
|
176
244
|
userAgent: string;
|
|
@@ -205,6 +273,7 @@ export const WorkspaceFeatureFlags = {
|
|
|
205
273
|
full_workspace_backup: undefined,
|
|
206
274
|
fixed_resources: undefined,
|
|
207
275
|
persistent_volume_claim: undefined,
|
|
276
|
+
protected_secrets: undefined,
|
|
208
277
|
};
|
|
209
278
|
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
|
|
210
279
|
|
|
@@ -243,7 +312,7 @@ export namespace UserEnvVar {
|
|
|
243
312
|
return "Name must not be empty.";
|
|
244
313
|
}
|
|
245
314
|
if (name.length > 255) {
|
|
246
|
-
return
|
|
315
|
+
return "Name too long. Maximum name length is 255 characters.";
|
|
247
316
|
}
|
|
248
317
|
if (!/^[a-zA-Z_]+[a-zA-Z0-9_]*$/.test(name)) {
|
|
249
318
|
return "Name must match /^[a-zA-Z_]+[a-zA-Z0-9_]*$/.";
|
|
@@ -252,7 +321,7 @@ export namespace UserEnvVar {
|
|
|
252
321
|
return "Value must not be empty.";
|
|
253
322
|
}
|
|
254
323
|
if (variable.value.length > 32767) {
|
|
255
|
-
return
|
|
324
|
+
return "Value too long. Maximum value length is 32767 characters.";
|
|
256
325
|
}
|
|
257
326
|
if (pattern.trim() === "") {
|
|
258
327
|
return "Scope must not be empty.";
|
|
@@ -356,6 +425,68 @@ export namespace UserEnvVar {
|
|
|
356
425
|
}
|
|
357
426
|
}
|
|
358
427
|
|
|
428
|
+
export interface SSHPublicKeyValue {
|
|
429
|
+
name: string;
|
|
430
|
+
key: string;
|
|
431
|
+
}
|
|
432
|
+
export interface UserSSHPublicKey extends SSHPublicKeyValue {
|
|
433
|
+
id: string;
|
|
434
|
+
key: string;
|
|
435
|
+
userId: string;
|
|
436
|
+
fingerprint: string;
|
|
437
|
+
creationTime: string;
|
|
438
|
+
lastUsedTime?: string;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export type UserSSHPublicKeyValue = Omit<UserSSHPublicKey, "key" | "userId">;
|
|
442
|
+
|
|
443
|
+
export namespace SSHPublicKeyValue {
|
|
444
|
+
export function validate(value: SSHPublicKeyValue): string | undefined {
|
|
445
|
+
if (value.name.length === 0) {
|
|
446
|
+
return "Title must not be empty.";
|
|
447
|
+
}
|
|
448
|
+
if (value.name.length > 255) {
|
|
449
|
+
return "Title too long. Maximum value length is 255 characters.";
|
|
450
|
+
}
|
|
451
|
+
if (value.key.length === 0) {
|
|
452
|
+
return "Key must not be empty.";
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
getData(value);
|
|
456
|
+
} catch (e) {
|
|
457
|
+
return "Key is invalid. You must supply a key in OpenSSH public key format.";
|
|
458
|
+
}
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export function getData(value: SSHPublicKeyValue) {
|
|
463
|
+
// Begins with 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com'.
|
|
464
|
+
const regex =
|
|
465
|
+
/^(?<type>ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519|sk-ecdsa-sha2-nistp256@openssh\.com|sk-ssh-ed25519@openssh\.com) (?<key>.*?)( (?<email>.*?))?$/;
|
|
466
|
+
const resultGroup = regex.exec(value.key.trim());
|
|
467
|
+
if (!resultGroup) {
|
|
468
|
+
throw new Error("Key is invalid.");
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
type: resultGroup.groups?.["type"] as string,
|
|
472
|
+
key: resultGroup.groups?.["key"] as string,
|
|
473
|
+
email: resultGroup.groups?.["email"] || undefined,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function getFingerprint(value: SSHPublicKeyValue) {
|
|
478
|
+
const data = getData(value);
|
|
479
|
+
let buf = Buffer.from(data.key, "base64");
|
|
480
|
+
// gitlab style
|
|
481
|
+
// const hash = createHash("md5").update(buf).digest("hex");
|
|
482
|
+
// github style
|
|
483
|
+
const hash = createHash("sha256").update(buf).digest("base64");
|
|
484
|
+
return hash;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export const MAXIMUM_KEY_LENGTH = 5;
|
|
488
|
+
}
|
|
489
|
+
|
|
359
490
|
export interface GitpodToken {
|
|
360
491
|
/** Hash value (SHA256) of the token (primary key). */
|
|
361
492
|
tokenHash: string;
|
|
@@ -490,8 +621,8 @@ export interface Snapshot {
|
|
|
490
621
|
|
|
491
622
|
export interface VolumeSnapshot {
|
|
492
623
|
id: string;
|
|
624
|
+
workspaceId: string;
|
|
493
625
|
creationTime: string;
|
|
494
|
-
originalWorkspaceId: string;
|
|
495
626
|
volumeHandle: string;
|
|
496
627
|
}
|
|
497
628
|
|
|
@@ -564,7 +695,7 @@ export interface Workspace {
|
|
|
564
695
|
|
|
565
696
|
export type WorkspaceSoftDeletion = "user" | "gc";
|
|
566
697
|
|
|
567
|
-
export type WorkspaceType = "regular" | "prebuild" | "probe";
|
|
698
|
+
export type WorkspaceType = "regular" | "prebuild" | "probe"; // TODO(gpl) Removed prove here
|
|
568
699
|
|
|
569
700
|
export namespace Workspace {
|
|
570
701
|
export function getFullRepositoryName(ws: Workspace): string | undefined {
|
|
@@ -640,6 +771,7 @@ export interface JetBrainsConfig {
|
|
|
640
771
|
}
|
|
641
772
|
export interface JetBrainsProductConfig {
|
|
642
773
|
prebuilds?: JetBrainsPrebuilds;
|
|
774
|
+
vmoptions?: string;
|
|
643
775
|
}
|
|
644
776
|
export interface JetBrainsPrebuilds {
|
|
645
777
|
version?: "stable" | "latest" | "both";
|
|
@@ -755,7 +887,9 @@ export interface PrebuiltWorkspace {
|
|
|
755
887
|
|
|
756
888
|
export namespace PrebuiltWorkspace {
|
|
757
889
|
export function isDone(pws: PrebuiltWorkspace) {
|
|
758
|
-
return
|
|
890
|
+
return (
|
|
891
|
+
pws.state === "available" || pws.state === "timeout" || pws.state === "aborted" || pws.state === "failed"
|
|
892
|
+
);
|
|
759
893
|
}
|
|
760
894
|
|
|
761
895
|
export function isAvailable(pws: PrebuiltWorkspace) {
|
|
@@ -879,6 +1013,7 @@ export namespace ExternalImageConfigFile {
|
|
|
879
1013
|
|
|
880
1014
|
export interface WorkspaceContext {
|
|
881
1015
|
title: string;
|
|
1016
|
+
ref?: string;
|
|
882
1017
|
/** This contains the URL portion of the contextURL (which might contain other modifiers as well). It's optional because it's not set for older workspaces. */
|
|
883
1018
|
normalizedContextURL?: string;
|
|
884
1019
|
forceCreateNewWorkspace?: boolean;
|
|
@@ -1347,3 +1482,11 @@ export interface Terms {
|
|
|
1347
1482
|
readonly content: string;
|
|
1348
1483
|
readonly formElements?: object;
|
|
1349
1484
|
}
|
|
1485
|
+
|
|
1486
|
+
export interface CostCenter {
|
|
1487
|
+
readonly id: string;
|
|
1488
|
+
/**
|
|
1489
|
+
* Unit: credits
|
|
1490
|
+
*/
|
|
1491
|
+
spendingLimit: number;
|
|
1492
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { PrebuiltWorkspaceState } from "./protocol";
|
|
8
8
|
import { v4 as uuidv4 } from "uuid";
|
|
9
9
|
import { DeepPartial } from "./util/deep-partial";
|
|
10
|
+
import { WebhookEvent } from "./webhook-event";
|
|
10
11
|
|
|
11
12
|
export interface ProjectConfig {
|
|
12
13
|
".gitpod.yml": string;
|
|
@@ -14,6 +15,8 @@ export interface ProjectConfig {
|
|
|
14
15
|
|
|
15
16
|
export interface ProjectSettings {
|
|
16
17
|
useIncrementalPrebuilds?: boolean;
|
|
18
|
+
usePersistentVolumeClaim?: boolean;
|
|
19
|
+
keepOutdatedPrebuildsRunning?: boolean;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export interface Project {
|
|
@@ -150,3 +153,15 @@ export interface TeamMembershipInvite {
|
|
|
150
153
|
/** This is a flag that triggers the HARD DELETION of this entity */
|
|
151
154
|
deleted?: boolean;
|
|
152
155
|
}
|
|
156
|
+
|
|
157
|
+
export interface PrebuildEvent {
|
|
158
|
+
id: string;
|
|
159
|
+
creationTime: string;
|
|
160
|
+
status: WebhookEvent.Status | WebhookEvent.PrebuildStatus;
|
|
161
|
+
message?: string;
|
|
162
|
+
prebuildId?: string;
|
|
163
|
+
projectId?: string;
|
|
164
|
+
cloneUrl?: string;
|
|
165
|
+
branch?: string;
|
|
166
|
+
commit?: string;
|
|
167
|
+
}
|
package/src/usage.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WorkspaceType } from "./protocol";
|
|
8
|
+
|
|
9
|
+
export interface BillableSession {
|
|
10
|
+
// The id of the one paying the bill
|
|
11
|
+
attributionId: string;
|
|
12
|
+
|
|
13
|
+
// Relevant for workspace type. When prebuild, shows "prebuild"
|
|
14
|
+
userId?: string;
|
|
15
|
+
teamId?: string;
|
|
16
|
+
|
|
17
|
+
instanceId: string;
|
|
18
|
+
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
|
|
21
|
+
workspaceType: BillableWorkspaceType;
|
|
22
|
+
|
|
23
|
+
workspaceClass: string;
|
|
24
|
+
|
|
25
|
+
// When the workspace started
|
|
26
|
+
startTime: string;
|
|
27
|
+
|
|
28
|
+
// When the workspace ended. Not set when the workspace is still running.
|
|
29
|
+
endTime?: string;
|
|
30
|
+
|
|
31
|
+
// The credits used for this session
|
|
32
|
+
credits: number;
|
|
33
|
+
|
|
34
|
+
// TODO - maybe
|
|
35
|
+
projectId?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BillableSessionRequest {
|
|
39
|
+
attributionId: string;
|
|
40
|
+
startedTimeOrder: SortOrder;
|
|
41
|
+
from?: number;
|
|
42
|
+
to?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type BillableWorkspaceType = WorkspaceType;
|
|
46
|
+
|
|
47
|
+
export enum SortOrder {
|
|
48
|
+
Descending = 0,
|
|
49
|
+
Ascending = 1,
|
|
50
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
|
|
3
|
+
* Licensed under the GNU Affero General Public License (AGPL).
|
|
4
|
+
* See License-AGPL.txt in the project root for license information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as http from "http";
|
|
8
|
+
import * as express from "express";
|
|
9
|
+
import { injectable, postConstruct } from "inversify";
|
|
10
|
+
import { log, LogrusLogLevel } from "./logging";
|
|
11
|
+
|
|
12
|
+
export interface SetLogLevelRequest {
|
|
13
|
+
level: LogrusLogLevel;
|
|
14
|
+
}
|
|
15
|
+
export namespace SetLogLevelRequest {
|
|
16
|
+
export function is(o: any): o is SetLogLevelRequest {
|
|
17
|
+
return typeof o === "object" && "level" in o;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class DebugApp {
|
|
23
|
+
protected _app: express.Application;
|
|
24
|
+
protected httpServer: http.Server | undefined = undefined;
|
|
25
|
+
|
|
26
|
+
@postConstruct()
|
|
27
|
+
public ctor() {
|
|
28
|
+
this._app = this.create();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
create(): express.Application {
|
|
32
|
+
const app = express();
|
|
33
|
+
|
|
34
|
+
app.use(express.json());
|
|
35
|
+
app.use(express.urlencoded({ extended: true }));
|
|
36
|
+
|
|
37
|
+
app.post("/debug/logging", (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const levelRequest = req.body;
|
|
40
|
+
if (!SetLogLevelRequest.is(levelRequest)) {
|
|
41
|
+
res.status(400).end("not a SetLogLevelRequest");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const newLogLevel = levelRequest.level;
|
|
46
|
+
log.setLogLevel(newLogLevel);
|
|
47
|
+
log.info("set log level", { newLogLevel });
|
|
48
|
+
res.status(200).end(JSON.stringify(levelRequest));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
res.status(500).end(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return app;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public start(port: number = 6060) {
|
|
57
|
+
this.httpServer = this._app.listen(port, "localhost", () => {
|
|
58
|
+
log.info(`debug server listening on port: ${port}`);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async stop() {
|
|
63
|
+
const server = this.httpServer;
|
|
64
|
+
if (!server) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
return new Promise<void>((resolve) =>
|
|
68
|
+
server.close((err: any) => {
|
|
69
|
+
if (err) {
|
|
70
|
+
log.warn(`error while closing http server`, { err });
|
|
71
|
+
}
|
|
72
|
+
this.httpServer = undefined;
|
|
73
|
+
resolve();
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public get app(): express.Application {
|
|
79
|
+
return this._app;
|
|
80
|
+
}
|
|
81
|
+
}
|