This commit is contained in:
mbecker20
2024-03-31 22:41:40 -07:00
parent 05886bed71
commit bc9877133c
10 changed files with 194 additions and 114 deletions

View File

@@ -3,14 +3,15 @@ use async_trait::async_trait;
use monitor_client::entities::{update::Log, SystemCommand};
use periphery_client::api::{
build::*, container::*, git::*, network::*, stats::*, GetAccounts,
GetHealth, GetSecrets, GetVersion, GetVersionResponse, RunCommand,
GetHealth, GetSecrets, GetVersion, GetVersionResponse, PruneAll,
RunCommand,
};
use resolver_api::{derive::Resolver, Resolve, ResolveToString};
use serde::{Deserialize, Serialize};
use crate::{
config::{accounts_response, secrets_response},
helpers::run_monitor_command,
helpers::{docker, run_monitor_command},
State,
};
@@ -70,6 +71,7 @@ pub enum PeripheryRequest {
CreateNetwork(CreateNetwork),
DeleteNetwork(DeleteNetwork),
PruneNetworks(PruneNetworks),
PruneAll(PruneAll),
}
//
@@ -147,3 +149,22 @@ impl Resolve<RunCommand> for State {
.context("failure in spawned task")
}
}
#[async_trait]
impl Resolve<PruneAll> for State {
async fn resolve(
&self,
PruneAll {}: PruneAll,
_: (),
) -> anyhow::Result<Vec<Log>> {
tokio::spawn(async move {
let mut logs = Vec::new();
logs.push(docker::prune_images().await);
logs.push(docker::container::prune_containers().await);
logs.push(docker::network::prune_networks().await);
logs
})
.await
.context("failure in spawned task")
}
}

View File

@@ -113,6 +113,57 @@ pub struct GetPeripheryVersionResponse {
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerNetworksResponse)]
pub struct GetDockerNetworks {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerNetworksResponse = Vec<DockerNetwork>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerImagesResponse)]
pub struct GetDockerImages {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerImagesResponse = Vec<ImageSummary>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerContainersResponse)]
pub struct GetDockerContainers {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerContainersResponse = Vec<ContainerSummary>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -249,23 +300,6 @@ pub type GetSystemComponentsResponse = Vec<SystemComponent>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerNetworksResponse)]
pub struct GetDockerNetworks {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerNetworksResponse = Vec<DockerNetwork>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
@@ -290,40 +324,6 @@ pub struct GetHistoricalServerStatsResponse {
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerImagesResponse)]
pub struct GetDockerImages {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerImagesResponse = Vec<ImageSummary>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,
)]
#[empty_traits(MonitorReadRequest)]
#[response(GetDockerContainersResponse)]
pub struct GetDockerContainers {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub server: String,
}
#[typeshare]
pub type GetDockerContainersResponse = Vec<ContainerSummary>;
//
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Request, EmptyTraits,

View File

