diff --git a/components/dashboard/src/admin/Settings.tsx b/components/dashboard/src/admin/Settings.tsx index 3744ffa87cc8ea..d571f887254d62 100644 --- a/components/dashboard/src/admin/Settings.tsx +++ b/components/dashboard/src/admin/Settings.tsx @@ -5,15 +5,32 @@ */ import { useContext } from "react"; -import { InstallationAdminSettings } from "@gitpod/gitpod-protocol"; +import { TelemetryData, InstallationAdminSettings } from "@gitpod/gitpod-protocol"; import { AdminContext } from "../admin-context"; import CheckBox from "../components/CheckBox"; import { PageWithSubMenu } from "../components/PageWithSubMenu"; import { getGitpodService } from "../service/service"; import { adminMenu } from "./admin-menu"; +import { useEffect, useState } from "react"; +import InfoBox from "../components/InfoBox"; +import { Redirect } from "react-router-dom"; +import { UserContext } from "../user-context"; export default function Settings() { const { adminSettings, setAdminSettings } = useContext(AdminContext); + const [telemetryData, setTelemetryData] = useState(); + const { user } = useContext(UserContext); + + useEffect(() => { + (async () => { + const data = await getGitpodService().server.adminGetTelemetryData(); + setTelemetryData(data) + })(); + }); + + if (!user || !user?.rolesOrPermissions?.includes('admin')) { + return + } const actuallySetTelemetryPrefs = async (value: InstallationAdminSettings) => { await getGitpodService().server.adminUpdateSettings(value); @@ -26,12 +43,13 @@ export default function Settings() {

Usage Statistics

This is used to provide insights on how you use your cluster so we can provide a better overall experience. Read our Privacy Policy} + desc={The following usage data is sent to provide insights on how you use your Gitpod instance, so we can provide a better overall experience. Read our Privacy Policy} checked={adminSettings?.sendTelemetry ?? false} onChange={(evt) => actuallySetTelemetryPrefs({ sendTelemetry: evt.target.checked, })} /> - +
{JSON.stringify(telemetryData, null, 2)}
+ ) } diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index 363bf56f8d796e..82858184cf3d34 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -30,7 +30,7 @@ import { GithubUpgradeURL, PlanCoupon } from './payment-protocol'; import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from './team-subscription-protocol'; import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from './analytics'; import { IDEServer } from './ide-protocol'; -import { InstallationAdminSettings } from './installation-admin-protocol'; +import { InstallationAdminSettings, TelemetryData } from './installation-admin-protocol'; export interface GitpodClient { onInstanceUpdate(instance: WorkspaceInstance): void; @@ -136,6 +136,7 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, // Admin Settings adminGetSettings(): Promise; adminUpdateSettings(settings: InstallationAdminSettings): Promise; + adminGetTelemetryData(): Promise; // Projects getProviderRepositoriesForUser(params: GetProviderRepositoriesParams): Promise; diff --git a/components/gitpod-protocol/src/installation-admin-protocol.ts b/components/gitpod-protocol/src/installation-admin-protocol.ts index 94eb746c5a1476..5d84bd1a4db6ab 100644 --- a/components/gitpod-protocol/src/installation-admin-protocol.ts +++ b/components/gitpod-protocol/src/installation-admin-protocol.ts @@ -23,7 +23,7 @@ export interface InstallationAdmin { settings: InstallationAdminSettings; } -export interface Data { +export interface TelemetryData { installationAdmin: InstallationAdmin totalUsers: number totalWorkspaces: number diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts index 70f881e7591eda..c1dd380d0f3073 100644 --- a/components/server/src/auth/rate-limiter.ts +++ b/components/server/src/auth/rate-limiter.ts @@ -148,6 +148,7 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig { "adminSetLicense": { group: "default", points: 1 }, "adminGetSettings": { group: "default", points: 1 }, "adminUpdateSettings": { group: "default", points: 1 }, + "adminGetTelemetryData": {group: "default", points: 1}, "validateLicense": { group: "default", points: 1 }, "getLicenseInfo": { group: "default", points: 1 }, diff --git a/components/server/src/container-module.ts b/components/server/src/container-module.ts index 6159d31b19884a..171be7a412d218 100644 --- a/components/server/src/container-module.ts +++ b/components/server/src/container-module.ts @@ -81,6 +81,7 @@ import { DebugApp } from './debug-app'; import { LocalMessageBroker, LocalRabbitMQBackedMessageBroker } from './messaging/local-message-broker'; import { contentServiceBinder } from '@gitpod/content-service/lib/sugar'; import { ReferrerPrefixParser } from './workspace/referrer-prefix-context-parser'; +import { InstallationAdminTelemetryDataProvider } from './installation-admin/telemetry-data-provider'; export const productionContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { bind(Config).toConstantValue(ConfigFile.fromFile()); @@ -193,6 +194,8 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo bind(TermsProvider).toSelf().inSingletonScope(); + bind(InstallationAdminTelemetryDataProvider).toSelf().inSingletonScope(); + // binds all content services (contentServiceBinder((ctx) => { const config = ctx.container.get(Config); diff --git a/components/server/src/installation-admin/installation-admin-controller.ts b/components/server/src/installation-admin/installation-admin-controller.ts index 824ab105513738..e2f2c860e22e43 100644 --- a/components/server/src/installation-admin/installation-admin-controller.ts +++ b/components/server/src/installation-admin/installation-admin-controller.ts @@ -6,27 +6,17 @@ import { injectable, inject } from 'inversify'; import * as express from 'express'; -import { InstallationAdminDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; -import { Data } from '@gitpod/gitpod-protocol' +import { InstallationAdminTelemetryDataProvider } from './telemetry-data-provider'; @injectable() export class InstallationAdminController { - @inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB; - @inject(UserDB) protected readonly userDb: UserDB - @inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB + @inject(InstallationAdminTelemetryDataProvider) protected readonly telemetryDataProvider: InstallationAdminTelemetryDataProvider; public create(): express.Application { const app = express(); app.get('/data', async (req: express.Request, res: express.Response) => { - const data: Data = { - installationAdmin: await this.installationAdminDb.getData(), - totalUsers: await this.userDb.getUserCount(true), - totalWorkspaces: await this.workspaceDb.getWorkspaceCount(), - totalInstances: await this.workspaceDb.getInstanceCount(), - } as Data; - - res.status(200).json(data); + res.status(200).json(await this.telemetryDataProvider.getTelemetryData()); }); return app; diff --git a/components/server/src/installation-admin/telemetry-data-provider.ts b/components/server/src/installation-admin/telemetry-data-provider.ts new file mode 100644 index 00000000000000..2d6a6189d76a54 --- /dev/null +++ b/components/server/src/installation-admin/telemetry-data-provider.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2020 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + + import { injectable, inject } from 'inversify'; + + import { TelemetryData } from "@gitpod/gitpod-protocol" + import { InstallationAdminDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; + + @injectable() + export class InstallationAdminTelemetryDataProvider { + @inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB; + @inject(UserDB) protected readonly userDb: UserDB + @inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB + + async getTelemetryData(): Promise { + const data: TelemetryData = { + installationAdmin: await this.installationAdminDb.getData(), + totalUsers: await this.userDb.getUserCount(true), + totalWorkspaces: await this.workspaceDb.getWorkspaceCount(), + totalInstances: await this.workspaceDb.getInstanceCount(), + } as TelemetryData; + + return data; + } + } diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 2d2c4287742a08..1c3ec649a79027 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -57,8 +57,9 @@ import { PartialProject } from '@gitpod/gitpod-protocol/src/teams-projects-proto import { ClientMetadata, traceClientMetadata } from '../websocket/websocket-connection-manager'; import { ConfigurationService } from '../config/configuration-service'; import { ProjectEnvVar } from '@gitpod/gitpod-protocol/src/protocol'; -import { InstallationAdminSettings } from '@gitpod/gitpod-protocol'; +import { InstallationAdminSettings, TelemetryData } from '@gitpod/gitpod-protocol'; import { Deferred } from '@gitpod/gitpod-protocol/lib/util/deferred'; +import { InstallationAdminTelemetryDataProvider } from '../installation-admin/telemetry-data-provider'; // shortcut export const traceWI = (ctx: TraceContext, wi: Omit) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager @@ -80,6 +81,8 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { @inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider; @inject(GitpodFileParser) protected readonly gitpodParser: GitpodFileParser; @inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB; + @inject(InstallationAdminTelemetryDataProvider) protected readonly telemetryDataProvider: InstallationAdminTelemetryDataProvider; + @inject(WorkspaceStarter) protected readonly workspaceStarter: WorkspaceStarter; @inject(WorkspaceManagerClientProvider) protected readonly workspaceManagerClientProvider: WorkspaceManagerClientProvider; @@ -2203,6 +2206,16 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { await this.installationAdminDb.setSettings(newSettings); } + async adminGetTelemetryData(ctx: TraceContext): Promise { + traceAPIParams(ctx, {}); + + await this.guardAdminAccess("adminGetTelemetryData", {}, Permission.ADMIN_API); + + return await this.telemetryDataProvider.getTelemetryData(); + } + + + async getLicenseInfo(): Promise { throw new ResponseError(ErrorCodes.EE_FEATURE, `Licensing is implemented in Gitpod's Enterprise Edition`); }