forked from github-starred/komodo
smth
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,6 +28,7 @@ export const ServerConfig = ({ id }: { id: string }) => {
|
||||
region: true,
|
||||
enabled: true,
|
||||
auto_prune: true,
|
||||
send_unreachable_alerts: true,
|
||||
},
|
||||
},
|
||||
warnings: {
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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} /> */}
|
||||
|
||||
5
frontend/src/types.d.ts
vendored
5
frontend/src/types.d.ts
vendored
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user