Skip to content

Commit d205d19

Browse files
committed
support delete cloud sync workspace locally or remotely
1 parent eff9d9e commit d205d19

3 files changed

Lines changed: 54 additions & 9 deletions

File tree

packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.mcp.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { redirect } from 'react-router';
22

3+
import { getAppVersion } from '~/common/constants';
34
import * as models from '~/models';
45
import { invariant } from '~/utils/invariant';
56

@@ -12,8 +13,18 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
1213
const activeWorkspace = await models.workspace.getById(workspaceId);
1314
invariant(activeWorkspace, 'Workspace not found');
1415
// Mcp collection only have one request
15-
const activeRequest = await models.mcpRequest.getByParentId(workspaceId);
16-
invariant(activeRequest, 'MCP Request not found');
16+
let activeRequest = await models.mcpRequest.getByParentId(workspaceId);
17+
if (!activeRequest) {
18+
activeRequest = await models.mcpRequest.create({
19+
parentId: workspaceId,
20+
transportType: 'streamable-http',
21+
url: '',
22+
name: 'MCP Client',
23+
headers: [{ name: 'User-Agent', value: `insomnia/${getAppVersion()}` }],
24+
description: '',
25+
});
26+
}
27+
// invariant(activeRequest, 'MCP Request not found');
1728
// Redirect to the debug page of the only request in the MCP workspace
1829
if (activeRequest) {
1930
return redirect(

packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.delete.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import { createFetcherSubmitHook } from '~/utils/router';
1010

1111
import type { Route } from './+types/organization.$organizationId.project.$projectId.workspace.delete';
1212

13-
async function deleteWorkspaceFromCloud(workspace: Workspace, project: Project) {
13+
async function deleteCloudSyncWorkspaceCloud(workspace: Workspace, project: Project, localOnly: boolean) {
1414
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(workspace._id);
1515
const isGitSync = !!workspaceMeta.gitRepositoryId;
1616

1717
if (isRemoteProject(project) && !isGitSync) {
1818
try {
1919
const vcs = VCSInstance();
2020
await vcs.switchAndCreateBackendProjectIfNotExist(workspace._id, workspace.name);
21-
await vcs.archiveProject();
21+
// For cloud sync workspaces, delete only local file or also delete remote copy
22+
await (localOnly ? vcs.removeBackendProjectsForRoot(workspace._id) : vcs.archiveProject());
2223
} catch (err) {
2324
return {
2425
error:
@@ -37,11 +38,11 @@ async function deleteWorkspaceFromLocal(workspace: Workspace) {
3738
await models.workspace.remove(workspace);
3839
}
3940

40-
async function deleteWorkspace(workspace: Workspace | null, project: Project | null) {
41+
async function deleteWorkspace(workspace: Workspace | null, project: Project | null, localOnly: boolean) {
4142
invariant(workspace, 'Workspace not found');
4243
invariant(project, 'Project not found');
4344

44-
const ret = await deleteWorkspaceFromCloud(workspace, project);
45+
const ret = await deleteCloudSyncWorkspaceCloud(workspace, project, localOnly);
4546
if (ret?.error) {
4647
return ret;
4748
}
@@ -65,12 +66,14 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
6566
const formData = await request.formData();
6667

6768
const workspaceId = formData.get('workspaceId');
69+
const localOnly = formData.get('localOnly') === 'true';
70+
console.log(`localOnly: ${localOnly}`);
6871
invariant(typeof workspaceId === 'string', 'Workspace ID is required');
6972

7073
const workspace = await models.workspace.getById(workspaceId);
7174
invariant(workspace, 'Workspace not found');
7275

73-
const msgObj = await deleteWorkspace(workspace, project);
76+
const msgObj = await deleteWorkspace(workspace, project, localOnly);
7477

7578
if (msgObj?.error) {
7679
return msgObj;
@@ -90,10 +93,13 @@ export const useWorkspaceDeleteActionFetcher = createFetcherSubmitHook(
9093
organizationId,
9194
projectId,
9295
workspaceId,
96+
// for cloud sync workspaces, delete only local file or also delete remote copy
97+
localOnly = 'true',
9398
}: {
9499
organizationId: string;
95100
projectId: string;
96101
workspaceId: string;
102+
localOnly?: 'true' | 'false';
97103
}) => {
98104
const url = href('/organization/:organizationId/project/:projectId/workspace/delete', {
99105
organizationId,
@@ -102,6 +108,7 @@ export const useWorkspaceDeleteActionFetcher = createFetcherSubmitHook(
102108

103109
const formData = new FormData();
104110
formData.append('workspaceId', workspaceId);
111+
formData.append('localOnly', localOnly);
105112

106113
return submit(formData, {
107114
action: url,

packages/insomnia/src/ui/components/dropdowns/workspace-card-dropdown.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
exportMockServerToFile,
55
} from 'insomnia/src/ui/components/settings/import-export';
66
import React, { type FC, Fragment, useCallback, useState } from 'react';
7-
import { Button, Dialog, Heading, Modal, ModalOverlay } from 'react-aria-components';
7+
import { Button, Dialog, Heading, Label, Modal, ModalOverlay, Radio, RadioGroup } from 'react-aria-components';
88
import { href, useParams } from 'react-router';
99

1010
import { useWorkspaceDeleteActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.delete';
@@ -282,7 +282,34 @@ export const WorkspaceCardDropdown: FC<Props> = props => {
282282
<p>
283283
This will permanently delete the{' '}
284284
{<strong style={{ whiteSpace: 'pre-wrap' }}>{workspace?.name}</strong>}{' '}
285-
{getWorkspaceLabel(workspace).singular} {isRemoteProject(project) ? 'remotely' : ''}.
285+
{getWorkspaceLabel(workspace).singular}
286+
{isRemoteProject(project) && (
287+
<RadioGroup name="localOnly" defaultValue="false" className="mb-2 flex flex-col gap-2">
288+
<Label className="text-sm text-(--hl)">How do you want to delete it?</Label>
289+
<div className="flex gap-2">
290+
<Radio
291+
value="true"
292+
className="flex-1 rounded-sm border border-solid border-(--hl-md) p-4 transition-colors hover:bg-(--hl-xs) focus:bg-(--hl-sm) focus:outline-hidden data-disabled:opacity-25 data-selected:border-(--color-surprise) data-selected:ring-2 data-selected:ring-(--color-surprise)"
293+
>
294+
<div className="flex items-center gap-2">
295+
<Heading className="text-lg font-bold">Delete Local File</Heading>
296+
</div>
297+
<p className="pt-2">Delete only the local file.</p>
298+
</Radio>
299+
<Radio
300+
value="false"
301+
className="flex-1 rounded-sm border border-solid border-(--hl-md) p-4 transition-colors hover:bg-(--hl-xs) focus:bg-(--hl-sm) focus:outline-hidden data-disabled:opacity-25 data-selected:border-(--color-surprise) data-selected:ring-2 data-selected:ring-(--color-surprise)"
302+
>
303+
<div className="flex items-center gap-2">
304+
<Heading className="text-lg font-bold">
305+
<span>Delete Local & Remote File</span>
306+
</Heading>
307+
</div>
308+
<p className="pt-2">Permanently removes from everywhere - local file and the cloud.</p>
309+
</Radio>
310+
</div>
311+
</RadioGroup>
312+
)}
286313
</p>
287314
{deleteWorkspaceFetcher.data && deleteWorkspaceFetcher.data.error && (
288315
<p className="notice error margin-bottom-sm no-margin-top">{deleteWorkspaceFetcher.data.error}</p>

0 commit comments

Comments
 (0)