Skip to content

Commit ba026d4

Browse files
author
Simon Emms
committed
[dashboard]: create a settings tab inside admin
1 parent 4f2e71e commit ba026d4

File tree

12 files changed

+131
-14
lines changed

12 files changed

+131
-14
lines changed

components/dashboard/src/App.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Login } from './Login';
1212
import { UserContext } from './user-context';
1313
import { TeamsContext } from './teams/teams-context';
1414
import { ThemeContext } from './theme-context';
15+
import { AdminContext } from './admin-context';
1516
import { getGitpodService } from './service/service';
1617
import { shouldSeeWhatsNew, WhatsNew } from './whatsnew/WhatsNew';
1718
import gitpodIcon from './icons/gitpod.svg';
@@ -52,6 +53,7 @@ const InstallGitHubApp = React.lazy(() => import(/* webpackPrefetch: true */ './
5253
const FromReferrer = React.lazy(() => import(/* webpackPrefetch: true */ './FromReferrer'));
5354
const UserSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/UserSearch'));
5455
const WorkspacesSearch = React.lazy(() => import(/* webpackPrefetch: true */ './admin/WorkspacesSearch'));
56+
const AdminSettings = React.lazy(() => import(/* webpackPrefetch: true */ './admin/Settings'));
5557
const OAuthClientApproval = React.lazy(() => import(/* webpackPrefetch: true */ './OauthClientApproval'));
5658

5759
function Loading() {
@@ -98,6 +100,7 @@ export function getURLHash() {
98100
function App() {
99101
const { user, setUser } = useContext(UserContext);
100102
const { teams, setTeams } = useContext(TeamsContext);
103+
const { setSendTelemetry } = useContext(AdminContext);
101104
const { setIsDark } = useContext(ThemeContext);
102105

103106
const [ loading, setLoading ] = useState<boolean>(true);
@@ -132,6 +135,11 @@ function App() {
132135
}
133136
}
134137
setTeams(teams);
138+
139+
if (user?.rolesOrPermissions?.includes('admin')) {
140+
const sendTelemetry = await getGitpodService().server.adminSettingsGetTelemetry();
141+
setSendTelemetry(sendTelemetry);
142+
}
135143
} catch (error) {
136144
console.error(error);
137145
if (error && "code" in error) {
@@ -279,6 +287,7 @@ function App() {
279287

280288
<Route path="/admin/users" component={UserSearch} />
281289
<Route path="/admin/workspaces" component={WorkspacesSearch} />
290+
<Route path="/admin/settings" component={AdminSettings} />
282291

283292
<Route path={["/", "/login"]} exact>
284293
<Redirect to={workspacesPathMain} />
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 React, { createContext, useState } from 'react';
8+
9+
const AdminContext = createContext<{
10+
sendTelemetry?: boolean,
11+
setSendTelemetry: React.Dispatch<boolean>,
12+
}>({
13+
setSendTelemetry: () => null,
14+
});
15+
16+
const AdminContextProvider: React.FC = ({ children }) => {
17+
const [sendTelemetry, setSendTelemetry] = useState<boolean>();
18+
return (
19+
<AdminContext.Provider value={{ sendTelemetry, setSendTelemetry }}>
20+
{children}
21+
</AdminContext.Provider>
22+
);
23+
};
24+
25+
export { AdminContext, AdminContextProvider };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 { useContext } from "react";
8+
import { AdminContext } from "../admin-context";
9+
import CheckBox from "../components/CheckBox";
10+
import { PageWithSubMenu } from "../components/PageWithSubMenu";
11+
import { getGitpodService } from "../service/service";
12+
import { adminMenu } from "./admin-menu";
13+
14+
export default function Settings() {
15+
const { sendTelemetry, setSendTelemetry } = useContext(AdminContext);
16+
17+
const actuallySetTelemetryPrefs = async (value: boolean) => {
18+
await getGitpodService().server.adminSettingsSetTelemetry(value);
19+
setSendTelemetry(value);
20+
}
21+
22+
return (
23+
<div>
24+
<PageWithSubMenu subMenu={adminMenu} title="Settings" subtitle="Configure settings for your Gitpod cluster.">
25+
<CheckBox
26+
title="Send anonymous service ping to Gitpod"
27+
desc="This is used to provide insights on how you use your cluster so we can provide a better overall experience."
28+
checked={sendTelemetry ?? false}
29+
onChange={(evt) => actuallySetTelemetryPrefs(evt.target.checked)} />
30+
</PageWithSubMenu>
31+
</div >
32+
)
33+
}

components/dashboard/src/admin/admin-menu.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ export const adminMenu = [{
1010
}, {
1111
title: 'Workspaces',
1212
link: ['/admin/workspaces']
13+
}, {
14+
title: 'Settings',
15+
link: ['/admin/settings']
1316
},];

components/dashboard/src/index.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import React from 'react';
88
import ReactDOM from 'react-dom';
99
import App from './App';
1010
import { UserContextProvider } from './user-context';
11+
import { AdminContextProvider } from './admin-context';
1112
import { TeamsContextProvider } from './teams/teams-context';
1213
import { ProjectContextProvider } from './projects/project-context';
1314
import { ThemeContextProvider } from './theme-context';
@@ -18,15 +19,17 @@ import "./index.css"
1819
ReactDOM.render(
1920
<React.StrictMode>
2021
<UserContextProvider>
21-
<TeamsContextProvider>
22-
<ProjectContextProvider>
23-
<ThemeContextProvider>
24-
<BrowserRouter>
25-
<App />
26-
</BrowserRouter>
27-
</ThemeContextProvider>
28-
</ProjectContextProvider>
29-
</TeamsContextProvider>
22+
<AdminContextProvider>
23+
<TeamsContextProvider>
24+
<ProjectContextProvider>
25+
<ThemeContextProvider>
26+
<BrowserRouter>
27+
<App />
28+
</BrowserRouter>
29+
</ThemeContextProvider>
30+
</ProjectContextProvider>
31+
</TeamsContextProvider>
32+
</AdminContextProvider>
3033
</UserContextProvider>
3134
</React.StrictMode>,
3235
document.getElementById('root')

components/gitpod-db/src/installation-admin-db.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ import { InstallationAdmin } from "@gitpod/gitpod-protocol";
99
export const InstallationAdminDB = Symbol('InstallationAdminDB');
1010
export interface InstallationAdminDB {
1111
getTelemetryData(): Promise<InstallationAdmin>;
12+
setSendTelemetry(value: boolean): Promise<void>;
1213
}

components/gitpod-db/src/typeorm/installation-admin-db-impl.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class TypeORMInstallationAdminImpl implements InstallationAdminDB {
2424
const record: InstallationAdmin = {
2525
id: uuidv4(),
2626
settings: {
27-
sendTelemetry: false,
27+
sendTelemetry: true,
2828
},
2929
};
3030

@@ -55,4 +55,12 @@ export class TypeORMInstallationAdminImpl implements InstallationAdminDB {
5555
/* Record not found - create one */
5656
return this.createDefaultRecord();
5757
}
58+
59+
async setSendTelemetry(value: boolean): Promise<void> {
60+
const record = await this.getTelemetryData();
61+
record.settings.sendTelemetry = value;
62+
63+
const repo = await this.getInstallationAdminRepo();
64+
repo.save(record);
65+
}
5866
}

components/gitpod-protocol/src/admin-protocol.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ export interface AdminServer {
2929
adminIsStudent(userId: string): Promise<boolean>;
3030
adminAddStudentEmailDomain(userId: string, domain: string): Promise<void>;
3131
adminGrantExtraHours(userId: string, extraHours: number): Promise<void>;
32+
33+
adminSettingsGetTelemetry(): Promise<boolean>
34+
adminSettingsSetTelemetry(send: boolean): Promise<void>
3235
}
3336

3437
export interface AdminGetListRequest<T> {
@@ -65,7 +68,7 @@ export interface AdminModifyPermanentWorkspaceFeatureFlagRequest {
6568
}[]
6669
}
6770

68-
export interface WorkspaceAndInstance extends Omit<Workspace, "id"|"creationTime">, Omit<WorkspaceInstance, "id"|"creationTime"> {
71+
export interface WorkspaceAndInstance extends Omit<Workspace, "id" | "creationTime">, Omit<WorkspaceInstance, "id" | "creationTime"> {
6972
workspaceId: string;
7073
workspaceCreationTime: string;
7174
instanceId: string;
@@ -78,7 +81,7 @@ export namespace WorkspaceAndInstance {
7881
return {
7982
id: wai.workspaceId,
8083
creationTime: wai.workspaceCreationTime,
81-
... wai
84+
...wai
8285
};
8386
}
8487

@@ -89,7 +92,7 @@ export namespace WorkspaceAndInstance {
8992
return {
9093
id: wai.instanceId,
9194
creationTime: wai.instanceCreationTime,
92-
... wai
95+
...wai
9396
};
9497
}
9598
}

components/gitpod-protocol/src/gitpod-service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
131131
resetGenericInvite(inviteId: string): Promise<TeamMembershipInvite>;
132132
deleteTeam(teamId: string, userId: string): Promise<void>;
133133

134+
// Admin Settings
135+
adminSettingsGetTelemetry(): Promise<boolean>;
136+
adminSettingsSetTelemetry(send: boolean): Promise<void>;
137+
134138
// Projects
135139
getProviderRepositoriesForUser(params: GetProviderRepositoriesParams): Promise<ProviderRepository[]>;
136140
createProject(params: CreateProjectParams): Promise<Project>;

components/server/ee/src/workspace/gitpod-server-impl.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,24 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
563563
return result;
564564
}
565565

566+
async adminSettingsGetTelemetry(ctx: TraceContext): Promise<boolean> {
567+
traceAPIParams(ctx, {});
568+
569+
await this.guardAdminAccess("adminSettingsGetTelemetry", {}, Permission.ADMIN_API);
570+
571+
const settings = await this.installationAdminDb.getTelemetryData();
572+
573+
return settings.settings.sendTelemetry;
574+
}
575+
576+
async adminSettingsSetTelemetry(ctx: TraceContext, send: boolean): Promise<void> {
577+
traceAPIParams(ctx, {});
578+
579+
await this.guardAdminAccess("adminSettingsGetTelemetry", {}, Permission.ADMIN_API);
580+
581+
await this.installationAdminDb.setSendTelemetry(send);
582+
}
583+
566584
async adminForceStopWorkspace(ctx: TraceContext, workspaceId: string): Promise<void> {
567585
traceAPIParams(ctx, { workspaceId });
568586

components/server/src/auth/rate-limiter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig {
136136
"adminForceStopWorkspace": { group: "default", points: 1 },
137137
"adminRestoreSoftDeletedWorkspace": { group: "default", points: 1 },
138138
"adminSetLicense": { group: "default", points: 1 },
139+
"adminSettingsGetTelemetry": { group: "default", points: 1 },
140+
"adminSettingsSetTelemetry": { group: "default", points: 1 },
139141

140142
"validateLicense": { group: "default", points: 1 },
141143
"getLicenseInfo": { group: "default", points: 1 },

components/server/src/workspace/gitpod-server-impl.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { DownloadUrlRequest, DownloadUrlResponse, UploadUrlRequest, UploadUrlResponse } from '@gitpod/content-service/lib/blobs_pb';
8-
import { AppInstallationDB, UserDB, UserMessageViewsDB, WorkspaceDB, DBWithTracing, TracedWorkspaceDB, DBGitpodToken, DBUser, UserStorageResourcesDB, TeamDB } from '@gitpod/gitpod-db/lib';
8+
import { AppInstallationDB, UserDB, UserMessageViewsDB, WorkspaceDB, DBWithTracing, TracedWorkspaceDB, DBGitpodToken, DBUser, UserStorageResourcesDB, TeamDB, InstallationAdminDB } from '@gitpod/gitpod-db/lib';
99
import { AuthProviderEntry, AuthProviderInfo, CommitContext, Configuration, CreateWorkspaceMode, DisposableCollection, GetWorkspaceTimeoutResult, GitpodClient as GitpodApiClient, GitpodServer, GitpodToken, GitpodTokenType, InstallPluginsParams, PermissionName, PortVisibility, PrebuiltWorkspace, PrebuiltWorkspaceContext, PreparePluginUploadParams, ResolvedPlugins, ResolvePluginsParams, SetWorkspaceTimeoutResult, StartPrebuildContext, StartWorkspaceResult, Terms, Token, UninstallPluginParams, User, UserEnvVar, UserEnvVarValue, UserInfo, WhitelistedRepository, Workspace, WorkspaceContext, WorkspaceCreationResult, WorkspaceImageBuild, WorkspaceInfo, WorkspaceInstance, WorkspaceInstancePort, WorkspaceInstanceUser, WorkspaceTimeoutDuration, GuessGitTokenScopesParams, GuessedGitTokenScopes, Team, TeamMemberInfo, TeamMembershipInvite, CreateProjectParams, Project, ProviderRepository, TeamMemberRole, WithDefaultConfig, FindPrebuildsParams, PrebuildWithStatus, StartPrebuildResult, ClientHeaderFields } from '@gitpod/gitpod-protocol';
1010
import { AccountStatement } from "@gitpod/gitpod-protocol/lib/accounting-protocol";
1111
import { AdminBlockUserRequest, AdminGetListRequest, AdminGetListResult, AdminGetWorkspacesRequest, AdminModifyPermanentWorkspaceFeatureFlagRequest, AdminModifyRoleOrPermissionRequest, WorkspaceAndInstance } from '@gitpod/gitpod-protocol/lib/admin-protocol';
@@ -77,6 +77,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
7777
@inject(ContextParser) protected contextParser: ContextParser;
7878
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
7979
@inject(GitpodFileParser) protected readonly gitpodParser: GitpodFileParser;
80+
@inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB;
8081

8182
@inject(WorkspaceStarter) protected readonly workspaceStarter: WorkspaceStarter;
8283
@inject(WorkspaceManagerClientProvider) protected readonly workspaceManagerClientProvider: WorkspaceManagerClientProvider;
@@ -2006,6 +2007,13 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
20062007
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
20072008
}
20082009

2010+
async adminSettingsGetTelemetry(ctx: TraceContext): Promise<boolean> {
2011+
return false;
2012+
}
2013+
2014+
async adminSettingsSetTelemetry(ctx: TraceContext, send: boolean): Promise<void> {
2015+
}
2016+
20092017
async getLicenseInfo(): Promise<GetLicenseInfoResult> {
20102018
throw new ResponseError(ErrorCodes.EE_FEATURE, `Licensing is implemented in Gitpod's Enterprise Edition`);
20112019
}

0 commit comments

Comments
 (0)