mirror of
https://github.com/moghtech/komodo.git
synced 2025-12-05 19:17:36 -06:00
sward resources use list items
This commit is contained in:
@@ -64,6 +64,8 @@ fn convert_node_list_item(
|
||||
state: node
|
||||
.status
|
||||
.and_then(|status| status.state.map(convert_state)),
|
||||
created_at: node.created_at,
|
||||
updated_at: node.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::Context;
|
||||
use bollard::query_parameters::ListSecretsOptions;
|
||||
use komodo_client::entities::docker::secret::{
|
||||
SecretSpec, SwarmSecret,
|
||||
SecretSpec, SwarmSecret, SwarmSecretListItem,
|
||||
};
|
||||
|
||||
use super::DockerClient;
|
||||
@@ -9,14 +9,14 @@ use super::DockerClient;
|
||||
impl DockerClient {
|
||||
pub async fn list_swarm_secrets(
|
||||
&self,
|
||||
) -> anyhow::Result<Vec<SwarmSecret>> {
|
||||
) -> anyhow::Result<Vec<SwarmSecretListItem>> {
|
||||
let secrets = self
|
||||
.docker
|
||||
.list_secrets(Option::<ListSecretsOptions>::None)
|
||||
.await
|
||||
.context("Failed to query for swarm secret list")?
|
||||
.into_iter()
|
||||
.map(convert_secret)
|
||||
.map(convert_secret_list_item)
|
||||
.collect();
|
||||
Ok(secrets)
|
||||
}
|
||||
@@ -38,6 +38,29 @@ impl DockerClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_secret_list_item(
|
||||
secret: bollard::models::Secret,
|
||||
) -> SwarmSecretListItem {
|
||||
let (name, driver, templating) = secret
|
||||
.spec
|
||||
.map(|spec| {
|
||||
(
|
||||
spec.name,
|
||||
spec.driver.map(|driver| driver.name),
|
||||
spec.templating.map(|driver| driver.name),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
SwarmSecretListItem {
|
||||
id: secret.id,
|
||||
name,
|
||||
driver,
|
||||
templating,
|
||||
created_at: secret.created_at,
|
||||
updated_at: secret.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_secret(secret: bollard::models::Secret) -> SwarmSecret {
|
||||
SwarmSecret {
|
||||
id: secret.id,
|
||||
|
||||
@@ -81,6 +81,8 @@ fn convert_service_list_item(
|
||||
image,
|
||||
restart,
|
||||
runtime,
|
||||
created_at: service.created_at,
|
||||
updated_at: service.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ use super::*;
|
||||
impl DockerClient {
|
||||
pub async fn list_swarm_tasks(
|
||||
&self,
|
||||
) -> anyhow::Result<Vec<SwarmTask>> {
|
||||
) -> anyhow::Result<Vec<SwarmTaskListItem>> {
|
||||
let tasks = self
|
||||
.docker
|
||||
.list_tasks(Option::<ListTasksOptions>::None)
|
||||
.await
|
||||
.context("Failed to query for swarm tasks list")?
|
||||
.into_iter()
|
||||
.map(convert_task)
|
||||
.map(convert_task_list_item)
|
||||
.collect();
|
||||
Ok(tasks)
|
||||
}
|
||||
@@ -34,6 +34,33 @@ impl DockerClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_task_list_item(
|
||||
task: bollard::models::Task,
|
||||
) -> SwarmTaskListItem {
|
||||
let (container_id, state) = task
|
||||
.status
|
||||
.map(|status| {
|
||||
(
|
||||
status
|
||||
.container_status
|
||||
.and_then(|status| status.container_id),
|
||||
status.state.map(convert_task_state),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
SwarmTaskListItem {
|
||||
id: task.id,
|
||||
name: task.name,
|
||||
node_id: task.node_id,
|
||||
service_id: task.service_id,
|
||||
container_id,
|
||||
state,
|
||||
desired_state: task.desired_state.map(convert_task_state),
|
||||
created_at: task.created_at,
|
||||
updated_at: task.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_task(task: bollard::models::Task) -> SwarmTask {
|
||||
SwarmTask {
|
||||
id: task.id,
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::entities::{
|
||||
docker::{
|
||||
config::{SwarmConfig, SwarmConfigListItem},
|
||||
node::{SwarmNode, SwarmNodeListItem},
|
||||
secret::SwarmSecret,
|
||||
secret::{SwarmSecret, SwarmSecretListItem},
|
||||
service::{SwarmService, SwarmServiceListItem},
|
||||
swarm::SwarmInspectInfo,
|
||||
task::SwarmTask,
|
||||
task::{SwarmTask, SwarmTaskListItem},
|
||||
},
|
||||
swarm::{Swarm, SwarmActionState, SwarmListItem, SwarmQuery},
|
||||
};
|
||||
@@ -242,7 +242,7 @@ pub struct ListSwarmTasks {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type ListSwarmTasksResponse = Vec<SwarmTask>;
|
||||
pub type ListSwarmTasksResponse = Vec<SwarmTaskListItem>;
|
||||
|
||||
//
|
||||
|
||||
@@ -284,7 +284,7 @@ pub struct ListSwarmSecrets {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type ListSwarmSecretsResponse = Vec<SwarmSecret>;
|
||||
pub type ListSwarmSecretsResponse = Vec<SwarmSecretListItem>;
|
||||
|
||||
//
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::entities::{
|
||||
docker::{
|
||||
config::SwarmConfigListItem, container::ContainerListItem,
|
||||
image::ImageListItem, network::NetworkListItem,
|
||||
node::SwarmNodeListItem, secret::SwarmSecret,
|
||||
service::SwarmServiceListItem, task::SwarmTask,
|
||||
node::SwarmNodeListItem, secret::SwarmSecretListItem,
|
||||
service::SwarmServiceListItem, task::SwarmTaskListItem,
|
||||
volume::VolumeListItem,
|
||||
},
|
||||
stack::ComposeProject,
|
||||
@@ -33,8 +33,8 @@ pub mod volume;
|
||||
pub struct SwarmLists {
|
||||
pub nodes: Vec<SwarmNodeListItem>,
|
||||
pub services: Vec<SwarmServiceListItem>,
|
||||
pub tasks: Vec<SwarmTask>,
|
||||
pub secrets: Vec<SwarmSecret>,
|
||||
pub tasks: Vec<SwarmTaskListItem>,
|
||||
pub secrets: Vec<SwarmSecretListItem>,
|
||||
pub configs: Vec<SwarmConfigListItem>,
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,14 @@ pub struct SwarmNodeListItem {
|
||||
/// State of the node
|
||||
#[serde(rename = "State")]
|
||||
pub state: Option<NodeState>,
|
||||
|
||||
/// Date and time at which the node was added to the swarm in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
|
||||
#[serde(rename = "CreatedAt")]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
/// Date and time at which the node was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
|
||||
#[serde(rename = "UpdatedAt")]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Swarm node details.
|
||||
|
||||
@@ -5,6 +5,35 @@ use typeshare::typeshare;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Swarm secret list item.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct SwarmSecretListItem {
|
||||
#[serde(rename = "ID")]
|
||||
pub id: Option<String>,
|
||||
|
||||
/// User-defined name of the secret.
|
||||
#[serde(rename = "Name")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Name of the secrets driver used to fetch the secret's value from an external secret store.
|
||||
#[serde(rename = "Driver")]
|
||||
pub driver: Option<String>,
|
||||
|
||||
/// Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template.
|
||||
/// If no driver is set, no templating is used.
|
||||
#[serde(rename = "Templating")]
|
||||
pub templating: Option<String>,
|
||||
|
||||
#[serde(rename = "CreatedAt")]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
#[serde(rename = "UpdatedAt")]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Swarm secret details.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
|
||||
@@ -40,6 +40,12 @@ pub struct SwarmServiceListItem {
|
||||
/// Number of replicas
|
||||
#[serde(rename = "Replicas")]
|
||||
pub replicas: Option<I64>,
|
||||
|
||||
#[serde(rename = "CreatedAt")]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
#[serde(rename = "UpdatedAt")]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Swarm service details.
|
||||
|
||||
@@ -3,6 +3,46 @@ use typeshare::typeshare;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Swarm task list item.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct SwarmTaskListItem {
|
||||
/// The ID of the task.
|
||||
#[serde(rename = "ID")]
|
||||
pub id: Option<String>,
|
||||
|
||||
/// Name of the task.
|
||||
#[serde(rename = "Name")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// The ID of the node that this task is on.
|
||||
#[serde(rename = "NodeID")]
|
||||
pub node_id: Option<String>,
|
||||
|
||||
/// The ID of the service this task is part of.
|
||||
#[serde(rename = "ServiceID")]
|
||||
pub service_id: Option<String>,
|
||||
|
||||
/// The ID of container associated with this task.
|
||||
#[serde(rename = "ContainerID")]
|
||||
pub container_id: Option<String>,
|
||||
|
||||
#[serde(rename = "State")]
|
||||
pub state: Option<TaskState>,
|
||||
|
||||
#[serde(rename = "DesiredState")]
|
||||
pub desired_state: Option<TaskState>,
|
||||
|
||||
#[serde(rename = "CreatedAt")]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
#[serde(rename = "UpdatedAt")]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Swarm task details.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
|
||||
|
||||
@@ -4372,6 +4372,7 @@ export interface TaskStatus {
|
||||
PortStatus?: PortStatus;
|
||||
}
|
||||
|
||||
/** Swarm task details. */
|
||||
export interface SwarmTask {
|
||||
/** The ID of the task. */
|
||||
ID?: string;
|
||||
@@ -5003,11 +5004,31 @@ export interface SwarmNodeListItem {
|
||||
Availability?: NodeSpecAvailabilityEnum;
|
||||
/** State of the node */
|
||||
State?: NodeState;
|
||||
/** Date and time at which the node was added to the swarm in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
|
||||
CreatedAt?: string;
|
||||
/** Date and time at which the node was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
export type ListSwarmNodesResponse = SwarmNodeListItem[];
|
||||
|
||||
export type ListSwarmSecretsResponse = SwarmSecret[];
|
||||
/** Swarm secret list item. */
|
||||
export interface SwarmSecretListItem {
|
||||
ID?: string;
|
||||
/** User-defined name of the secret. */
|
||||
Name?: string;
|
||||
/** Name of the secrets driver used to fetch the secret's value from an external secret store. */
|
||||
Driver?: string;
|
||||
/**
|
||||
* Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template.
|
||||
* If no driver is set, no templating is used.
|
||||
*/
|
||||
Templating?: string;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
export type ListSwarmSecretsResponse = SwarmSecretListItem[];
|
||||
|
||||
/** Swarm service list item. */
|
||||
export interface SwarmServiceListItem {
|
||||
@@ -5022,11 +5043,31 @@ export interface SwarmServiceListItem {
|
||||
Restart?: TaskSpecRestartPolicyConditionEnum;
|
||||
/** Number of replicas */
|
||||
Replicas?: I64;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
export type ListSwarmServicesResponse = SwarmServiceListItem[];
|
||||
|
||||
export type ListSwarmTasksResponse = SwarmTask[];
|
||||
/** Swarm task list item. */
|
||||
export interface SwarmTaskListItem {
|
||||
/** The ID of the task. */
|
||||
ID?: string;
|
||||
/** Name of the task. */
|
||||
Name?: string;
|
||||
/** The ID of the node that this task is on. */
|
||||
NodeID?: string;
|
||||
/** The ID of the service this task is part of. */
|
||||
ServiceID?: string;
|
||||
/** The ID of container associated with this task. */
|
||||
ContainerID?: string;
|
||||
State?: TaskState;
|
||||
DesiredState?: TaskState;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
export type ListSwarmTasksResponse = SwarmTaskListItem[];
|
||||
|
||||
export enum SwarmState {
|
||||
/** Unknown case */
|
||||
|
||||
43
frontend/public/client/types.d.ts
vendored
43
frontend/public/client/types.d.ts
vendored
@@ -4324,6 +4324,7 @@ export interface TaskStatus {
|
||||
ContainerStatus?: ContainerStatus;
|
||||
PortStatus?: PortStatus;
|
||||
}
|
||||
/** Swarm task details. */
|
||||
export interface SwarmTask {
|
||||
/** The ID of the task. */
|
||||
ID?: string;
|
||||
@@ -4878,9 +4879,28 @@ export interface SwarmNodeListItem {
|
||||
Availability?: NodeSpecAvailabilityEnum;
|
||||
/** State of the node */
|
||||
State?: NodeState;
|
||||
/** Date and time at which the node was added to the swarm in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
|
||||
CreatedAt?: string;
|
||||
/** Date and time at which the node was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
export type ListSwarmNodesResponse = SwarmNodeListItem[];
|
||||
export type ListSwarmSecretsResponse = SwarmSecret[];
|
||||
/** Swarm secret list item. */
|
||||
export interface SwarmSecretListItem {
|
||||
ID?: string;
|
||||
/** User-defined name of the secret. */
|
||||
Name?: string;
|
||||
/** Name of the secrets driver used to fetch the secret's value from an external secret store. */
|
||||
Driver?: string;
|
||||
/**
|
||||
* Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template.
|
||||
* If no driver is set, no templating is used.
|
||||
*/
|
||||
Templating?: string;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
export type ListSwarmSecretsResponse = SwarmSecretListItem[];
|
||||
/** Swarm service list item. */
|
||||
export interface SwarmServiceListItem {
|
||||
ID?: string;
|
||||
@@ -4894,9 +4914,28 @@ export interface SwarmServiceListItem {
|
||||
Restart?: TaskSpecRestartPolicyConditionEnum;
|
||||
/** Number of replicas */
|
||||
Replicas?: I64;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
export type ListSwarmServicesResponse = SwarmServiceListItem[];
|
||||
export type ListSwarmTasksResponse = SwarmTask[];
|
||||
/** Swarm task list item. */
|
||||
export interface SwarmTaskListItem {
|
||||
/** The ID of the task. */
|
||||
ID?: string;
|
||||
/** Name of the task. */
|
||||
Name?: string;
|
||||
/** The ID of the node that this task is on. */
|
||||
NodeID?: string;
|
||||
/** The ID of the service this task is part of. */
|
||||
ServiceID?: string;
|
||||
/** The ID of container associated with this task. */
|
||||
ContainerID?: string;
|
||||
State?: TaskState;
|
||||
DesiredState?: TaskState;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
export type ListSwarmTasksResponse = SwarmTaskListItem[];
|
||||
export declare enum SwarmState {
|
||||
/** Unknown case */
|
||||
Unknown = "Unknown",
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { RequiredResourceComponents } from "@types";
|
||||
import { Component } from "lucide-react";
|
||||
import {
|
||||
Component,
|
||||
Diamond,
|
||||
FolderCode,
|
||||
KeyRound,
|
||||
ListTodo,
|
||||
Settings,
|
||||
} from "lucide-react";
|
||||
import { DeleteResource, NewResource, ResourcePageHeader } from "../common";
|
||||
import { SwarmTable } from "./table";
|
||||
import {
|
||||
@@ -15,6 +22,7 @@ import { GroupActions } from "@components/group-actions";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
import { Card } from "@ui/card";
|
||||
import { SwarmTabs } from "./tabs";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export const useSwarm = (id?: string) =>
|
||||
useRead("ListSwarms", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -124,3 +132,43 @@ export const SwarmComponents: RequiredResourceComponents = {
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export type SwarmResourceType =
|
||||
| "Node"
|
||||
| "Service"
|
||||
| "Task"
|
||||
| "Secret"
|
||||
| "Config";
|
||||
|
||||
export const SWARM_ICONS: {
|
||||
[type in SwarmResourceType]: React.FC<{ size?: number }>;
|
||||
} = {
|
||||
Node: ({ size }) => <Diamond className={`w-${size} h-${size}`} />,
|
||||
Service: ({ size }) => <FolderCode className={`w-${size} h-${size}`} />,
|
||||
Task: ({ size }) => <ListTodo className={`w-${size} h-${size}`} />,
|
||||
Secret: ({ size }) => <KeyRound className={`w-${size} h-${size}`} />,
|
||||
Config: ({ size }) => <Settings className={`w-${size} h-${size}`} />,
|
||||
};
|
||||
|
||||
export const SwarmLink = ({
|
||||
type,
|
||||
swarm_id,
|
||||
resource_id,
|
||||
name,
|
||||
}: {
|
||||
type: SwarmResourceType;
|
||||
swarm_id: string;
|
||||
resource_id: string | undefined;
|
||||
name: string | undefined;
|
||||
}) => {
|
||||
const Icon = SWARM_ICONS[type];
|
||||
return (
|
||||
<Link
|
||||
to={`/swarms/${swarm_id}/swarm-${type.toLowerCase()}/${resource_id}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<Icon size={4} />
|
||||
{name ?? "Unknown"}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { KeyRound, Search } from "lucide-react";
|
||||
import { Search } from "lucide-react";
|
||||
import { Input } from "@ui/input";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SwarmLink } from "..";
|
||||
|
||||
export const SwarmConfigs = ({
|
||||
id,
|
||||
@@ -55,15 +55,13 @@ export const SwarmConfigs = ({
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
to={`/swarms/${id}/swarm-config/${row.original.ID}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<KeyRound className="w-4 h-4" />
|
||||
{row.original.Name ?? "Unknown"}
|
||||
</Link>
|
||||
<SwarmLink
|
||||
type="Config"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.ID}
|
||||
name={row.original.Name}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "ID",
|
||||
@@ -71,7 +69,18 @@ export const SwarmConfigs = ({
|
||||
<SortableHeader column={column} title="Id" />
|
||||
),
|
||||
cell: ({ row }) => row.original.ID ?? "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "UpdatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Updated" />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Created" />
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { Diamond, Search } from "lucide-react";
|
||||
import { Search } from "lucide-react";
|
||||
import { Input } from "@ui/input";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SwarmLink } from "..";
|
||||
|
||||
export const SwarmNodes = ({
|
||||
id,
|
||||
@@ -55,13 +55,12 @@ export const SwarmNodes = ({
|
||||
<SortableHeader column={column} title="Hostname" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
to={`/swarms/${id}/swarm-node/${row.original.ID}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<Diamond className="w-4 h-4" />
|
||||
{row.original.Hostname ?? "Unknown"}
|
||||
</Link>
|
||||
<SwarmLink
|
||||
type="Node"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.ID}
|
||||
name={row.original.Hostname}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
@@ -79,7 +78,6 @@ export const SwarmNodes = ({
|
||||
<SortableHeader column={column} title="Role" />
|
||||
),
|
||||
cell: ({ row }) => row.original.Role ?? "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "Availability",
|
||||
@@ -87,7 +85,6 @@ export const SwarmNodes = ({
|
||||
<SortableHeader column={column} title="Availability" />
|
||||
),
|
||||
cell: ({ row }) => row.original.Availability ?? "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "State",
|
||||
@@ -96,6 +93,28 @@ export const SwarmNodes = ({
|
||||
),
|
||||
cell: ({ row }) => row.original.State ?? "Unknown",
|
||||
},
|
||||
{
|
||||
accessorKey: "UpdatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Updated" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.UpdatedAt
|
||||
? new Date(row.original.UpdatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Created" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.CreatedAt
|
||||
? new Date(row.original.CreatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { KeyRound, Search } from "lucide-react";
|
||||
import { Search } from "lucide-react";
|
||||
import { Input } from "@ui/input";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SwarmLink } from "..";
|
||||
|
||||
export const SwarmSecrets = ({
|
||||
id,
|
||||
@@ -24,7 +24,7 @@ export const SwarmSecrets = ({
|
||||
const filtered = filterBySplit(
|
||||
secrets,
|
||||
search,
|
||||
(secret) => secret.Spec?.Name ?? secret.ID ?? "Unknown"
|
||||
(secret) => secret.Name ?? secret.ID ?? "Unknown"
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -52,16 +52,15 @@ export const SwarmSecrets = ({
|
||||
{
|
||||
accessorKey: "Name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="ID" />
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
to={`/swarms/${id}/swarm-secret/${row.original.ID}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<KeyRound className="w-4 h-4" />
|
||||
{row.original.Spec?.Name ?? "Unknown"}
|
||||
</Link>
|
||||
<SwarmLink
|
||||
type="Secret"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.ID}
|
||||
name={row.original.Name}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
@@ -73,6 +72,48 @@ export const SwarmSecrets = ({
|
||||
cell: ({ row }) => row.original.ID ?? "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "Driver",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Driver" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.Driver ?? (
|
||||
<div className="text-muted-foreground">None</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "Templating",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Templating" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.Templating ?? (
|
||||
<div className="text-muted-foreground">None</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "UpdatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Updated" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.UpdatedAt
|
||||
? new Date(row.original.UpdatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Created" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.CreatedAt
|
||||
? new Date(row.original.CreatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { FolderCode, Search } from "lucide-react";
|
||||
import { Search } from "lucide-react";
|
||||
import { Input } from "@ui/input";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SwarmLink } from "..";
|
||||
|
||||
export const SwarmServices = ({
|
||||
id,
|
||||
@@ -55,13 +55,12 @@ export const SwarmServices = ({
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
to={`/swarms/${id}/swarm-service/${row.original.ID}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<FolderCode className="w-4 h-4" />
|
||||
{row.original.Name ?? "Unknown"}
|
||||
</Link>
|
||||
<SwarmLink
|
||||
type="Service"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.ID}
|
||||
name={row.original.Name}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
@@ -73,6 +72,28 @@ export const SwarmServices = ({
|
||||
cell: ({ row }) => row.original.ID ?? "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "UpdatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Updated" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.UpdatedAt
|
||||
? new Date(row.original.UpdatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Created" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.CreatedAt
|
||||
? new Date(row.original.CreatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { ListTodo, Search } from "lucide-react";
|
||||
import { Search } from "lucide-react";
|
||||
import { Input } from "@ui/input";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SwarmLink } from "..";
|
||||
|
||||
export const SwarmTasks = ({
|
||||
id,
|
||||
@@ -17,11 +17,25 @@ export const SwarmTasks = ({
|
||||
_search: [string, Dispatch<SetStateAction<string>>];
|
||||
}) => {
|
||||
const [search, setSearch] = _search;
|
||||
const tasks =
|
||||
const services =
|
||||
useRead("ListSwarmServices", { swarm: id }, { refetchInterval: 10_000 })
|
||||
.data ?? [];
|
||||
const _tasks =
|
||||
useRead("ListSwarmTasks", { swarm: id }, { refetchInterval: 10_000 })
|
||||
.data ?? [];
|
||||
|
||||
const filtered = filterBySplit(tasks, search, (task) => task.ID ?? "Unknown");
|
||||
const tasks = _tasks.map((task) => {
|
||||
return {
|
||||
...task,
|
||||
service: services.find((service) => task.ServiceID === service.ID),
|
||||
};
|
||||
});
|
||||
|
||||
const filtered = filterBySplit(
|
||||
tasks,
|
||||
search,
|
||||
(task) => task.Name ?? task.service?.Name ?? "Unknown"
|
||||
);
|
||||
|
||||
return (
|
||||
<Section
|
||||
@@ -51,16 +65,64 @@ export const SwarmTasks = ({
|
||||
<SortableHeader column={column} title="Id" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
to={`/swarms/${id}/swarm-task/${row.original.ID}`}
|
||||
className="flex gap-2 items-center hover:underline"
|
||||
>
|
||||
<ListTodo className="w-4 h-4" />
|
||||
{row.original.ID ?? "Unknown"}
|
||||
</Link>
|
||||
<SwarmLink
|
||||
type="Task"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.ID}
|
||||
name={row.original.ID}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "service.Name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Service" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<SwarmLink
|
||||
type="Service"
|
||||
swarm_id={id}
|
||||
resource_id={row.original.service?.ID}
|
||||
name={row.original.service?.Name}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "State",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="State" />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "DesiredState",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Desired State" />
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "UpdatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Updated" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.UpdatedAt
|
||||
? new Date(row.original.UpdatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "CreatedAt",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Created" />
|
||||
),
|
||||
cell: ({ row }) =>
|
||||
row.original.CreatedAt
|
||||
? new Date(row.original.CreatedAt).toLocaleString()
|
||||
: "Unknown",
|
||||
size: 200,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
@@ -632,7 +632,11 @@ export const ShowHideButton = ({
|
||||
);
|
||||
};
|
||||
|
||||
type DockerResourceType = "container" | "network" | "image" | "volume";
|
||||
type DockerResourceType =
|
||||
| "container"
|
||||
| "network"
|
||||
| "image"
|
||||
| "volume";
|
||||
|
||||
export const DOCKER_LINK_ICONS: {
|
||||
[type in DockerResourceType]: React.FC<{
|
||||
|
||||
Reference in New Issue
Block a user