@@ -48,6 +48,12 @@ pub struct GetSecrets {}
//
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
#[response(Vec<Log>)]
pub struct PruneAll {}
//
#[derive(Serialize, Deserialize, Debug, Clone, Request)]
#[response(Log)]
pub struct RunCommand {

View File

@@ -48,56 +48,58 @@ export const BuildComponents: RequiredResourceComponents = {
if (id) return <Icon id={id} />;
else return <Hammer className="w-4 h-4" />;
},
Actions: ({ id }) => {
const { toast } = useToast();
const building = useRead("GetBuildActionState", { build: id }).data
?.building;
const { mutate: run_mutate, isPending: runPending } = useExecute(
"RunBuild",
{
onMutate: () => {
toast({ title: "Run Build Sent" });
},
}
);
const { mutate: cancel_mutate, isPending: cancelPending } = useExecute(
"CancelBuild",
{
onMutate: () => {
toast({ title: "Cancel Build Sent" });
},
onSuccess: () => {
toast({ title: "Build Cancelled" });
},
}
);
if (building) {
return (
<ConfirmButton
title="Cancel Build"
variant="destructive"
icon={<Ban className="h-4 w-4" />}
onClick={() => cancel_mutate({ build: id })}
disabled={cancelPending}
/>
Actions: [
({ id }) => {
const { toast } = useToast();
const building = useRead("GetBuildActionState", { build: id }).data
?.building;
const { mutate: run_mutate, isPending: runPending } = useExecute(
"RunBuild",
{
onMutate: () => {
toast({ title: "Run Build Sent" });
},
}
);
} else {
return (
<ConfirmButton
title="Build"
icon={
runPending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Hammer className="h-4 w-4" />
)
}
onClick={() => run_mutate({ build: id })}
disabled={runPending}
/>
const { mutate: cancel_mutate, isPending: cancelPending } = useExecute(
"CancelBuild",
{
onMutate: () => {
toast({ title: "Cancel Build Sent" });
},
onSuccess: () => {
toast({ title: "Build Cancelled" });
},
}
);
}
},
if (building) {
return (
<ConfirmButton
title="Cancel Build"
variant="destructive"
icon={<Ban className="h-4 w-4" />}
onClick={() => cancel_mutate({ build: id })}
disabled={cancelPending}
/>
);
} else {
return (
<ConfirmButton
title="Build"
icon={
runPending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Hammer className="h-4 w-4" />
)
}
onClick={() => run_mutate({ build: id })}
disabled={runPending}
/>
);
}
},
],
New: () => {
const { mutateAsync } = useWrite("CreateBuild");
const [name, setName] = useState("");

View File

@@ -3,7 +3,7 @@ import {
ActionWithDialog,
ConfirmButton,
} from "@components/util";
import { Play, Trash, Pause, Rocket, Pen } from "lucide-react";
import { Play, Trash, Pause, Rocket, Pen, Loader2 } from "lucide-react";
import { useExecute, useInvalidate, useRead, useWrite } from "@lib/hooks";
import { useNavigate } from "react-router-dom";
import { Input } from "@ui/input";
@@ -21,14 +21,20 @@ export const RedeployContainer = ({ id }: DeploymentId) => {
const deployment = deployments?.find((d) => d.id === id);
const deploying = useRead("GetDeploymentActionState", { deployment: id }).data
?.deploying;
const pending = isPending || deploying;
return (
<ConfirmButton
title={deployment?.info.status ? "Redeploy" : "Deploy"}
icon={<Rocket className="h-4 w-4" />}
icon={
pending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Rocket className="h-4 w-4" />
)
}
onClick={() => mutate({ deployment: id })}
disabled={isPending}
loading={deploying}
disabled={pending}
loading={pending}
/>
);
};

View File

@@ -1,11 +1,48 @@
import { ActionButton, ActionWithDialog } from "@components/util";
import { useInvalidate, useRead, useWrite } from "@lib/hooks";
import {
ActionButton,
ActionWithDialog,
ConfirmButton,
} from "@components/util";
import { useExecute, useInvalidate, useRead, useWrite } from "@lib/hooks";
import { Types } from "@monitor/client";
import { IdComponent } from "@types";
import { Input } from "@ui/input";
import { useToast } from "@ui/use-toast";
import { Pen, Trash } from "lucide-react";
import { Loader2, Pen, Scissors, Trash } from "lucide-react";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
type PruneType = "Images" | "Containers" | "Networks";
const PruneButton = ({ type, id }: { type: PruneType; id: string }) => {
const { mutate, isPending } = useExecute(`Prune${type}`);
const pruning = useRead("GetServerActionState", { server: id }).data?.[
`pruning_${type.toLowerCase()}` as keyof Types.ServerActionState
];
const pending = isPending || pruning;
return (
<ConfirmButton
title={`Prune ${type}`}
icon={
pending ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Scissors className="w-4 h-4" />
)
}
onClick={() => mutate({ server: id })}
/>
);
};
const PRUNE_TYPES: PruneType[] = ["Images", "Containers"];
export const SERVER_ACTIONS: IdComponent[] = PRUNE_TYPES.map(
(type) =>
({ id }) =>
<PruneButton type={type} id={id} />
);
export const DeleteServer = ({ id }: { id: string }) => {
const nav = useNavigate();
const server = useRead("GetServer", { server: id }).data;

View File

@@ -28,6 +28,7 @@ export const ServerConfig = ({ id }: { id: string }) => {
region: true,
enabled: true,
auto_prune: true,
send_unreachable_alerts: true,
},
},
warnings: {

View File

@@ -7,7 +7,7 @@ import { ServerStats } from "./stats";
import { useState } from "react";
import { NewResource, Section } from "@components/layouts";
import { Input } from "@ui/input";
import { DeleteServer, RenameServer } from "./actions";
import { DeleteServer, RenameServer, SERVER_ACTIONS } from "./actions";
import {
fill_color_class_by_intention,
server_status_intention,
@@ -50,7 +50,7 @@ export const ServerComponents: RequiredResourceComponents = {
</div>
);
},
Actions: () => null,
Actions: SERVER_ACTIONS,
Page: {
Stats: ({ id }) => <ServerStats server_id={id} />,
Deployments: ({ id }) => {

View File

@@ -38,7 +38,13 @@ export const Resource = () => {
</div>
</div>
}
actions={<Components.Actions id={id} />}
actions={
<div className="flex gap-2 items-center">
{Components.Actions.map((Action) => (
<Action id={id} />
))}
</div>
}
>
<ResourceUpdates type={type} id={id} />
{/* <ResourcePermissions type={type} id={id} /> */}

View File

@@ -16,10 +16,11 @@ export interface RequiredResourceComponents {
Name: IdComponent;
Description: IdComponent;
Info: IdComponent[];
Status: IdComponent;
Link: IdComponent;
Actions: IdComponent;
Info: IdComponent[];
Actions: IdComponent[];
Table: React.FC;