Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dashboard] Allow restoring a soft-deleted workspace from the admin dashboard #3955

Merged
merged 1 commit into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion components/dashboard/src/admin/WorkspaceDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ export default function WorkspaceDetail(props: { workspace: WorkspaceAndInstance
<div className="flex w-full mt-6">
<Property name="User"><Link className="text-blue-400 hover:text-blue-600" to={"/admin/users/" + props.workspace.ownerId}>{user?.name || props.workspace.ownerId}</Link></Property>
<Property name="Sharing">{workspace.shareable ? 'Enabled' : 'Disabled'}</Property>
<Property name=""><div></div></Property>
<Property name="Soft Deleted" actions={(!!workspace.softDeleted && !workspace.contentDeletedTime) && [{
label: 'Restore & Pin',
onClick: () => {
getGitpodService().server.adminRestoreSoftDeletedWorkspace(workspace.workspaceId);
}
}] || undefined}>{workspace.softDeleted ? `'${workspace.softDeleted}' ${moment(workspace.softDeletedTime).fromNow()}` : 'No'}</Property>
</div>
<div className="flex w-full mt-12">
<Property name="Latest Instance ID">
Expand Down
1 change: 1 addition & 0 deletions components/gitpod-protocol/src/admin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface AdminServer {
adminGetWorkspaces(req: AdminGetWorkspacesRequest): Promise<AdminGetListResult<WorkspaceAndInstance>>;
adminGetWorkspace(id: string): Promise<WorkspaceAndInstance>;
adminForceStopWorkspace(id: string): Promise<void>;
adminRestoreSoftDeletedWorkspace(id: string): Promise<void>;

adminSetLicense(key: string): Promise<void>;

Expand Down
22 changes: 22 additions & 0 deletions components/server/ee/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,28 @@ export class GitpodServerEEImpl<C extends GitpodClient, S extends GitpodServer>
await this.internalStopWorkspace({ span }, id, undefined, StopWorkspacePolicy.IMMEDIATELY);
}

async adminRestoreSoftDeletedWorkspace(id: string): Promise<void> {
this.requireEELicense(Feature.FeatureAdminDashboard);

await this.guardAdminAccess("adminRestoreSoftDeletedWorkspace", {id}, Permission.ADMIN_WORKSPACES);

const span = opentracing.globalTracer().startSpan("adminRestoreSoftDeletedWorkspace");
await this.workspaceDb.trace({ span }).transaction(async db => {
const ws = await this.internalGetWorkspace(id, db);
if (!ws.softDeleted) {
return;
}
if (!!ws.contentDeletedTime) {
throw new ResponseError(ErrorCodes.NOT_FOUND, "The workspace content was already garbage-collected.");
}
// @ts-ignore
ws.softDeleted = null;
ws.softDeletedTime = '';
ws.pinned = true;
await db.store(ws);
});
}

protected async guardAdminAccess(method: string, params: any, requiredPermission: PermissionName) {
const user = this.checkAndBlockUser(method);
if (!this.authorizationService.hasPermission(user, requiredPermission)) {
Expand Down
1 change: 1 addition & 0 deletions components/server/src/auth/rate-limiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ function readConfig(): RateLimiterConfig {
"adminGetWorkspaces": { group: "default", points: 1 },
"adminGetWorkspace": { group: "default", points: 1 },
"adminForceStopWorkspace": { group: "default", points: 1 },
"adminRestoreSoftDeletedWorkspace": { group: "default", points: 1 },
"adminSetLicense": { group: "default", points: 1 },

"validateLicense": { group: "default", points: 1 },
Expand Down
4 changes: 4 additions & 0 deletions components/server/src/workspace/gitpod-server-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,10 @@ export class GitpodServerImpl<Client extends GitpodClient, Server extends Gitpod
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}

async adminRestoreSoftDeletedWorkspace(id: string): Promise<void> {
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}

async adminSetLicense(key: string): Promise<void> {
throw new ResponseError(ErrorCodes.EE_FEATURE, `Admin support is implemented in Gitpod's Enterprise Edition`);
}
Expand Down