feat: implement ssh forwarding

This commit is contained in:
2024-11-17 18:10:35 +00:00
parent a7933f8b06
commit 45bfbe093a
21 changed files with 1175 additions and 296 deletions

View File

@@ -1,8 +1,8 @@
import { fetchApi } from "@/api";
import useSWR, { useSWRConfig } from "swr";
import type { Workspace } from "./types";
import { WorkspaceStatus, type Workspace } from "./types";
import { useCallback, useState } from "react";
import { QueryStatus } from "@/lib/query";
import type { QueryStatus } from "@/lib/query";
function useWorkspaces() {
return useSWR(
@@ -20,29 +20,143 @@ function useCreateWorkspace() {
async ({
workspaceName,
imageId,
}: { workspaceName: string; imageId: string }): Promise<Workspace> => {
}: {
workspaceName: string;
imageId: string;
}): Promise<Workspace | null> => {
setStatus({ type: "loading" });
try {
const res = await fetchApi(`/workspaces/${workspaceName}`, {
method: "POST",
body: JSON.stringify({ imageId }),
headers: {
"Content-Type": "application/json",
const workspace = await mutate(
"/workspaces",
fetchApi(`/workspaces/${workspaceName}`, {
method: "POST",
body: JSON.stringify({ imageId }),
headers: {
"Content-Type": "application/json",
},
}).then((res): Promise<Workspace> => res.json()),
{
populateCache: (createdWorkspace, workspaces) => [
...workspaces,
createdWorkspace,
],
throwOnError: true,
},
});
const workspace = await res.json();
setStatus({ type: "ok" });
return workspace;
);
return workspace ?? null;
} catch (error: unknown) {
setStatus({ type: "error", error });
return null;
}
},
[],
[mutate],
);
return { createWorkspace, status };
}
export { useWorkspaces, useCreateWorkspace };
function useChangeWorkspaceStatus() {
const [status, setStatus] = useState<QueryStatus>({ type: "idle" });
const { mutate } = useSWRConfig();
const startWorkspace = useCallback(
async (workspaceName: string) => {
setStatus({ type: "loading" });
try {
await mutate(
"/workspaces",
fetchApi(`/workspaces/${workspaceName}`, {
method: "POST",
body: JSON.stringify({ status: WorkspaceStatus.Running }),
headers: {
"Content-Type": "application/json",
},
}).then((res): Promise<Workspace> => res.json()),
{
populateCache: (updatedWorkspace, workspaces) =>
workspaces.map((workspace: Workspace) =>
workspace.containerId === updatedWorkspace.containerId
? updatedWorkspace
: workspace,
),
throwOnError: true,
},
);
setStatus({ type: "ok" });
} catch (error: unknown) {
setStatus({ type: "error", error });
}
},
[mutate],
);
const stopWorkspace = useCallback(
async (workspaceName: string) => {
setStatus({ type: "loading" });
try {
await mutate(
"/workspaces",
fetchApi(`/workspaces/${workspaceName}`, {
method: "POST",
body: JSON.stringify({ status: WorkspaceStatus.Stopped }),
headers: {
"Content-Type": "application/json",
},
}).then((res): Promise<Workspace> => res.json()),
{
populateCache: (updatedWorkspace, workspaces) =>
workspaces.map((workspace: Workspace) =>
workspace.containerId === updatedWorkspace.containerId
? updatedWorkspace
: workspace,
),
throwOnError: true,
},
);
setStatus({ type: "ok" });
} catch (error: unknown) {
setStatus({ type: "error", error });
}
},
[mutate],
);
return { startWorkspace, stopWorkspace, status };
}
function useDeleteWorkspace() {
const [status, setStatus] = useState<QueryStatus>({ type: "idle" });
const { mutate } = useSWRConfig();
const deleteWorkspace = useCallback(
async (workspaceName: string) => {
setStatus({ type: "loading" });
try {
await mutate(
"/workspaces",
fetchApi(`/workspaces/${workspaceName}`, { method: "DELETE" }),
{
populateCache: (_, workspaces) =>
workspaces.filter(
(workspace: Workspace) => workspace.name === workspaceName,
),
throwOnError: true,
},
);
setStatus({ type: "ok" });
} catch (error: unknown) {
setStatus({ type: "error", error });
}
},
[mutate],
);
return { deleteWorkspace, status };
}
export {
useWorkspaces,
useCreateWorkspace,
useChangeWorkspaceStatus,
useDeleteWorkspace,
};