@gitpod/gitpod-protocol 0.1.5-prs-prebuilds.16 → 0.1.5-release-2022.6.1.1
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 +18 -2
- package/lib/accounting-protocol.d.ts +7 -0
- package/lib/accounting-protocol.d.ts.map +1 -1
- package/lib/accounting-protocol.js +8 -1
- package/lib/accounting-protocol.js.map +1 -1
- 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 +20 -2
- package/lib/gitpod-service.d.ts.map +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/license-protocol.d.ts +7 -0
- package/lib/license-protocol.d.ts.map +1 -1
- package/lib/license-protocol.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 +56 -1
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +97 -3
- 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 +16 -0
- package/lib/team-subscription-protocol.d.ts.map +1 -1
- package/lib/team-subscription-protocol.js +12 -1
- package/lib/team-subscription-protocol.js.map +1 -1
- package/lib/teams-projects-protocol.d.ts +2 -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 +22 -0
- package/lib/usage.d.ts.map +1 -0
- package/lib/usage.js +115 -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/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-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.js +1 -1
- package/package.json +9 -6
- package/pkg-yarn.lock +7 -5
- package/provenance-bundle.jsonl +1 -1
- package/src/accounting-protocol.ts +10 -0
- 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 +31 -1
- package/src/index.ts +1 -0
- package/src/installation-admin-protocol.ts +3 -0
- package/src/license-protocol.ts +7 -0
- package/src/messaging/error.ts +3 -0
- package/src/protocol.spec.ts +97 -0
- package/src/protocol.ts +151 -4
- package/src/team-subscription-protocol.ts +24 -0
- package/src/teams-projects-protocol.ts +2 -0
- package/src/usage.ts +146 -0
- package/src/util/debug-app.ts +81 -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-cluster.ts +2 -4
- package/src/workspace-instance.ts +17 -0
- package/src/wsready.ts +1 -1
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,
|
|
@@ -47,10 +50,17 @@ import { LicenseService } from "./license-protocol";
|
|
|
47
50
|
import { Emitter } from "./util/event";
|
|
48
51
|
import { AccountStatement, CreditAlert } from "./accounting-protocol";
|
|
49
52
|
import { GithubUpgradeURL, PlanCoupon } from "./payment-protocol";
|
|
50
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
TeamSubscription,
|
|
55
|
+
TeamSubscription2,
|
|
56
|
+
TeamSubscriptionSlot,
|
|
57
|
+
TeamSubscriptionSlotResolved,
|
|
58
|
+
} from "./team-subscription-protocol";
|
|
51
59
|
import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics";
|
|
52
60
|
import { IDEServer } from "./ide-protocol";
|
|
53
61
|
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
|
|
62
|
+
import { Currency } from "./plans";
|
|
63
|
+
import { BillableSession } from "./usage";
|
|
54
64
|
|
|
55
65
|
export interface GitpodClient {
|
|
56
66
|
onInstanceUpdate(instance: WorkspaceInstance): void;
|
|
@@ -142,6 +152,12 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
142
152
|
setEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
143
153
|
deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
|
|
144
154
|
|
|
155
|
+
// User SSH Keys
|
|
156
|
+
hasSSHPublicKey(): Promise<boolean>;
|
|
157
|
+
getSSHPublicKeys(): Promise<UserSSHPublicKeyValue[]>;
|
|
158
|
+
addSSHPublicKey(value: SSHPublicKeyValue): Promise<UserSSHPublicKeyValue>;
|
|
159
|
+
deleteSSHPublicKey(id: string): Promise<void>;
|
|
160
|
+
|
|
145
161
|
// Teams
|
|
146
162
|
getTeams(): Promise<Team[]>;
|
|
147
163
|
getTeamMembers(teamId: string): Promise<TeamMemberInfo[]>;
|
|
@@ -166,6 +182,8 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
166
182
|
getUserProjects(): Promise<Project[]>;
|
|
167
183
|
getProjectOverview(projectId: string): Promise<Project.Overview | undefined>;
|
|
168
184
|
findPrebuilds(params: FindPrebuildsParams): Promise<PrebuildWithStatus[]>;
|
|
185
|
+
findPrebuildByWorkspaceID(workspaceId: string): Promise<PrebuiltWorkspace | undefined>;
|
|
186
|
+
getPrebuild(prebuildId: string): Promise<PrebuildWithStatus | undefined>;
|
|
169
187
|
triggerPrebuild(projectId: string, branchName: string | null): Promise<StartPrebuildResult>;
|
|
170
188
|
cancelPrebuild(projectId: string, prebuildId: string): Promise<void>;
|
|
171
189
|
fetchProjectRepositoryConfiguration(projectId: string): Promise<string | undefined>;
|
|
@@ -235,7 +253,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
235
253
|
*/
|
|
236
254
|
getChargebeeSiteId(): Promise<string>;
|
|
237
255
|
createPortalSession(): Promise<{}>;
|
|
256
|
+
createTeamPortalSession(teamId: string): Promise<{}>;
|
|
238
257
|
checkout(planId: string, planQuantity?: number): Promise<{}>;
|
|
258
|
+
teamCheckout(teamId: string, planId: string): Promise<{}>;
|
|
239
259
|
getAvailableCoupons(): Promise<PlanCoupon[]>;
|
|
240
260
|
getAppliedCoupons(): Promise<PlanCoupon[]>;
|
|
241
261
|
|
|
@@ -247,6 +267,7 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
247
267
|
subscriptionCancel(subscriptionId: string): Promise<void>;
|
|
248
268
|
subscriptionCancelDowngrade(subscriptionId: string): Promise<void>;
|
|
249
269
|
|
|
270
|
+
getTeamSubscription(teamId: string): Promise<TeamSubscription2 | undefined>;
|
|
250
271
|
tsGet(): Promise<TeamSubscription[]>;
|
|
251
272
|
tsGetSlots(): Promise<TeamSubscriptionSlotResolved[]>;
|
|
252
273
|
tsGetUnassignedSlot(teamSubscriptionId: string): Promise<TeamSubscriptionSlot | undefined>;
|
|
@@ -262,6 +283,14 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
|
|
|
262
283
|
|
|
263
284
|
getGithubUpgradeUrls(): Promise<GithubUpgradeURL[]>;
|
|
264
285
|
|
|
286
|
+
getStripePublishableKey(): Promise<string>;
|
|
287
|
+
getStripeSetupIntentClientSecret(): Promise<string>;
|
|
288
|
+
findStripeSubscriptionIdForTeam(teamId: string): Promise<string | undefined>;
|
|
289
|
+
subscribeTeamToStripe(teamId: string, setupIntentId: string, currency: Currency): Promise<void>;
|
|
290
|
+
getStripePortalUrlForTeam(teamId: string): Promise<string>;
|
|
291
|
+
|
|
292
|
+
getBilledUsage(attributionId: string): Promise<BillableSession[]>;
|
|
293
|
+
|
|
265
294
|
/**
|
|
266
295
|
* Analytics
|
|
267
296
|
*/
|
|
@@ -365,6 +394,7 @@ export interface SetWorkspaceTimeoutResult {
|
|
|
365
394
|
|
|
366
395
|
export interface GetWorkspaceTimeoutResult {
|
|
367
396
|
duration: WorkspaceTimeoutDuration;
|
|
397
|
+
durationRaw: string;
|
|
368
398
|
canChange: boolean;
|
|
369
399
|
}
|
|
370
400
|
|
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 {
|
package/src/license-protocol.ts
CHANGED
|
@@ -15,9 +15,15 @@ export type LicenseIssue = "seats-exhausted";
|
|
|
15
15
|
export interface LicenseInfo {
|
|
16
16
|
key: string;
|
|
17
17
|
seats: number;
|
|
18
|
+
userCount?: number;
|
|
18
19
|
valid: boolean;
|
|
19
20
|
validUntil: string;
|
|
20
21
|
plan?: string;
|
|
22
|
+
features?: string[];
|
|
23
|
+
enabledFeatures?: string[];
|
|
24
|
+
type?: string;
|
|
25
|
+
errorMsg?: string;
|
|
26
|
+
fallbackAllowed: boolean;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
export interface GetLicenseInfoResult {
|
|
@@ -33,5 +39,6 @@ export enum LicenseFeature {
|
|
|
33
39
|
export interface LicenseService {
|
|
34
40
|
validateLicense(): Promise<LicenseValidationResult>;
|
|
35
41
|
getLicenseInfo(): Promise<GetLicenseInfoResult>;
|
|
42
|
+
adminGetLicense(): Promise<LicenseInfo>;
|
|
36
43
|
licenseIncludesFeature(feature: LicenseFeature): Promise<boolean>;
|
|
37
44
|
}
|
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
|
@@ -70,11 +70,14 @@ export namespace User {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Returns the stored email or if it doesn't exist returns the primaryEmail of the first identity this user signed up with.
|
|
74
74
|
* @param user
|
|
75
75
|
* @returns A primaryEmail, or undefined if there is none.
|
|
76
76
|
*/
|
|
77
77
|
export function getPrimaryEmail(user: User): string | undefined {
|
|
78
|
+
if (user.additionalData?.profile?.emailAddress) {
|
|
79
|
+
return user.additionalData?.profile?.emailAddress;
|
|
80
|
+
}
|
|
78
81
|
const identities = user.identities.filter((i) => !!i.primaryEmail);
|
|
79
82
|
if (identities.length <= 0) {
|
|
80
83
|
return undefined;
|
|
@@ -136,6 +139,50 @@ export namespace User {
|
|
|
136
139
|
}
|
|
137
140
|
user.additionalData.ideSettings = newIDESettings;
|
|
138
141
|
}
|
|
142
|
+
|
|
143
|
+
export function getProfile(user: User): Profile {
|
|
144
|
+
return {
|
|
145
|
+
name: User.getName(user!) || "",
|
|
146
|
+
email: User.getPrimaryEmail(user!) || "",
|
|
147
|
+
company: user?.additionalData?.profile?.companyName,
|
|
148
|
+
avatarURL: user?.avatarUrl,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function setProfile(user: User, profile: Profile): User {
|
|
153
|
+
user.fullName = profile.name;
|
|
154
|
+
user.avatarUrl = profile.avatarURL;
|
|
155
|
+
|
|
156
|
+
if (!user.additionalData) {
|
|
157
|
+
user.additionalData = {};
|
|
158
|
+
}
|
|
159
|
+
if (!user.additionalData.profile) {
|
|
160
|
+
user.additionalData.profile = {};
|
|
161
|
+
}
|
|
162
|
+
user.additionalData.profile.emailAddress = profile.email;
|
|
163
|
+
user.additionalData.profile.companyName = profile.company;
|
|
164
|
+
user.additionalData.profile.lastUpdatedDetailsNudge = new Date().toISOString();
|
|
165
|
+
|
|
166
|
+
return user;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The actual Profile of a User
|
|
170
|
+
export interface Profile {
|
|
171
|
+
name: string;
|
|
172
|
+
email: string;
|
|
173
|
+
company?: string;
|
|
174
|
+
avatarURL?: string;
|
|
175
|
+
}
|
|
176
|
+
export namespace Profile {
|
|
177
|
+
export function hasChanges(before: Profile, after: Profile) {
|
|
178
|
+
return (
|
|
179
|
+
before.name !== after.name ||
|
|
180
|
+
before.email !== after.email ||
|
|
181
|
+
before.company !== after.company ||
|
|
182
|
+
before.avatarURL !== after.avatarURL
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
139
186
|
}
|
|
140
187
|
|
|
141
188
|
export interface AdditionalUserData {
|
|
@@ -150,9 +197,24 @@ export interface AdditionalUserData {
|
|
|
150
197
|
oauthClientsApproved?: { [key: string]: string };
|
|
151
198
|
// to remember GH Orgs the user installed/updated the GH App for
|
|
152
199
|
knownGitHubOrgs?: string[];
|
|
153
|
-
|
|
154
200
|
// Git clone URL pointing to the user's dotfile repo
|
|
155
201
|
dotfileRepo?: string;
|
|
202
|
+
// Identifies an explicit team or user ID to which all the user's workspace usage should be attributed to (e.g. for billing purposes)
|
|
203
|
+
usageAttributionId?: string;
|
|
204
|
+
// preferred workspace classes
|
|
205
|
+
workspaceClasses?: WorkspaceClasses;
|
|
206
|
+
// additional user profile data
|
|
207
|
+
profile?: ProfileDetails;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The format in which we store User Profiles in
|
|
211
|
+
export interface ProfileDetails {
|
|
212
|
+
// when was the last time the user updated their profile information or has been nudged to do so.
|
|
213
|
+
lastUpdatedDetailsNudge?: string;
|
|
214
|
+
// the user's company name
|
|
215
|
+
companyName?: string;
|
|
216
|
+
// the user's email
|
|
217
|
+
emailAddress?: string;
|
|
156
218
|
}
|
|
157
219
|
|
|
158
220
|
export interface EmailNotificationSettings {
|
|
@@ -171,6 +233,11 @@ export type IDESettings = {
|
|
|
171
233
|
useLatestVersion?: boolean;
|
|
172
234
|
};
|
|
173
235
|
|
|
236
|
+
export interface WorkspaceClasses {
|
|
237
|
+
regular: string;
|
|
238
|
+
prebuild: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
174
241
|
export interface UserPlatform {
|
|
175
242
|
uid: string;
|
|
176
243
|
userAgent: string;
|
|
@@ -205,6 +272,7 @@ export const WorkspaceFeatureFlags = {
|
|
|
205
272
|
full_workspace_backup: undefined,
|
|
206
273
|
fixed_resources: undefined,
|
|
207
274
|
persistent_volume_claim: undefined,
|
|
275
|
+
protected_secrets: undefined,
|
|
208
276
|
};
|
|
209
277
|
export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags;
|
|
210
278
|
|
|
@@ -242,12 +310,18 @@ export namespace UserEnvVar {
|
|
|
242
310
|
if (name.trim() === "") {
|
|
243
311
|
return "Name must not be empty.";
|
|
244
312
|
}
|
|
313
|
+
if (name.length > 255) {
|
|
314
|
+
return "Name too long. Maximum name length is 255 characters.";
|
|
315
|
+
}
|
|
245
316
|
if (!/^[a-zA-Z_]+[a-zA-Z0-9_]*$/.test(name)) {
|
|
246
317
|
return "Name must match /^[a-zA-Z_]+[a-zA-Z0-9_]*$/.";
|
|
247
318
|
}
|
|
248
319
|
if (variable.value.trim() === "") {
|
|
249
320
|
return "Value must not be empty.";
|
|
250
321
|
}
|
|
322
|
+
if (variable.value.length > 32767) {
|
|
323
|
+
return "Value too long. Maximum value length is 32767 characters.";
|
|
324
|
+
}
|
|
251
325
|
if (pattern.trim() === "") {
|
|
252
326
|
return "Scope must not be empty.";
|
|
253
327
|
}
|
|
@@ -350,6 +424,68 @@ export namespace UserEnvVar {
|
|
|
350
424
|
}
|
|
351
425
|
}
|
|
352
426
|
|
|
427
|
+
export interface SSHPublicKeyValue {
|
|
428
|
+
name: string;
|
|
429
|
+
key: string;
|
|
430
|
+
}
|
|
431
|
+
export interface UserSSHPublicKey extends SSHPublicKeyValue {
|
|
432
|
+
id: string;
|
|
433
|
+
key: string;
|
|
434
|
+
userId: string;
|
|
435
|
+
fingerprint: string;
|
|
436
|
+
creationTime: string;
|
|
437
|
+
lastUsedTime?: string;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export type UserSSHPublicKeyValue = Omit<UserSSHPublicKey, "key" | "userId">;
|
|
441
|
+
|
|
442
|
+
export namespace SSHPublicKeyValue {
|
|
443
|
+
export function validate(value: SSHPublicKeyValue): string | undefined {
|
|
444
|
+
if (value.name.length === 0) {
|
|
445
|
+
return "Title must not be empty.";
|
|
446
|
+
}
|
|
447
|
+
if (value.name.length > 255) {
|
|
448
|
+
return "Title too long. Maximum value length is 255 characters.";
|
|
449
|
+
}
|
|
450
|
+
if (value.key.length === 0) {
|
|
451
|
+
return "Key must not be empty.";
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
getData(value);
|
|
455
|
+
} catch (e) {
|
|
456
|
+
return "Key is invalid. You must supply a key in OpenSSH public key format.";
|
|
457
|
+
}
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function getData(value: SSHPublicKeyValue) {
|
|
462
|
+
// 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'.
|
|
463
|
+
const regex =
|
|
464
|
+
/^(?<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>.*?))?$/;
|
|
465
|
+
const resultGroup = regex.exec(value.key.trim());
|
|
466
|
+
if (!resultGroup) {
|
|
467
|
+
throw new Error("Key is invalid.");
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
type: resultGroup.groups?.["type"] as string,
|
|
471
|
+
key: resultGroup.groups?.["key"] as string,
|
|
472
|
+
email: resultGroup.groups?.["email"] || undefined,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function getFingerprint(value: SSHPublicKeyValue) {
|
|
477
|
+
const data = getData(value);
|
|
478
|
+
let buf = Buffer.from(data.key, "base64");
|
|
479
|
+
// gitlab style
|
|
480
|
+
// const hash = createHash("md5").update(buf).digest("hex");
|
|
481
|
+
// github style
|
|
482
|
+
const hash = createHash("sha256").update(buf).digest("base64");
|
|
483
|
+
return hash;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export const MAXIMUM_KEY_LENGTH = 5;
|
|
487
|
+
}
|
|
488
|
+
|
|
353
489
|
export interface GitpodToken {
|
|
354
490
|
/** Hash value (SHA256) of the token (primary key). */
|
|
355
491
|
tokenHash: string;
|
|
@@ -482,6 +618,13 @@ export interface Snapshot {
|
|
|
482
618
|
message?: string;
|
|
483
619
|
}
|
|
484
620
|
|
|
621
|
+
export interface VolumeSnapshot {
|
|
622
|
+
id: string;
|
|
623
|
+
workspaceId: string;
|
|
624
|
+
creationTime: string;
|
|
625
|
+
volumeHandle: string;
|
|
626
|
+
}
|
|
627
|
+
|
|
485
628
|
export type SnapshotState = "pending" | "available" | "error";
|
|
486
629
|
|
|
487
630
|
export interface LayoutData {
|
|
@@ -551,7 +694,7 @@ export interface Workspace {
|
|
|
551
694
|
|
|
552
695
|
export type WorkspaceSoftDeletion = "user" | "gc";
|
|
553
696
|
|
|
554
|
-
export type WorkspaceType = "regular" | "prebuild" | "probe";
|
|
697
|
+
export type WorkspaceType = "regular" | "prebuild" | "probe"; // TODO(gpl) Removed prove here
|
|
555
698
|
|
|
556
699
|
export namespace Workspace {
|
|
557
700
|
export function getFullRepositoryName(ws: Workspace): string | undefined {
|
|
@@ -627,6 +770,7 @@ export interface JetBrainsConfig {
|
|
|
627
770
|
}
|
|
628
771
|
export interface JetBrainsProductConfig {
|
|
629
772
|
prebuilds?: JetBrainsPrebuilds;
|
|
773
|
+
vmoptions?: string;
|
|
630
774
|
}
|
|
631
775
|
export interface JetBrainsPrebuilds {
|
|
632
776
|
version?: "stable" | "latest" | "both";
|
|
@@ -742,7 +886,9 @@ export interface PrebuiltWorkspace {
|
|
|
742
886
|
|
|
743
887
|
export namespace PrebuiltWorkspace {
|
|
744
888
|
export function isDone(pws: PrebuiltWorkspace) {
|
|
745
|
-
return
|
|
889
|
+
return (
|
|
890
|
+
pws.state === "available" || pws.state === "timeout" || pws.state === "aborted" || pws.state === "failed"
|
|
891
|
+
);
|
|
746
892
|
}
|
|
747
893
|
|
|
748
894
|
export function isAvailable(pws: PrebuiltWorkspace) {
|
|
@@ -866,6 +1012,7 @@ export namespace ExternalImageConfigFile {
|
|
|
866
1012
|
|
|
867
1013
|
export interface WorkspaceContext {
|
|
868
1014
|
title: string;
|
|
1015
|
+
ref?: string;
|
|
869
1016
|
/** 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. */
|
|
870
1017
|
normalizedContextURL?: string;
|
|
871
1018
|
forceCreateNewWorkspace?: boolean;
|
|
@@ -33,6 +33,30 @@ export namespace TeamSubscription {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export interface TeamSubscription2 {
|
|
37
|
+
id: string;
|
|
38
|
+
teamId: string;
|
|
39
|
+
planId: string;
|
|
40
|
+
startDate: string;
|
|
41
|
+
endDate?: string;
|
|
42
|
+
quantity: number;
|
|
43
|
+
/** The Chargebee subscription id */
|
|
44
|
+
paymentReference: string;
|
|
45
|
+
cancellationDate?: string;
|
|
46
|
+
excludeFromMoreResources: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export namespace TeamSubscription2 {
|
|
50
|
+
export const create = (ts2: Omit<TeamSubscription2, "id">): TeamSubscription2 => {
|
|
51
|
+
const withId = ts2 as TeamSubscription2;
|
|
52
|
+
withId.id = uuidv4();
|
|
53
|
+
return withId;
|
|
54
|
+
};
|
|
55
|
+
export const isActive = (ts2: TeamSubscription2, date: string): boolean => {
|
|
56
|
+
return ts2.startDate <= date && (ts2.endDate === undefined || date < ts2.endDate);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
36
60
|
/**
|
|
37
61
|
* A slot represents one unit of a TeamSubscription that gets assigned to one user at a time
|
|
38
62
|
*/
|
package/src/usage.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
// "standard" or "XL"
|
|
24
|
+
workspaceClass: string;
|
|
25
|
+
|
|
26
|
+
// When the workspace started
|
|
27
|
+
startTime: string;
|
|
28
|
+
|
|
29
|
+
// When the workspace ended
|
|
30
|
+
endTime: string;
|
|
31
|
+
|
|
32
|
+
// The credits used for this session
|
|
33
|
+
credits: number;
|
|
34
|
+
|
|
35
|
+
// TODO - maybe
|
|
36
|
+
projectId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type BillableWorkspaceType = WorkspaceType;
|
|
40
|
+
|
|
41
|
+
export const billableSessionDummyData: BillableSession[] = [
|
|
42
|
+
{
|
|
43
|
+
attributionId: "some-attribution-id",
|
|
44
|
+
userId: "prebuild",
|
|
45
|
+
teamId: "prebuild",
|
|
46
|
+
instanceId: "some-instance-id",
|
|
47
|
+
workspaceId: "some-workspace-id",
|
|
48
|
+
workspaceType: "prebuild",
|
|
49
|
+
workspaceClass: "XL",
|
|
50
|
+
startTime: new Date(Date.now() + -3 * 24 * 3600 * 1000).toISOString(), // 3 days ago
|
|
51
|
+
endTime: new Date().toISOString(),
|
|
52
|
+
credits: 320,
|
|
53
|
+
projectId: "project-123",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
attributionId: "some-attribution-id2",
|
|
57
|
+
userId: "some-user",
|
|
58
|
+
teamId: "some-team",
|
|
59
|
+
instanceId: "some-instance-id2",
|
|
60
|
+
workspaceId: "some-workspace-id2",
|
|
61
|
+
workspaceType: "regular",
|
|
62
|
+
workspaceClass: "standard",
|
|
63
|
+
startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
|
|
64
|
+
endTime: new Date().toISOString(),
|
|
65
|
+
credits: 130,
|
|
66
|
+
projectId: "project-123",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
attributionId: "some-attribution-id3",
|
|
70
|
+
userId: "some-other-user",
|
|
71
|
+
teamId: "some-other-team",
|
|
72
|
+
instanceId: "some-instance-id3",
|
|
73
|
+
workspaceId: "some-workspace-id3",
|
|
74
|
+
workspaceType: "regular",
|
|
75
|
+
workspaceClass: "XL",
|
|
76
|
+
startTime: new Date(Date.now() + -5 * 24 * 3600 * 1000).toISOString(),
|
|
77
|
+
endTime: new Date(Date.now() + -4 * 24 * 3600 * 1000).toISOString(),
|
|
78
|
+
credits: 150,
|
|
79
|
+
projectId: "project-134",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
attributionId: "some-attribution-id4",
|
|
83
|
+
userId: "some-other-user2",
|
|
84
|
+
teamId: "some-other-team2",
|
|
85
|
+
instanceId: "some-instance-id4",
|
|
86
|
+
workspaceId: "some-workspace-id4",
|
|
87
|
+
workspaceType: "regular",
|
|
88
|
+
workspaceClass: "standard",
|
|
89
|
+
startTime: new Date(Date.now() + -10 * 24 * 3600 * 1000).toISOString(),
|
|
90
|
+
endTime: new Date(Date.now() + -9 * 24 * 3600 * 1000).toISOString(),
|
|
91
|
+
credits: 330,
|
|
92
|
+
projectId: "project-137",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
attributionId: "some-attribution-id5",
|
|
96
|
+
userId: "some-other-user3",
|
|
97
|
+
teamId: "some-other-team3",
|
|
98
|
+
instanceId: "some-instance-id5",
|
|
99
|
+
workspaceId: "some-workspace-id5",
|
|
100
|
+
workspaceType: "regular",
|
|
101
|
+
workspaceClass: "XL",
|
|
102
|
+
startTime: new Date(Date.now() + -2 * 24 * 3600 * 1000).toISOString(),
|
|
103
|
+
endTime: new Date().toISOString(),
|
|
104
|
+
credits: 222,
|
|
105
|
+
projectId: "project-138",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
attributionId: "some-attribution-id6",
|
|
109
|
+
userId: "some-other-user4",
|
|
110
|
+
teamId: "some-other-team4",
|
|
111
|
+
instanceId: "some-instance-id6",
|
|
112
|
+
workspaceId: "some-workspace-id3",
|
|
113
|
+
workspaceType: "regular",
|
|
114
|
+
workspaceClass: "XL",
|
|
115
|
+
startTime: new Date(Date.now() + -7 * 24 * 3600 * 1000).toISOString(),
|
|
116
|
+
endTime: new Date(Date.now() + -6 * 24 * 3600 * 1000).toISOString(),
|
|
117
|
+
credits: 300,
|
|
118
|
+
projectId: "project-134",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
attributionId: "some-attribution-id8",
|
|
122
|
+
userId: "some-other-user5",
|
|
123
|
+
teamId: "some-other-team5",
|
|
124
|
+
instanceId: "some-instance-id8",
|
|
125
|
+
workspaceId: "some-workspace-id3",
|
|
126
|
+
workspaceType: "regular",
|
|
127
|
+
workspaceClass: "standard",
|
|
128
|
+
startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
|
|
129
|
+
endTime: new Date().toISOString(),
|
|
130
|
+
credits: 100,
|
|
131
|
+
projectId: "project-567",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
attributionId: "some-attribution-id7",
|
|
135
|
+
userId: "prebuild",
|
|
136
|
+
teamId: "some-other-team7",
|
|
137
|
+
instanceId: "some-instance-id7",
|
|
138
|
+
workspaceId: "some-workspace-id7",
|
|
139
|
+
workspaceType: "prebuild",
|
|
140
|
+
workspaceClass: "XL",
|
|
141
|
+
startTime: new Date(Date.now() + -1 * 24 * 3600 * 1000).toISOString(),
|
|
142
|
+
endTime: new Date().toISOString(),
|
|
143
|
+
credits: 200,
|
|
144
|
+
projectId: "project-345",
|
|
145
|
+
},
|
|
146
|
+
];
|