Skip to content

Commit

Permalink
feat: editable ports
Browse files Browse the repository at this point in the history
Signed-off-by: Quentin Guidée <[email protected]>
  • Loading branch information
quentinguidee committed Mar 5, 2024
1 parent a90b4cf commit d78df26
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 31 deletions.
6 changes: 6 additions & 0 deletions client/src/apps/Containers/backend/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ContainerFilters,
Containers,
EnvVariables,
Port,
Tags,
} from "./models";
import { DockerContainerInfo } from "../../../models/docker";
Expand Down Expand Up @@ -82,6 +83,10 @@ const getContainerPorts = async (id: string) => {
return data;
};

const saveContainerPorts = (id: string, ports: Port[]) => {
return server.patch(`/containers/${id}/ports`, { ports });
};

const getDocker = async (id: string) => {
const { data } = await server.get<DockerContainerInfo>(
`/containers/${id}/docker`
Expand Down Expand Up @@ -117,6 +122,7 @@ export const API = {
getContainerEnvironment,
saveEnv,
getContainerPorts,
saveContainerPorts,
getDockerInfo: getDocker,
recreateDocker,
updateService,
Expand Down
7 changes: 7 additions & 0 deletions client/src/apps/Containers/backend/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export type EnvVariable = {
secret: boolean;
};

export type Port = {
id: string;
container_id: string;
in: string;
out: string;
};

export type Containers = Container[];
export type Container = {
id: string;
Expand Down
26 changes: 25 additions & 1 deletion client/src/apps/Containers/hooks/useContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { useQuery } from "@tanstack/react-query";
import {
useMutation,
UseMutationOptions,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { API } from "../backend/api";
import { Port } from "../backend/models";

export default function useContainer(id?: string) {
const queryContainer = useQuery({
Expand Down Expand Up @@ -48,3 +54,21 @@ export function useContainerPorts(id?: string) {
errorPorts: queryPorts.error,
};
}

export function useSaveContainerPorts(
id?: string,
options?: UseMutationOptions<unknown, unknown, Port[]>
) {
const queryClient = useQueryClient();
const { mutate: savePorts, ...rest } = useMutation({
...options,
mutationFn: (ports: Port[]) => API.saveContainerPorts(id, ports),
onSettled: async (...args) => {
await queryClient.invalidateQueries({
queryKey: ["container_ports", id],
});
options?.onSettled?.(...args);
},
});
return { savePorts, ...rest };
}
136 changes: 110 additions & 26 deletions client/src/apps/Containers/pages/ContainerPorts/ContainerPorts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,130 @@ import {
Vertical,
} from "@vertex-center/components";
import { useParams } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { useContainerPorts } from "../../hooks/useContainer";
import { ProgressOverlay } from "../../../../components/Progress/Progress";
import { APIError } from "../../../../components/Error/APIError";
import { Horizontal } from "../../../../components/Layouts/Layouts";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { ArrowUUpLeft, FloppyDiskBack } from "@phosphor-icons/react";
import { API } from "../../backend/api";
import { useSaveContainerPorts } from "../../hooks/useContainer";

export default function ContainerPorts() {
const { uuid } = useParams();
// const queryClient = useQueryClient();
const { control, handleSubmit, reset } = useForm({
defaultValues: async () => {
const ports = await API.getContainerPorts(uuid);
return { ports };
},
});

const { ports, isLoadingPorts, errorPorts } = useContainerPorts(uuid);
const { fields } = useFieldArray({
control,
name: "ports",
});

const error = errorPorts;
const isLoading = isLoadingPorts;
const { savePorts, isPending, error } = useSaveContainerPorts(uuid, {
onSuccess: () => {
reset({}, { keepValues: true });
},
});

const onSubmit = handleSubmit((data) => {
savePorts(data.ports);
});

const isLoading = isPending;

return (
<Vertical gap={24}>
<Title variant="h2">Ports</Title>
<APIError error={error} />
<ProgressOverlay show={isLoading} />
<Table>
<TableHead>
<TableRow>
<TableHeadCell>Port inside container</TableHeadCell>
<TableHeadCell>Port outside container</TableHeadCell>
</TableRow>
</TableHead>
<TableBody>
{ports?.map((port, i) => (
<TableRow>
<TableCell>
<Input value={port?.in} disabled />
</TableCell>
<TableCell>
<Input value={port?.out} disabled />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<form onSubmit={onSubmit}>
<Vertical gap={24}>
<Table>
<TableHead>
<TableRow>
<TableHeadCell>
Port inside container
</TableHeadCell>
<TableHeadCell>
Port outside container
</TableHeadCell>
</TableRow>
</TableHead>
<TableBody>
{fields?.map((port, i) => (
<TableRow key={port.id}>
<TableCell>
<Controller
control={control}
name={`ports.${i}.in`}
render={({
field,
formState: { dirtyFields },
}) => (
<Input
{...field}
style={{
color:
dirtyFields
?.ports?.[
`${i}`
]?.in &&
"var(--blue)",
}}
/>
)}
/>
</TableCell>
<TableCell>
<Controller
control={control}
name={`ports.${i}.out`}
render={({
field,
formState: { dirtyFields },
}) => (
<Input
{...field}
style={{
color:
dirtyFields
?.ports?.[
`${i}`
]?.out &&
"var(--blue)",
}}
/>
)}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Horizontal justifyContent="flex-end" gap={10}>
<Button
type="reset"
variant="outlined"
onClick={() => reset()}
rightIcon={<ArrowUUpLeft />}
disabled={isLoading}
>
Cancel
</Button>
<Button
type="submit"
variant="colored"
rightIcon={<FloppyDiskBack />}
disabled={isLoading}
>
Save
</Button>
</Horizontal>
</Vertical>
</form>
</Vertical>
);
}
9 changes: 9 additions & 0 deletions server/apps/containers/adapter/port_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,12 @@ func (a *portDBAdapter) DeleteContainerPorts(ctx context.Context, id uuid.UUID)
`, id)
return err
}

func (a *portDBAdapter) UpdateContainerPortByID(ctx context.Context, port types.Port) error {
_, err := a.db.NamedExec(`
UPDATE ports
SET internal_port = :internal_port, external_port = :external_port
WHERE id = :id
`, port)
return err
}
7 changes: 7 additions & 0 deletions server/apps/containers/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ func (a *App) InitializeRouter(r *fizz.RouterGroup) error {
fizz.Response("404", "Container not found", nil, nil, map[string]interface{}{"error": "container not found"}),
}, containersHandler.GetContainerPorts())

containers.PATCH("/:container_id/ports", []fizz.OperationOption{
fizz.ID("patchContainerPorts"),
fizz.Summary("Patch container ports"),
fizz.Response("404", "Container not found", nil, nil, map[string]interface{}{"error": "container not found"}),
fizz.Response("500", "", nil, nil, map[string]interface{}{"error": "failed to patch container ports"}),
}, containersHandler.PatchContainerPorts())

containers.GET("/:container_id/events", []fizz.OperationOption{
fizz.ID("eventsContainer"),
fizz.Summary("Get container events"),
Expand Down
1 change: 1 addition & 0 deletions server/apps/containers/core/port/adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type (
GetContainerPorts(ctx context.Context, id uuid.UUID) (types.Ports, error)
CreatePort(ctx context.Context, port types.Port) error
DeleteContainerPorts(ctx context.Context, id uuid.UUID) error
UpdateContainerPortByID(ctx context.Context, port types.Port) error
}

VolumeAdapter interface {
Expand Down
1 change: 1 addition & 0 deletions server/apps/containers/core/port/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type (
GetContainerEnv() gin.HandlerFunc
PatchEnvironment() gin.HandlerFunc
GetContainerPorts() gin.HandlerFunc
PatchContainerPorts() gin.HandlerFunc
GetDocker() gin.HandlerFunc
RecreateDocker() gin.HandlerFunc
GetLogs() gin.HandlerFunc
Expand Down
1 change: 1 addition & 0 deletions server/apps/containers/core/port/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type (
GetContainerEnv(ctx context.Context, id uuid.UUID) (types.EnvVariables, error)
SaveEnv(ctx context.Context, id uuid.UUID, env types.EnvVariables) error
GetContainerPorts(ctx context.Context, id uuid.UUID) (types.Ports, error)
SaveContainerPorts(ctx context.Context, id uuid.UUID, ports []types.Port) error
GetAllVersions(ctx context.Context, id uuid.UUID, useCache bool) ([]string, error)
GetContainerInfo(ctx context.Context, id uuid.UUID) (map[string]any, error)
WaitStatus(ctx context.Context, id uuid.UUID, status string) error
Expand Down
5 changes: 5 additions & 0 deletions server/apps/containers/core/port/services_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ func (m *MockContainerService) GetContainerPorts(ctx context.Context, id uuid.UU
return args.Get(0).(types.Ports), args.Error(1)
}

func (m *MockContainerService) SaveContainerPorts(ctx context.Context, id uuid.UUID, ports []types.Port) error {
args := m.Called(ctx, id, ports)
return args.Error(0)
}

func (m *MockContainerService) RecreateContainer(ctx context.Context, uuid uuid.UUID) error {
args := m.Called(ctx, uuid)
return args.Error(0)
Expand Down
18 changes: 14 additions & 4 deletions server/apps/containers/core/service/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,10 +623,6 @@ func (s *containerService) GetContainerEnv(ctx context.Context, id uuid.UUID) (t
return s.vars.GetContainerVariables(ctx, id)
}

func (s *containerService) GetContainerPorts(ctx context.Context, id uuid.UUID) (types.Ports, error) {
return s.ports.GetContainerPorts(ctx, id)
}

// remapDatabaseEnv remaps the environment variables of a container.
func (s *containerService) remapDatabaseEnv(ctx context.Context, c *types.Container, options map[string]*types.SetDatabasesOptions) error {
for databaseID, databaseContainerID := range c.Databases {
Expand Down Expand Up @@ -736,6 +732,20 @@ func (s *containerService) SaveEnv(ctx context.Context, id uuid.UUID, env types.
return s.RecreateContainer(ctx, id)
}

func (s *containerService) GetContainerPorts(ctx context.Context, id uuid.UUID) (types.Ports, error) {
return s.ports.GetContainerPorts(ctx, id)
}

func (s *containerService) SaveContainerPorts(ctx context.Context, id uuid.UUID, ports []types.Port) error {
for _, p := range ports {
err := s.ports.UpdateContainerPortByID(ctx, p)
if err != nil {
return err
}
}
return s.RecreateContainer(ctx, id)
}

func (s *containerService) GetAllVersions(ctx context.Context, id uuid.UUID, useCache bool) ([]string, error) {
c, err := s.containers.GetContainer(ctx, id)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions server/apps/containers/handler/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,17 @@ func (h *containerHandler) GetContainerPorts() gin.HandlerFunc {
return tonic.Handler(func(ctx *gin.Context, params *GetContainerPortsParams) (types.Ports, error) {
return h.containerService.GetContainerPorts(ctx, params.ContainerID.UUID)
}, http.StatusOK)
}

type PatchContainerPortsParams struct {
ContainerID uuid.NullUUID `path:"container_id"`
Ports []types.Port `body:"ports"`
}

func (h *containerHandler) PatchContainerPorts() gin.HandlerFunc {
return tonic.Handler(func(ctx *gin.Context, params *PatchContainerPortsParams) error {
return h.containerService.SaveContainerPorts(ctx, params.ContainerID.UUID, params.Ports)
}, http.StatusOK)
}

func (h *containerHandler) GetDocker() gin.HandlerFunc {
Expand Down

0 comments on commit d78df26

Please sign in to comment.