mirror of
https://github.com/moghtech/komodo.git
synced 2025-12-05 19:17:36 -06:00
1.18.1 (#566)
* 1.18.1 * improve stack header / all resource links * disable build config selector * clean up deployment header * update build header * builder header * update repo header * start adding repo links from api * implement list item repo link * clean up fe * gen client * repo links across the board * include state tracking buffer, so alerts are only triggered by consecutive out of bounds conditions * add runnables-cli link in runfile * improve frontend first load time through some code splitting * add services count to stack header * fix repo on pull * Add dedicated Deploying state to Deployments and Stacks * move predeploy script before compose config (#584) * Periphery / core version mismatch check / red text * move builders / alerts out of sidebar, into settings * remove force push * list schedules api * dev-1 * actually dev-3 * fix action * filter none procedures * fix schedule api * dev-5 * basic schedules page * prog on schedule page * simplify schedule * use name to sort target * add resource tags to schedule * Schedule page working * dev-6 * remove schedule table type column * reorder schedule table * force confirm dialogs for delete, even if disabled in config * 1.18.1 --------- Co-authored-by: undaunt <31376520+undaunt@users.noreply.github.com>
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -890,7 +890,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cache"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
@@ -1057,7 +1057,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "command"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"formatting",
|
||||
@@ -1541,7 +1541,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment_file"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
@@ -1621,7 +1621,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "formatting"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"serror",
|
||||
]
|
||||
@@ -1783,7 +1783,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cache",
|
||||
@@ -2520,7 +2520,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_cli"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2536,7 +2536,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_client"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2568,7 +2568,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_core"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -2637,7 +2637,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_periphery"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2681,7 +2681,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_util"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenvy",
|
||||
@@ -2770,7 +2770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "logger"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -3525,7 +3525,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -4053,7 +4053,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "response"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
||||
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub enum ExecutionResult {
|
||||
Single(Update),
|
||||
Single(Box<Update>),
|
||||
Batch(BatchExecutionResponse),
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::RunAction(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunAction(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -235,7 +235,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::RunProcedure(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunProcedure(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -243,7 +243,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::RunBuild(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunBuild(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -251,11 +251,11 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::CancelBuild(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Deploy(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeploy(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -263,31 +263,31 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::PullDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyDeployment(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -295,7 +295,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::CloneRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchCloneRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -303,7 +303,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::PullRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -311,7 +311,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::BuildRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchBuildRepo(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -319,103 +319,103 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::CancelRepoBuild(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyContainer(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartAllContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartAllContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseAllContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseAllContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopAllContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneContainers(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteNetwork(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneNetworks(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteImage(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneImages(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteVolume(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneVolumes(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneDockerBuilders(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneBuildx(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneSystem(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RunSync(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CommitSync(request) => komodo_client()
|
||||
.write(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeployStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -423,7 +423,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::DeployStackIfChanged(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStackIfChanged(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -431,7 +431,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::PullStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -439,27 +439,27 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::StartStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyStack(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
@@ -467,7 +467,7 @@ pub async fn run(execution: Execution) -> anyhow::Result<()> {
|
||||
Execution::TestAlerter(request) => komodo_client()
|
||||
.execute(request)
|
||||
.await
|
||||
.map(ExecutionResult::Single),
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
|
||||
@@ -588,8 +588,9 @@ async fn handle_post_build_redeploy(build_id: &str) {
|
||||
redeploy_deployments
|
||||
.into_iter()
|
||||
.map(|deployment| async move {
|
||||
let state =
|
||||
get_deployment_state(&deployment).await.unwrap_or_default();
|
||||
let state = get_deployment_state(&deployment.id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
if state == DeploymentState::Running {
|
||||
let req = super::ExecuteRequest::Deploy(Deploy {
|
||||
deployment: deployment.id.clone(),
|
||||
|
||||
@@ -174,8 +174,11 @@ async fn handler(
|
||||
Ok((TypedHeader(ContentType::json()), res))
|
||||
}
|
||||
|
||||
#[typeshare(serialized_as = "Update")]
|
||||
type BoxUpdate = Box<Update>;
|
||||
|
||||
pub enum ExecutionResult {
|
||||
Single(Update),
|
||||
Single(BoxUpdate),
|
||||
/// The batch contents will be pre serialized here
|
||||
Batch(String),
|
||||
}
|
||||
@@ -245,7 +248,7 @@ pub fn inner_handler(
|
||||
}
|
||||
});
|
||||
|
||||
Ok(ExecutionResult::Single(update))
|
||||
Ok(ExecutionResult::Single(update.into()))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ impl Resolve<ExecuteArgs> for CloneRepo {
|
||||
impl super::BatchExecute for BatchPullRepo {
|
||||
type Resource = Repo;
|
||||
fn single_request(repo: String) -> ExecuteRequest {
|
||||
ExecuteRequest::CloneRepo(CloneRepo { repo })
|
||||
ExecuteRequest::PullRepo(PullRepo { repo })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ mod permission;
|
||||
mod procedure;
|
||||
mod provider;
|
||||
mod repo;
|
||||
mod schedule;
|
||||
mod server;
|
||||
mod stack;
|
||||
mod sync;
|
||||
@@ -98,6 +99,9 @@ enum ReadRequest {
|
||||
ListActions(ListActions),
|
||||
ListFullActions(ListFullActions),
|
||||
|
||||
// ==== SCHEDULE ====
|
||||
ListSchedules(ListSchedules),
|
||||
|
||||
// ==== SERVER ====
|
||||
GetServersSummary(GetServersSummary),
|
||||
GetServer(GetServer),
|
||||
|
||||
102
bin/core/src/api/read/schedule.rs
Normal file
102
bin/core/src/api/read/schedule.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use futures::future::join_all;
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
ResourceTarget, action::Action, permission::PermissionLevel,
|
||||
procedure::Procedure, resource::ResourceQuery,
|
||||
schedule::Schedule,
|
||||
},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_all_tags, get_last_run_at},
|
||||
resource::list_full_for_user,
|
||||
schedule::get_schedule_item_info,
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
|
||||
impl Resolve<ReadArgs> for ListSchedules {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &ReadArgs,
|
||||
) -> serror::Result<Vec<Schedule>> {
|
||||
let all_tags = get_all_tags(None).await?;
|
||||
let (actions, procedures) = tokio::try_join!(
|
||||
list_full_for_user::<Action>(
|
||||
ResourceQuery {
|
||||
names: Default::default(),
|
||||
tag_behavior: self.tag_behavior,
|
||||
tags: self.tags.clone(),
|
||||
specific: Default::default(),
|
||||
},
|
||||
&args.user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
),
|
||||
list_full_for_user::<Procedure>(
|
||||
ResourceQuery {
|
||||
names: Default::default(),
|
||||
tag_behavior: self.tag_behavior,
|
||||
tags: self.tags.clone(),
|
||||
specific: Default::default(),
|
||||
},
|
||||
&args.user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
)?;
|
||||
let actions = actions.into_iter().map(async |action| {
|
||||
let (next_scheduled_run, schedule_error) =
|
||||
get_schedule_item_info(&ResourceTarget::Action(
|
||||
action.id.clone(),
|
||||
));
|
||||
let last_run_at =
|
||||
get_last_run_at::<Action>(&action.id).await.unwrap_or(None);
|
||||
Schedule {
|
||||
target: ResourceTarget::Action(action.id),
|
||||
name: action.name,
|
||||
enabled: action.config.schedule_enabled,
|
||||
schedule_format: action.config.schedule_format,
|
||||
schedule: action.config.schedule,
|
||||
schedule_timezone: action.config.schedule_timezone,
|
||||
tags: action.tags,
|
||||
last_run_at,
|
||||
next_scheduled_run,
|
||||
schedule_error,
|
||||
}
|
||||
});
|
||||
let procedures = procedures.into_iter().map(async |procedure| {
|
||||
let (next_scheduled_run, schedule_error) =
|
||||
get_schedule_item_info(&ResourceTarget::Procedure(
|
||||
procedure.id.clone(),
|
||||
));
|
||||
let last_run_at = get_last_run_at::<Procedure>(&procedure.id)
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
Schedule {
|
||||
target: ResourceTarget::Procedure(procedure.id),
|
||||
name: procedure.name,
|
||||
enabled: procedure.config.schedule_enabled,
|
||||
schedule_format: procedure.config.schedule_format,
|
||||
schedule: procedure.config.schedule,
|
||||
schedule_timezone: procedure.config.schedule_timezone,
|
||||
tags: procedure.tags,
|
||||
last_run_at,
|
||||
next_scheduled_run,
|
||||
schedule_error,
|
||||
}
|
||||
});
|
||||
let (actions, procedures) =
|
||||
tokio::join!(join_all(actions), join_all(procedures));
|
||||
|
||||
Ok(
|
||||
actions
|
||||
.into_iter()
|
||||
.chain(procedures)
|
||||
.filter(|s| !s.schedule.is_empty())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ impl Resolve<ReadArgs> for InspectStackContainer {
|
||||
.curr
|
||||
.services;
|
||||
let Some(name) = services
|
||||
.into_iter()
|
||||
.iter()
|
||||
.find(|s| s.service == service)
|
||||
.and_then(|s| s.container.as_ref().map(|c| c.name.clone()))
|
||||
else {
|
||||
|
||||
@@ -209,7 +209,7 @@ impl Resolve<WriteArgs> for RenameDeployment {
|
||||
|
||||
let name = to_docker_compatible_name(&self.name);
|
||||
|
||||
let container_state = get_deployment_state(&deployment).await?;
|
||||
let container_state = get_deployment_state(&deployment.id).await?;
|
||||
|
||||
if container_state == DeploymentState::Unknown {
|
||||
return Err(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::time::Duration;
|
||||
use std::{fmt::Write, time::Duration};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use indexmap::IndexSet;
|
||||
@@ -194,3 +194,21 @@ pub fn flatten_document(doc: Document) -> Document {
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
pub fn repo_link(
|
||||
provider: &str,
|
||||
repo: &str,
|
||||
branch: &str,
|
||||
https: bool,
|
||||
) -> String {
|
||||
let mut res = format!(
|
||||
"http{}://{provider}/{repo}",
|
||||
if https { "s" } else { "" }
|
||||
);
|
||||
// Each provider uses a different link format to get to branches.
|
||||
// At least can support github for branch aware link.
|
||||
if provider == "github.com" {
|
||||
let _ = write!(&mut res, "/tree/{branch}");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ use anyhow::{Context, anyhow};
|
||||
use async_timing_util::{ONE_MIN_MS, unix_timestamp_ms};
|
||||
use komodo_client::entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant,
|
||||
action::Action,
|
||||
action::{Action, ActionState},
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::Builder,
|
||||
deployment::{Deployment, DeploymentState},
|
||||
docker::container::{ContainerListItem, ContainerStateStatusEnum},
|
||||
permission::{PermissionLevel, PermissionLevelAndSpecifics},
|
||||
procedure::Procedure,
|
||||
procedure::{Procedure, ProcedureState},
|
||||
repo::Repo,
|
||||
server::{Server, ServerState},
|
||||
stack::{Stack, StackServiceNames, StackState},
|
||||
@@ -40,9 +40,13 @@ use tokio::sync::Mutex;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
permission::get_user_permission_on_resource,
|
||||
resource,
|
||||
resource::{self, KomodoResource},
|
||||
stack::compose_container_match_regex,
|
||||
state::{db_client, deployment_status_cache, stack_status_cache},
|
||||
state::{
|
||||
action_state_cache, action_states, db_client,
|
||||
deployment_status_cache, procedure_state_cache,
|
||||
stack_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
use super::periphery_client;
|
||||
@@ -88,10 +92,22 @@ pub async fn get_server_state(server: &Server) -> ServerState {
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_deployment_state(
|
||||
deployment: &Deployment,
|
||||
id: &String,
|
||||
) -> anyhow::Result<DeploymentState> {
|
||||
if action_states()
|
||||
.deployment
|
||||
.get(id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.deploying))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return Ok(DeploymentState::Deploying);
|
||||
}
|
||||
let state = deployment_status_cache()
|
||||
.get(&deployment.id)
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.curr
|
||||
@@ -424,3 +440,56 @@ pub async fn get_system_info(
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Get last time procedure / action was run using Update query.
|
||||
/// Ignored whether run was successful.
|
||||
pub async fn get_last_run_at<R: KomodoResource>(
|
||||
id: &String,
|
||||
) -> anyhow::Result<Option<i64>> {
|
||||
let resource_type = R::resource_type();
|
||||
let res = db_client()
|
||||
.updates
|
||||
.find_one(doc! {
|
||||
"target.type": resource_type.as_ref(),
|
||||
"target.id": id,
|
||||
"operation": format!("Run{resource_type}"),
|
||||
"status": "Complete"
|
||||
})
|
||||
.sort(doc! { "start_ts": -1 })
|
||||
.await
|
||||
.context("Failed to query updates collection for last run time")?
|
||||
.map(|u| u.start_ts);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn get_action_state(id: &String) -> ActionState {
|
||||
if action_states()
|
||||
.action
|
||||
.get(id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.running))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return ActionState::Running;
|
||||
}
|
||||
action_state_cache().get(id).await.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub async fn get_procedure_state(id: &String) -> ProcedureState {
|
||||
if action_states()
|
||||
.procedure
|
||||
.get(id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.running))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return ProcedureState::Running;
|
||||
}
|
||||
procedure_state_cache().get(id).await.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use std::{collections::HashMap, path::PathBuf, str::FromStr};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use derive_variants::ExtractVariant;
|
||||
@@ -32,7 +37,8 @@ pub async fn alert_servers(
|
||||
) {
|
||||
let server_statuses = server_status_cache().get_list().await;
|
||||
|
||||
let (alerts, disk_alerts) = match get_open_alerts().await {
|
||||
let (open_alerts, open_disk_alerts) = match get_open_alerts().await
|
||||
{
|
||||
Ok(alerts) => alerts,
|
||||
Err(e) => {
|
||||
error!("{e:#}");
|
||||
@@ -44,12 +50,14 @@ pub async fn alert_servers(
|
||||
let mut alerts_to_update = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alert_ids_to_close = Vec::<(Alert, SendAlerts)>::new();
|
||||
|
||||
let buffer = alert_buffer();
|
||||
|
||||
for server_status in server_statuses {
|
||||
let Some(server) = servers.remove(&server_status.id) else {
|
||||
continue;
|
||||
};
|
||||
let server_alerts =
|
||||
alerts.get(&ResourceTarget::Server(server_status.id.clone()));
|
||||
let server_alerts = open_alerts
|
||||
.get(&ResourceTarget::Server(server_status.id.clone()));
|
||||
|
||||
// ===================
|
||||
// SERVER HEALTH
|
||||
@@ -59,23 +67,28 @@ pub async fn alert_servers(
|
||||
});
|
||||
match (server_status.state, health_alert) {
|
||||
(ServerState::NotOk, None) => {
|
||||
// open unreachable alert
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: SeverityLevel::Critical,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerUnreachable {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
err: server_status.err.clone(),
|
||||
},
|
||||
};
|
||||
alerts_to_open
|
||||
.push((alert, server.config.send_unreachable_alerts))
|
||||
if buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerUnreachable,
|
||||
) {
|
||||
// open unreachable alert
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: SeverityLevel::Critical,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerUnreachable {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
err: server_status.err.clone(),
|
||||
},
|
||||
};
|
||||
alerts_to_open
|
||||
.push((alert, server.config.send_unreachable_alerts))
|
||||
}
|
||||
}
|
||||
(ServerState::NotOk, Some(alert)) => {
|
||||
// update alert err
|
||||
@@ -109,7 +122,11 @@ pub async fn alert_servers(
|
||||
server.config.send_unreachable_alerts,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
(ServerState::Ok | ServerState::Disabled, None) => buffer
|
||||
.reset(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerUnreachable,
|
||||
),
|
||||
}
|
||||
|
||||
let Some(health) = &server_status.health else {
|
||||
@@ -127,25 +144,30 @@ pub async fn alert_servers(
|
||||
{
|
||||
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
|
||||
// open alert
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.cpu.level,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerCpu {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
percentage: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.cpu_perc as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
};
|
||||
alerts_to_open.push((alert, server.config.send_cpu_alerts));
|
||||
if buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerCpu,
|
||||
) {
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.cpu.level,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerCpu {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
percentage: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.cpu_perc as f64)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
};
|
||||
alerts_to_open.push((alert, server.config.send_cpu_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
@@ -184,7 +206,9 @@ pub async fn alert_servers(
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_cpu_alerts))
|
||||
}
|
||||
_ => {}
|
||||
(SeverityLevel::Ok, Some(_), false) => {}
|
||||
(SeverityLevel::Ok, None, _) => buffer
|
||||
.reset(server_status.id.clone(), AlertDataVariant::ServerCpu),
|
||||
}
|
||||
|
||||
// ===================
|
||||
@@ -198,30 +222,35 @@ pub async fn alert_servers(
|
||||
{
|
||||
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
|
||||
// open alert
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.mem.level,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerMem {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
total_gb: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.mem_total_gb)
|
||||
.unwrap_or(0.0),
|
||||
used_gb: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.mem_used_gb)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
};
|
||||
alerts_to_open.push((alert, server.config.send_mem_alerts));
|
||||
if buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerMem,
|
||||
) {
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.mem.level,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerMem {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
total_gb: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.mem_total_gb)
|
||||
.unwrap_or(0.0),
|
||||
used_gb: server_status
|
||||
.stats
|
||||
.as_ref()
|
||||
.map(|s| s.mem_used_gb)
|
||||
.unwrap_or(0.0),
|
||||
},
|
||||
};
|
||||
alerts_to_open.push((alert, server.config.send_mem_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
@@ -270,14 +299,16 @@ pub async fn alert_servers(
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_mem_alerts))
|
||||
}
|
||||
_ => {}
|
||||
(SeverityLevel::Ok, Some(_), false) => {}
|
||||
(SeverityLevel::Ok, None, _) => buffer
|
||||
.reset(server_status.id.clone(), AlertDataVariant::ServerMem),
|
||||
}
|
||||
|
||||
// ===================
|
||||
// SERVER DISK
|
||||
// ===================
|
||||
|
||||
let server_disk_alerts = disk_alerts
|
||||
let server_disk_alerts = open_disk_alerts
|
||||
.get(&ResourceTarget::Server(server_status.id.clone()));
|
||||
|
||||
for (path, health) in &health.disks {
|
||||
@@ -291,27 +322,38 @@ pub async fn alert_servers(
|
||||
None,
|
||||
_,
|
||||
) => {
|
||||
let disk = server_status.stats.as_ref().and_then(|stats| {
|
||||
stats.disks.iter().find(|disk| disk.mount == *path)
|
||||
});
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.level,
|
||||
target: ResourceTarget::Server(server_status.id.clone()),
|
||||
data: AlertData::ServerDisk {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
path: path.to_owned(),
|
||||
total_gb: disk.map(|d| d.total_gb).unwrap_or_default(),
|
||||
used_gb: disk.map(|d| d.used_gb).unwrap_or_default(),
|
||||
},
|
||||
};
|
||||
alerts_to_open
|
||||
.push((alert, server.config.send_disk_alerts));
|
||||
// open alert
|
||||
if buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerDisk,
|
||||
) {
|
||||
let disk =
|
||||
server_status.stats.as_ref().and_then(|stats| {
|
||||
stats.disks.iter().find(|disk| disk.mount == *path)
|
||||
});
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: false,
|
||||
resolved_ts: None,
|
||||
level: health.level,
|
||||
target: ResourceTarget::Server(
|
||||
server_status.id.clone(),
|
||||
),
|
||||
data: AlertData::ServerDisk {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
path: path.to_owned(),
|
||||
total_gb: disk
|
||||
.map(|d| d.total_gb)
|
||||
.unwrap_or_default(),
|
||||
used_gb: disk.map(|d| d.used_gb).unwrap_or_default(),
|
||||
},
|
||||
};
|
||||
alerts_to_open
|
||||
.push((alert, server.config.send_disk_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
@@ -354,7 +396,11 @@ pub async fn alert_servers(
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_disk_alerts))
|
||||
}
|
||||
_ => {}
|
||||
(SeverityLevel::Ok, Some(_), false) => {}
|
||||
(SeverityLevel::Ok, None, _) => buffer.reset(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerDisk,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,14 +418,14 @@ pub async fn alert_servers(
|
||||
}
|
||||
|
||||
tokio::join!(
|
||||
open_alerts(&alerts_to_open),
|
||||
open_new_alerts(&alerts_to_open),
|
||||
update_alerts(&alerts_to_update),
|
||||
resolve_alerts(&alert_ids_to_close),
|
||||
);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn open_alerts(alerts: &[(Alert, SendAlerts)]) {
|
||||
async fn open_new_alerts(alerts: &[(Alert, SendAlerts)]) {
|
||||
if alerts.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -560,3 +606,40 @@ async fn get_open_alerts()
|
||||
|
||||
Ok((map, disk_map))
|
||||
}
|
||||
|
||||
/// Alerts should only be opened after
|
||||
/// 2 *consecutive* alerting conditions.
|
||||
/// This reduces alerting noise.
|
||||
#[derive(Default)]
|
||||
struct AlertBuffer {
|
||||
/// (ServerId, AlertType) -> should_open.
|
||||
buffer: Mutex<HashMap<(String, AlertDataVariant), bool>>,
|
||||
}
|
||||
|
||||
impl AlertBuffer {
|
||||
fn reset(&self, server_id: String, variant: AlertDataVariant) {
|
||||
let mut lock = self.buffer.lock().unwrap();
|
||||
lock.remove(&(server_id, variant));
|
||||
}
|
||||
|
||||
fn ready_to_open(
|
||||
&self,
|
||||
server_id: String,
|
||||
variant: AlertDataVariant,
|
||||
) -> bool {
|
||||
let mut lock = self.buffer.lock().unwrap();
|
||||
let ready = lock.entry((server_id, variant)).or_default();
|
||||
if *ready {
|
||||
*ready = false;
|
||||
true
|
||||
} else {
|
||||
*ready = true;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alert_buffer() -> &'static AlertBuffer {
|
||||
static ALERT_BUFFER: OnceLock<AlertBuffer> = OnceLock::new();
|
||||
ALERT_BUFFER.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
@@ -267,8 +267,9 @@ pub async fn update_cache_for_server(server: &Server) {
|
||||
path: optional_string(&repo.config.path),
|
||||
})
|
||||
.await
|
||||
.map(|r| (r.hash, r.message))
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|c| (c.hash, c.message))
|
||||
.unzip();
|
||||
status_cache
|
||||
.insert(
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use komodo_client::entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant,
|
||||
NoData, Operation, ResourceTarget, ResourceTargetVariant,
|
||||
action::{
|
||||
Action, ActionConfig, ActionConfigDiff, ActionInfo,
|
||||
ActionListItem, ActionListItemInfo, ActionQuerySpecifics,
|
||||
ActionState, PartialActionConfig,
|
||||
Action, ActionConfig, ActionConfigDiff, ActionListItem,
|
||||
ActionListItemInfo, ActionQuerySpecifics, ActionState,
|
||||
PartialActionConfig,
|
||||
},
|
||||
resource::Resource,
|
||||
update::Update,
|
||||
@@ -18,6 +18,7 @@ use mungos::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_action_state, get_last_run_at},
|
||||
schedule::{
|
||||
cancel_schedule, get_schedule_item_info, update_schedule,
|
||||
},
|
||||
@@ -28,7 +29,7 @@ impl super::KomodoResource for Action {
|
||||
type Config = ActionConfig;
|
||||
type PartialConfig = PartialActionConfig;
|
||||
type ConfigDiff = ActionConfigDiff;
|
||||
type Info = ActionInfo;
|
||||
type Info = NoData;
|
||||
type ListItem = ActionListItem;
|
||||
type QuerySpecifics = ActionQuerySpecifics;
|
||||
|
||||
@@ -48,7 +49,10 @@ impl super::KomodoResource for Action {
|
||||
async fn to_list_item(
|
||||
action: Resource<Self::Config, Self::Info>,
|
||||
) -> Self::ListItem {
|
||||
let state = get_action_state(&action.id).await;
|
||||
let (state, last_run_at) = tokio::join!(
|
||||
get_action_state(&action.id),
|
||||
get_last_run_at::<Action>(&action.id)
|
||||
);
|
||||
let (next_scheduled_run, schedule_error) = get_schedule_item_info(
|
||||
&ResourceTarget::Action(action.id.clone()),
|
||||
);
|
||||
@@ -59,7 +63,7 @@ impl super::KomodoResource for Action {
|
||||
resource_type: ResourceTargetVariant::Action,
|
||||
info: ActionListItemInfo {
|
||||
state,
|
||||
last_run_at: action.info.last_run_at,
|
||||
last_run_at: last_run_at.unwrap_or(None),
|
||||
next_scheduled_run,
|
||||
schedule_error,
|
||||
},
|
||||
@@ -181,22 +185,6 @@ pub async fn refresh_action_state_cache() {
|
||||
});
|
||||
}
|
||||
|
||||
async fn get_action_state(id: &String) -> ActionState {
|
||||
if action_states()
|
||||
.action
|
||||
.get(id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.running))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return ActionState::Running;
|
||||
}
|
||||
action_state_cache().get(id).await.unwrap_or_default()
|
||||
}
|
||||
|
||||
async fn get_action_state_from_db(id: &str) -> ActionState {
|
||||
async {
|
||||
let state = db_client()
|
||||
|
||||
@@ -29,7 +29,9 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
config::core_config,
|
||||
helpers::{empty_or_only_spaces, query::get_latest_update},
|
||||
helpers::{
|
||||
empty_or_only_spaces, query::get_latest_update, repo_link,
|
||||
},
|
||||
state::{action_states, build_state_cache, db_client},
|
||||
};
|
||||
|
||||
@@ -72,9 +74,15 @@ impl super::KomodoResource for Build {
|
||||
version: build.config.version,
|
||||
builder_id: build.config.builder_id,
|
||||
files_on_host: build.config.files_on_host,
|
||||
git_provider: optional_string(build.config.git_provider),
|
||||
repo: optional_string(build.config.repo),
|
||||
branch: optional_string(build.config.branch),
|
||||
repo_link: repo_link(
|
||||
&build.config.git_provider,
|
||||
&build.config.repo,
|
||||
&build.config.branch,
|
||||
build.config.git_https,
|
||||
),
|
||||
git_provider: build.config.git_provider,
|
||||
repo: build.config.repo,
|
||||
branch: build.config.branch,
|
||||
image_registry_domain: optional_string(
|
||||
build.config.image_registry.domain,
|
||||
),
|
||||
|
||||
@@ -78,6 +78,20 @@ impl super::KomodoResource for Deployment {
|
||||
deployment: Resource<Self::Config, Self::Info>,
|
||||
) -> Self::ListItem {
|
||||
let status = deployment_status_cache().get(&deployment.id).await;
|
||||
let state = if action_states()
|
||||
.deployment
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.deploying))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
DeploymentState::Deploying
|
||||
} else {
|
||||
status.as_ref().map(|s| s.curr.state).unwrap_or_default()
|
||||
};
|
||||
let (build_image, build_id) = match deployment.config.image {
|
||||
DeploymentImage::Build { build_id, version } => {
|
||||
let (build_name, build_id, build_version) =
|
||||
@@ -117,10 +131,7 @@ impl super::KomodoResource for Deployment {
|
||||
tags: deployment.tags,
|
||||
resource_type: ResourceTargetVariant::Deployment,
|
||||
info: DeploymentListItemInfo {
|
||||
state: status
|
||||
.as_ref()
|
||||
.map(|s| s.curr.state)
|
||||
.unwrap_or_default(),
|
||||
state,
|
||||
status: status.as_ref().and_then(|s| {
|
||||
s.curr.container.as_ref().and_then(|c| c.status.to_owned())
|
||||
}),
|
||||
@@ -217,9 +228,9 @@ impl super::KomodoResource for Deployment {
|
||||
deployment: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
let state = get_deployment_state(deployment)
|
||||
let state = get_deployment_state(&deployment.id)
|
||||
.await
|
||||
.context("failed to get container state")?;
|
||||
.context("Failed to get deployment state")?;
|
||||
if matches!(
|
||||
state,
|
||||
DeploymentState::NotDeployed | DeploymentState::Unknown
|
||||
@@ -235,7 +246,7 @@ impl super::KomodoResource for Deployment {
|
||||
Ok(server) => server,
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"remove container",
|
||||
"Remove Container",
|
||||
format_serror(
|
||||
&e.context(format!(
|
||||
"failed to retrieve server at {} from db.",
|
||||
@@ -250,8 +261,8 @@ impl super::KomodoResource for Deployment {
|
||||
if !server.config.enabled {
|
||||
// Don't need to
|
||||
update.push_simple_log(
|
||||
"remove container",
|
||||
"skipping container removal, server is disabled.",
|
||||
"Remove Container",
|
||||
"Skipping container removal, server is disabled.",
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -261,9 +272,9 @@ impl super::KomodoResource for Deployment {
|
||||
// This case won't ever happen, as periphery_client only fallible if the server is disabled.
|
||||
// Leaving it for completeness sake
|
||||
update.push_error_log(
|
||||
"remove container",
|
||||
"Remove Container",
|
||||
format_serror(
|
||||
&e.context("failed to get periphery client").into(),
|
||||
&e.context("Failed to get periphery client").into(),
|
||||
),
|
||||
);
|
||||
return Ok(());
|
||||
@@ -279,9 +290,9 @@ impl super::KomodoResource for Deployment {
|
||||
{
|
||||
Ok(log) => update.logs.push(log),
|
||||
Err(e) => update.push_error_log(
|
||||
"remove container",
|
||||
"Remove Container",
|
||||
format_serror(
|
||||
&e.context("failed to remove container").into(),
|
||||
&e.context("Failed to remove container").into(),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ use mungos::{
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::query::{get_last_run_at, get_procedure_state},
|
||||
schedule::{
|
||||
cancel_schedule, get_schedule_item_info, update_schedule,
|
||||
},
|
||||
@@ -61,7 +62,10 @@ impl super::KomodoResource for Procedure {
|
||||
async fn to_list_item(
|
||||
procedure: Resource<Self::Config, Self::Info>,
|
||||
) -> Self::ListItem {
|
||||
let state = get_procedure_state(&procedure.id).await;
|
||||
let (state, last_run_at) = tokio::join!(
|
||||
get_procedure_state(&procedure.id),
|
||||
get_last_run_at::<Procedure>(&procedure.id)
|
||||
);
|
||||
let (next_scheduled_run, schedule_error) = get_schedule_item_info(
|
||||
&ResourceTarget::Procedure(procedure.id.clone()),
|
||||
);
|
||||
@@ -73,6 +77,7 @@ impl super::KomodoResource for Procedure {
|
||||
info: ProcedureListItemInfo {
|
||||
stages: procedure.config.stages.len() as i64,
|
||||
state,
|
||||
last_run_at: last_run_at.unwrap_or(None),
|
||||
next_scheduled_run,
|
||||
schedule_error,
|
||||
},
|
||||
@@ -754,22 +759,6 @@ pub async fn refresh_procedure_state_cache() {
|
||||
});
|
||||
}
|
||||
|
||||
async fn get_procedure_state(id: &String) -> ProcedureState {
|
||||
if action_states()
|
||||
.procedure
|
||||
.get(id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.running))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return ProcedureState::Running;
|
||||
}
|
||||
procedure_state_cache().get(id).await.unwrap_or_default()
|
||||
}
|
||||
|
||||
async fn get_procedure_state_from_db(id: &str) -> ProcedureState {
|
||||
async {
|
||||
let state = db_client()
|
||||
|
||||
@@ -24,7 +24,7 @@ use periphery_client::api::git::DeleteRepo;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::periphery_client,
|
||||
helpers::{periphery_client, repo_link},
|
||||
state::{
|
||||
action_states, db_client, repo_state_cache, repo_status_cache,
|
||||
},
|
||||
@@ -73,6 +73,12 @@ impl super::KomodoResource for Repo {
|
||||
builder_id: repo.config.builder_id,
|
||||
last_pulled_at: repo.info.last_pulled_at,
|
||||
last_built_at: repo.info.last_built_at,
|
||||
repo_link: repo_link(
|
||||
&repo.config.git_provider,
|
||||
&repo.config.repo,
|
||||
&repo.config.branch,
|
||||
repo.config.git_https,
|
||||
),
|
||||
git_provider: repo.config.git_provider,
|
||||
repo: repo.config.repo,
|
||||
branch: repo.config.branch,
|
||||
|
||||
@@ -25,7 +25,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
config::core_config,
|
||||
helpers::{periphery_client, query::get_stack_state},
|
||||
helpers::{periphery_client, query::get_stack_state, repo_link},
|
||||
monitor::update_cache_for_server,
|
||||
state::{
|
||||
action_states, db_client, server_status_cache, stack_status_cache,
|
||||
@@ -79,8 +79,20 @@ impl super::KomodoResource for Stack {
|
||||
stack: Resource<Self::Config, Self::Info>,
|
||||
) -> Self::ListItem {
|
||||
let status = stack_status_cache().get(&stack.id).await;
|
||||
let state =
|
||||
status.as_ref().map(|s| s.curr.state).unwrap_or_default();
|
||||
let state = if action_states()
|
||||
.stack
|
||||
.get(&stack.id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.deploying))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
StackState::Deploying
|
||||
} else {
|
||||
status.as_ref().map(|s| s.curr.state).unwrap_or_default()
|
||||
};
|
||||
let project_name = stack.project_name(false);
|
||||
let services = status
|
||||
.as_ref()
|
||||
@@ -139,6 +151,12 @@ impl super::KomodoResource for Stack {
|
||||
server_id: stack.config.server_id,
|
||||
missing_files: stack.info.missing_files,
|
||||
files_on_host: stack.config.files_on_host,
|
||||
repo_link: repo_link(
|
||||
&stack.config.git_provider,
|
||||
&stack.config.repo,
|
||||
&stack.config.branch,
|
||||
stack.config.git_https,
|
||||
),
|
||||
git_provider: stack.config.git_provider,
|
||||
repo: stack.config.repo,
|
||||
branch: stack.config.branch,
|
||||
|
||||
@@ -22,6 +22,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
helpers::repo_link,
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
@@ -61,6 +62,12 @@ impl super::KomodoResource for ResourceSync {
|
||||
file_contents: !resource_sync.config.file_contents.is_empty(),
|
||||
files_on_host: resource_sync.config.files_on_host,
|
||||
managed: resource_sync.config.managed,
|
||||
repo_link: repo_link(
|
||||
&resource_sync.config.git_provider,
|
||||
&resource_sync.config.repo,
|
||||
&resource_sync.config.branch,
|
||||
resource_sync.config.git_https,
|
||||
),
|
||||
git_provider: resource_sync.config.git_provider,
|
||||
repo: resource_sync.config.repo,
|
||||
branch: resource_sync.config.branch,
|
||||
|
||||
@@ -555,6 +555,7 @@ fn build_cache_for_stack<'a>(
|
||||
// Here can diff the changes, to see if they merit a redeploy.
|
||||
|
||||
// See if any remote contents don't match deployed contents
|
||||
#[allow(clippy::single_match)]
|
||||
match (
|
||||
&original.info.deployed_contents,
|
||||
&original.info.remote_contents,
|
||||
|
||||
@@ -799,7 +799,7 @@ async fn expand_user_group_permissions(
|
||||
if id.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let matcher = Matcher::new(&id)?;
|
||||
let matcher = Matcher::new(id)?;
|
||||
match variant {
|
||||
ResourceTargetVariant::Build => {
|
||||
let permissions = all_resources
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use anyhow::Context;
|
||||
use formatting::format_serror;
|
||||
use git::GitRes;
|
||||
use komodo_client::entities::{CloneArgs, LatestCommit, update::Log};
|
||||
@@ -17,21 +17,16 @@ impl Resolve<super::Args> for GetLatestCommit {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &super::Args,
|
||||
) -> serror::Result<LatestCommit> {
|
||||
) -> serror::Result<Option<LatestCommit>> {
|
||||
let repo_path = match self.path {
|
||||
Some(p) => PathBuf::from(p),
|
||||
None => periphery_config().repo_dir().join(self.name),
|
||||
};
|
||||
if !repo_path.is_dir() {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Repo path {} is not directory. is it cloned?",
|
||||
repo_path.display()
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
// Make sure its a repo, or return null to avoid log spam
|
||||
if !repo_path.is_dir() || !repo_path.join(".git").is_dir() {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(git::get_commit_hash_info(&repo_path).await?)
|
||||
Ok(Some(git::get_commit_hash_info(&repo_path).await?))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +172,35 @@ pub async fn compose_up(
|
||||
output
|
||||
});
|
||||
|
||||
// Pre deploy command
|
||||
let pre_deploy_path =
|
||||
run_directory.join(&stack.config.pre_deploy.path);
|
||||
if let Some(log) = if stack.config.skip_secret_interp {
|
||||
run_komodo_command_multiline(
|
||||
"Pre Deploy",
|
||||
pre_deploy_path.as_ref(),
|
||||
&stack.config.pre_deploy.command,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
run_komodo_command_with_interpolation(
|
||||
"Pre Deploy",
|
||||
pre_deploy_path.as_ref(),
|
||||
&stack.config.pre_deploy.command,
|
||||
true,
|
||||
&periphery_config().secrets,
|
||||
&replacers,
|
||||
)
|
||||
.await
|
||||
} {
|
||||
res.logs.push(log);
|
||||
}
|
||||
if !all_logs_success(&res.logs) {
|
||||
return Err(anyhow!(
|
||||
"Failed at running pre_deploy command, stopping the run."
|
||||
));
|
||||
}
|
||||
|
||||
// Uses 'docker compose config' command to extract services (including image)
|
||||
// after performing interpolation
|
||||
{
|
||||
@@ -291,35 +320,6 @@ pub async fn compose_up(
|
||||
}
|
||||
}
|
||||
|
||||
// Pre deploy command
|
||||
let pre_deploy_path =
|
||||
run_directory.join(&stack.config.pre_deploy.path);
|
||||
if let Some(log) = if stack.config.skip_secret_interp {
|
||||
run_komodo_command_multiline(
|
||||
"Pre Deploy",
|
||||
pre_deploy_path.as_ref(),
|
||||
&stack.config.pre_deploy.command,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
run_komodo_command_with_interpolation(
|
||||
"Pre Deploy",
|
||||
pre_deploy_path.as_ref(),
|
||||
&stack.config.pre_deploy.command,
|
||||
true,
|
||||
&periphery_config().secrets,
|
||||
&replacers,
|
||||
)
|
||||
.await
|
||||
} {
|
||||
res.logs.push(log);
|
||||
}
|
||||
if !all_logs_success(&res.logs) {
|
||||
return Err(anyhow!(
|
||||
"Failed at running pre_deploy command, stopping the run."
|
||||
));
|
||||
}
|
||||
|
||||
if stack.config.destroy_before_deploy
|
||||
// Also check if project name changed, which also requires taking down.
|
||||
|| last_project_name != project_name
|
||||
|
||||
@@ -160,14 +160,14 @@ pub enum BatchExecutionResponseItem {
|
||||
Err(BatchExecutionResponseItemErr),
|
||||
}
|
||||
|
||||
impl From<Result<Update, BatchExecutionResponseItemErr>>
|
||||
impl From<Result<Box<Update>, BatchExecutionResponseItemErr>>
|
||||
for BatchExecutionResponseItem
|
||||
{
|
||||
fn from(
|
||||
value: Result<Update, BatchExecutionResponseItemErr>,
|
||||
value: Result<Box<Update>, BatchExecutionResponseItemErr>,
|
||||
) -> Self {
|
||||
match value {
|
||||
Ok(update) => Self::Ok(update),
|
||||
Ok(update) => Self::Ok(*update),
|
||||
Err(e) => Self::Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ mod permission;
|
||||
mod procedure;
|
||||
mod provider;
|
||||
mod repo;
|
||||
mod schedule;
|
||||
mod server;
|
||||
mod stack;
|
||||
mod sync;
|
||||
@@ -33,6 +34,7 @@ pub use permission::*;
|
||||
pub use procedure::*;
|
||||
pub use provider::*;
|
||||
pub use repo::*;
|
||||
pub use schedule::*;
|
||||
pub use server::*;
|
||||
pub use stack::*;
|
||||
pub use sync::*;
|
||||
|
||||
32
client/core/rs/src/api/read/schedule.rs
Normal file
32
client/core/rs/src/api/read/schedule.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use derive_empty_traits::EmptyTraits;
|
||||
use resolver_api::Resolve;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{
|
||||
deserializers::string_list_deserializer,
|
||||
entities::{resource::TagBehavior, schedule::Schedule},
|
||||
};
|
||||
|
||||
use super::KomodoReadRequest;
|
||||
|
||||
/// List configured schedules.
|
||||
/// Response: [ListSchedulesResponse].
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoReadRequest)]
|
||||
#[response(ListSchedulesResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct ListSchedules {
|
||||
/// Pass Vec of tag ids or tag names
|
||||
#[serde(default, deserialize_with = "string_list_deserializer")]
|
||||
pub tags: Vec<String>,
|
||||
/// 'All' or 'Any'
|
||||
#[serde(default)]
|
||||
pub tag_behavior: TagBehavior,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type ListSchedulesResponse = Vec<Schedule>;
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
deserializers::{
|
||||
file_contents_deserializer, option_file_contents_deserializer,
|
||||
},
|
||||
entities::I64,
|
||||
entities::{I64, NoData},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -24,11 +24,11 @@ pub type ActionListItem = ResourceListItem<ActionListItemInfo>;
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct ActionListItemInfo {
|
||||
/// Action last run timestamp in ms.
|
||||
pub last_run_at: I64,
|
||||
/// Whether last action run successful
|
||||
pub state: ActionState,
|
||||
/// If the procedure has schedule enabled, this is the
|
||||
/// Action last successful run timestamp in ms.
|
||||
pub last_run_at: Option<I64>,
|
||||
/// If the action has schedule enabled, this is the
|
||||
/// next scheduled run time in unix ms.
|
||||
pub next_scheduled_run: Option<I64>,
|
||||
/// If there is an error parsing schedule expression,
|
||||
@@ -53,15 +53,7 @@ pub enum ActionState {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type Action = Resource<ActionConfig, ActionInfo>;
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct ActionInfo {
|
||||
/// When action was last run
|
||||
#[serde(default)]
|
||||
pub last_run_at: I64,
|
||||
}
|
||||
pub type Action = Resource<ActionConfig, NoData>;
|
||||
|
||||
#[typeshare(serialized_as = "Partial<ActionConfig>")]
|
||||
pub type _PartialActionConfig = PartialActionConfig;
|
||||
|
||||
@@ -45,11 +45,15 @@ pub struct BuildListItemInfo {
|
||||
pub files_on_host: bool,
|
||||
|
||||
/// The git provider domain
|
||||
pub git_provider: Option<String>,
|
||||
pub git_provider: String,
|
||||
/// The repo used as the source of the build
|
||||
pub repo: Option<String>,
|
||||
pub repo: String,
|
||||
/// The branch of the repo
|
||||
pub branch: Option<String>,
|
||||
pub branch: String,
|
||||
/// Full link to the repo.
|
||||
pub repo_link: String,
|
||||
|
||||
|
||||
/// Latest built short commit hash, or null.
|
||||
pub built_hash: Option<String>,
|
||||
/// Latest short commit hash, or null. Only for repo based stacks
|
||||
|
||||
@@ -27,8 +27,9 @@ pub type _PartialBuilderConfig = PartialBuilderConfig;
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct BuilderListItemInfo {
|
||||
/// 'Server' or 'Aws'
|
||||
/// 'Url', 'Server', or 'Aws'
|
||||
pub builder_type: String,
|
||||
/// If 'Url': null
|
||||
/// If 'Server': the server id
|
||||
/// If 'Aws': the instance type (eg. c5.xlarge)
|
||||
pub instance_type: Option<String>,
|
||||
|
||||
@@ -338,16 +338,27 @@ pub fn conversions_from_str(
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum DeploymentState {
|
||||
/// The deployment is currently re/deploying
|
||||
Deploying,
|
||||
/// Container is running
|
||||
Running,
|
||||
/// Container is created but not running
|
||||
Created,
|
||||
/// Container is in restart loop
|
||||
Restarting,
|
||||
/// Container is being removed
|
||||
Removing,
|
||||
/// Container is paused
|
||||
Paused,
|
||||
/// Container is exited
|
||||
Exited,
|
||||
/// Container is dead
|
||||
Dead,
|
||||
/// The deployment is not deployed (no matching container)
|
||||
NotDeployed,
|
||||
/// Server not reachable for status
|
||||
#[default]
|
||||
Unknown,
|
||||
NotDeployed,
|
||||
Created,
|
||||
Restarting,
|
||||
Running,
|
||||
Removing,
|
||||
Paused,
|
||||
Exited,
|
||||
Dead,
|
||||
}
|
||||
|
||||
impl From<ContainerStateStatusEnum> for DeploymentState {
|
||||
|
||||
@@ -52,6 +52,8 @@ pub mod provider;
|
||||
pub mod repo;
|
||||
/// Subtypes of [Resource][resource::Resource].
|
||||
pub mod resource;
|
||||
/// Subtypes of [Schedule][schedule::Schedule]
|
||||
pub mod schedule;
|
||||
/// Subtypes of [Server][server::Server].
|
||||
pub mod server;
|
||||
/// Subtypes of [Stack][stack::Stack]
|
||||
|
||||
@@ -159,7 +159,7 @@ pub enum SpecificPermission {
|
||||
|
||||
impl SpecificPermission {
|
||||
fn all() -> IndexSet<SpecificPermission> {
|
||||
SpecificPermission::VARIANTS.into_iter().cloned().collect()
|
||||
SpecificPermission::VARIANTS.iter().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct ProcedureListItemInfo {
|
||||
pub stages: I64,
|
||||
/// Reflect whether last run successful / currently running.
|
||||
pub state: ProcedureState,
|
||||
/// Procedure last successful run timestamp in ms.
|
||||
pub last_run_at: Option<I64>,
|
||||
/// If the procedure has schedule enabled, this is the
|
||||
/// next scheduled run time in unix ms.
|
||||
pub next_scheduled_run: Option<I64>,
|
||||
|
||||
@@ -40,6 +40,8 @@ pub struct RepoListItemInfo {
|
||||
pub repo: String,
|
||||
/// The configured branch
|
||||
pub branch: String,
|
||||
/// Full link to the repo.
|
||||
pub repo_link: String,
|
||||
/// The repo state
|
||||
pub state: RepoState,
|
||||
/// If the repo is cloned, will be the cloned short commit hash.
|
||||
|
||||
@@ -106,6 +106,7 @@ pub struct ResourceQuery<T: Default> {
|
||||
/// Pass Vec of tag ids or tag names
|
||||
#[serde(default, deserialize_with = "string_list_deserializer")]
|
||||
pub tags: Vec<String>,
|
||||
/// 'All' or 'Any'
|
||||
#[serde(default)]
|
||||
pub tag_behavior: TagBehavior,
|
||||
#[serde(default)]
|
||||
|
||||
31
client/core/rs/src/entities/schedule.rs
Normal file
31
client/core/rs/src/entities/schedule.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::entities::{I64, ResourceTarget, ScheduleFormat};
|
||||
|
||||
/// A scheduled Action / Procedure run.
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct Schedule {
|
||||
/// Procedure or Alerter
|
||||
pub target: ResourceTarget,
|
||||
/// Readable name of the target resource
|
||||
pub name: String,
|
||||
/// The format of the schedule expression
|
||||
pub schedule_format: ScheduleFormat,
|
||||
/// The schedule for the run
|
||||
pub schedule: String,
|
||||
/// Whether the scheduled run is enabled
|
||||
pub enabled: bool,
|
||||
/// Custom schedule timezone if it exists
|
||||
pub schedule_timezone: String,
|
||||
/// Last run timestamp in ms.
|
||||
pub last_run_at: Option<I64>,
|
||||
/// Next scheduled run time in unix ms.
|
||||
pub next_scheduled_run: Option<I64>,
|
||||
/// If there is an error parsing schedule expression,
|
||||
/// it will be given here.
|
||||
pub schedule_error: Option<String>,
|
||||
/// Resource tags.
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
@@ -34,14 +34,11 @@ impl Stack {
|
||||
return project_name.clone();
|
||||
}
|
||||
}
|
||||
self
|
||||
.config
|
||||
.project_name
|
||||
.is_empty()
|
||||
.then(|| to_docker_compatible_name(&self.name))
|
||||
.unwrap_or_else(|| {
|
||||
to_docker_compatible_name(&self.config.project_name)
|
||||
})
|
||||
if self.config.project_name.is_empty() {
|
||||
to_docker_compatible_name(&self.name)
|
||||
} else {
|
||||
to_docker_compatible_name(&self.config.project_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_paths(&self) -> &[String] {
|
||||
@@ -77,6 +74,8 @@ pub struct StackListItemInfo {
|
||||
pub repo: String,
|
||||
/// The configured branch
|
||||
pub branch: String,
|
||||
/// Full link to the repo.
|
||||
pub repo_link: String,
|
||||
/// The stack state
|
||||
pub state: StackState,
|
||||
/// A string given by docker conveying the status of the stack.
|
||||
@@ -125,6 +124,8 @@ pub struct StackServiceWithUpdate {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum StackState {
|
||||
/// The stack is currently re/deploying
|
||||
Deploying,
|
||||
/// All containers are running.
|
||||
Running,
|
||||
/// All containers are paused
|
||||
@@ -143,7 +144,7 @@ pub enum StackState {
|
||||
Unhealthy,
|
||||
/// The stack is not deployed
|
||||
Down,
|
||||
/// Server not reachable
|
||||
/// Server not reachable for status
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ pub struct ResourceSyncListItemInfo {
|
||||
pub repo: String,
|
||||
/// The branch of the repo
|
||||
pub branch: String,
|
||||
/// Full link to the repo.
|
||||
pub repo_link: String,
|
||||
/// Short commit hash of last sync, or empty string
|
||||
pub last_sync_hash: Option<String>,
|
||||
/// Commit message of last sync, or empty string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "komodo_client",
|
||||
"version": "1.18.0",
|
||||
"version": "1.18.1",
|
||||
"description": "Komodo client package",
|
||||
"homepage": "https://komo.do",
|
||||
"main": "dist/lib.js",
|
||||
|
||||
@@ -50,6 +50,9 @@ export type ReadResponses = {
|
||||
ListActions: Types.ListActionsResponse;
|
||||
ListFullActions: Types.ListFullActionsResponse;
|
||||
|
||||
// ==== SCHEDULE ====
|
||||
ListSchedules: Types.ListSchedulesResponse;
|
||||
|
||||
// ==== SERVER ====
|
||||
GetServersSummary: Types.GetServersSummaryResponse;
|
||||
GetServer: Types.GetServerResponse;
|
||||
|
||||
@@ -116,12 +116,11 @@ export interface ActionConfig {
|
||||
file_contents?: string;
|
||||
}
|
||||
|
||||
export interface ActionInfo {
|
||||
/** When action was last run */
|
||||
last_run_at?: I64;
|
||||
/** Represents an empty json object: `{}` */
|
||||
export interface NoData {
|
||||
}
|
||||
|
||||
export type Action = Resource<ActionConfig, ActionInfo>;
|
||||
export type Action = Resource<ActionConfig, NoData>;
|
||||
|
||||
export interface ResourceListItem<Info> {
|
||||
/** The resource id */
|
||||
@@ -148,12 +147,12 @@ export enum ActionState {
|
||||
}
|
||||
|
||||
export interface ActionListItemInfo {
|
||||
/** Action last run timestamp in ms. */
|
||||
last_run_at: I64;
|
||||
/** Whether last action run successful */
|
||||
state: ActionState;
|
||||
/** Action last successful run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/**
|
||||
* If the procedure has schedule enabled, this is the
|
||||
* If the action has schedule enabled, this is the
|
||||
* next scheduled run time in unix ms.
|
||||
*/
|
||||
next_scheduled_run?: I64;
|
||||
@@ -178,6 +177,7 @@ export interface ResourceQuery<T> {
|
||||
names?: string[];
|
||||
/** Pass Vec of tag ids or tag names */
|
||||
tags?: string[];
|
||||
/** 'All' or 'Any' */
|
||||
tag_behavior?: TagBehavior;
|
||||
specific?: T;
|
||||
}
|
||||
@@ -270,12 +270,204 @@ export type BatchExecutionResponseItem =
|
||||
|
||||
export type BatchExecutionResponse = BatchExecutionResponseItem[];
|
||||
|
||||
export enum Operation {
|
||||
None = "None",
|
||||
CreateServer = "CreateServer",
|
||||
UpdateServer = "UpdateServer",
|
||||
DeleteServer = "DeleteServer",
|
||||
RenameServer = "RenameServer",
|
||||
StartContainer = "StartContainer",
|
||||
RestartContainer = "RestartContainer",
|
||||
PauseContainer = "PauseContainer",
|
||||
UnpauseContainer = "UnpauseContainer",
|
||||
StopContainer = "StopContainer",
|
||||
DestroyContainer = "DestroyContainer",
|
||||
StartAllContainers = "StartAllContainers",
|
||||
RestartAllContainers = "RestartAllContainers",
|
||||
PauseAllContainers = "PauseAllContainers",
|
||||
UnpauseAllContainers = "UnpauseAllContainers",
|
||||
StopAllContainers = "StopAllContainers",
|
||||
PruneContainers = "PruneContainers",
|
||||
CreateNetwork = "CreateNetwork",
|
||||
DeleteNetwork = "DeleteNetwork",
|
||||
PruneNetworks = "PruneNetworks",
|
||||
DeleteImage = "DeleteImage",
|
||||
PruneImages = "PruneImages",
|
||||
DeleteVolume = "DeleteVolume",
|
||||
PruneVolumes = "PruneVolumes",
|
||||
PruneDockerBuilders = "PruneDockerBuilders",
|
||||
PruneBuildx = "PruneBuildx",
|
||||
PruneSystem = "PruneSystem",
|
||||
CreateStack = "CreateStack",
|
||||
UpdateStack = "UpdateStack",
|
||||
RenameStack = "RenameStack",
|
||||
DeleteStack = "DeleteStack",
|
||||
WriteStackContents = "WriteStackContents",
|
||||
RefreshStackCache = "RefreshStackCache",
|
||||
PullStack = "PullStack",
|
||||
DeployStack = "DeployStack",
|
||||
StartStack = "StartStack",
|
||||
RestartStack = "RestartStack",
|
||||
PauseStack = "PauseStack",
|
||||
UnpauseStack = "UnpauseStack",
|
||||
StopStack = "StopStack",
|
||||
DestroyStack = "DestroyStack",
|
||||
DeployStackService = "DeployStackService",
|
||||
PullStackService = "PullStackService",
|
||||
StartStackService = "StartStackService",
|
||||
RestartStackService = "RestartStackService",
|
||||
PauseStackService = "PauseStackService",
|
||||
UnpauseStackService = "UnpauseStackService",
|
||||
StopStackService = "StopStackService",
|
||||
DestroyStackService = "DestroyStackService",
|
||||
CreateDeployment = "CreateDeployment",
|
||||
UpdateDeployment = "UpdateDeployment",
|
||||
RenameDeployment = "RenameDeployment",
|
||||
DeleteDeployment = "DeleteDeployment",
|
||||
Deploy = "Deploy",
|
||||
PullDeployment = "PullDeployment",
|
||||
StartDeployment = "StartDeployment",
|
||||
RestartDeployment = "RestartDeployment",
|
||||
PauseDeployment = "PauseDeployment",
|
||||
UnpauseDeployment = "UnpauseDeployment",
|
||||
StopDeployment = "StopDeployment",
|
||||
DestroyDeployment = "DestroyDeployment",
|
||||
CreateBuild = "CreateBuild",
|
||||
UpdateBuild = "UpdateBuild",
|
||||
RenameBuild = "RenameBuild",
|
||||
DeleteBuild = "DeleteBuild",
|
||||
RunBuild = "RunBuild",
|
||||
CancelBuild = "CancelBuild",
|
||||
WriteDockerfile = "WriteDockerfile",
|
||||
CreateRepo = "CreateRepo",
|
||||
UpdateRepo = "UpdateRepo",
|
||||
RenameRepo = "RenameRepo",
|
||||
DeleteRepo = "DeleteRepo",
|
||||
CloneRepo = "CloneRepo",
|
||||
PullRepo = "PullRepo",
|
||||
BuildRepo = "BuildRepo",
|
||||
CancelRepoBuild = "CancelRepoBuild",
|
||||
CreateProcedure = "CreateProcedure",
|
||||
UpdateProcedure = "UpdateProcedure",
|
||||
RenameProcedure = "RenameProcedure",
|
||||
DeleteProcedure = "DeleteProcedure",
|
||||
RunProcedure = "RunProcedure",
|
||||
CreateAction = "CreateAction",
|
||||
UpdateAction = "UpdateAction",
|
||||
RenameAction = "RenameAction",
|
||||
DeleteAction = "DeleteAction",
|
||||
RunAction = "RunAction",
|
||||
CreateBuilder = "CreateBuilder",
|
||||
UpdateBuilder = "UpdateBuilder",
|
||||
RenameBuilder = "RenameBuilder",
|
||||
DeleteBuilder = "DeleteBuilder",
|
||||
CreateAlerter = "CreateAlerter",
|
||||
UpdateAlerter = "UpdateAlerter",
|
||||
RenameAlerter = "RenameAlerter",
|
||||
DeleteAlerter = "DeleteAlerter",
|
||||
TestAlerter = "TestAlerter",
|
||||
CreateResourceSync = "CreateResourceSync",
|
||||
UpdateResourceSync = "UpdateResourceSync",
|
||||
RenameResourceSync = "RenameResourceSync",
|
||||
DeleteResourceSync = "DeleteResourceSync",
|
||||
WriteSyncContents = "WriteSyncContents",
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
CreateGitProviderAccount = "CreateGitProviderAccount",
|
||||
UpdateGitProviderAccount = "UpdateGitProviderAccount",
|
||||
DeleteGitProviderAccount = "DeleteGitProviderAccount",
|
||||
CreateDockerRegistryAccount = "CreateDockerRegistryAccount",
|
||||
UpdateDockerRegistryAccount = "UpdateDockerRegistryAccount",
|
||||
DeleteDockerRegistryAccount = "DeleteDockerRegistryAccount",
|
||||
}
|
||||
|
||||
/** Represents the output of some command being run */
|
||||
export interface Log {
|
||||
/** A label for the log */
|
||||
stage: string;
|
||||
/** The command which was executed */
|
||||
command: string;
|
||||
/** The output of the command in the standard channel */
|
||||
stdout: string;
|
||||
/** The output of the command in the error channel */
|
||||
stderr: string;
|
||||
/** Whether the command run was successful */
|
||||
success: boolean;
|
||||
/** The start time of the command execution */
|
||||
start_ts: I64;
|
||||
/** The end time of the command execution */
|
||||
end_ts: I64;
|
||||
}
|
||||
|
||||
/** An update's status */
|
||||
export enum UpdateStatus {
|
||||
/** The run is in the system but hasn't started yet */
|
||||
Queued = "Queued",
|
||||
/** The run is currently running */
|
||||
InProgress = "InProgress",
|
||||
/** The run is complete */
|
||||
Complete = "Complete",
|
||||
}
|
||||
|
||||
export interface Version {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
}
|
||||
|
||||
/** Represents an action performed by Komodo. */
|
||||
export interface Update {
|
||||
/**
|
||||
* The Mongo ID of the update.
|
||||
* This field is de/serialized from/to JSON as
|
||||
* `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }`
|
||||
*/
|
||||
_id?: MongoId;
|
||||
/** The operation performed */
|
||||
operation: Operation;
|
||||
/** The time the operation started */
|
||||
start_ts: I64;
|
||||
/** Whether the operation was successful */
|
||||
success: boolean;
|
||||
/**
|
||||
* The user id that triggered the update.
|
||||
*
|
||||
* Also can take these values for operations triggered automatically:
|
||||
* - `Procedure`: The operation was triggered as part of a procedure run
|
||||
* - `Github`: The operation was triggered by a github webhook
|
||||
* - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
|
||||
*/
|
||||
operator: string;
|
||||
/** The target resource to which this update refers */
|
||||
target: ResourceTarget;
|
||||
/** Logs produced as the operation is performed */
|
||||
logs: Log[];
|
||||
/** The time the operation completed. */
|
||||
end_ts?: I64;
|
||||
/**
|
||||
* The status of the update
|
||||
* - `Queued`
|
||||
* - `InProgress`
|
||||
* - `Complete`
|
||||
*/
|
||||
status: UpdateStatus;
|
||||
/** An optional version on the update, ie build version or deployed version. */
|
||||
version?: Version;
|
||||
/** An optional commit hash associated with the update, ie cloned hash or deployed hash. */
|
||||
commit_hash?: string;
|
||||
/** Some unstructured, operation specific data. Not for general usage. */
|
||||
other_data?: string;
|
||||
/** If the update is for resource config update, give the previous toml contents */
|
||||
prev_toml?: string;
|
||||
/** If the update is for resource config update, give the current (at time of Update) toml contents */
|
||||
current_toml?: string;
|
||||
}
|
||||
|
||||
export type BoxUpdate = Update;
|
||||
|
||||
/** Configuration for an image registry */
|
||||
export interface ImageRegistryConfig {
|
||||
/**
|
||||
@@ -463,11 +655,13 @@ export interface BuildListItemInfo {
|
||||
/** Whether build is in files on host mode. */
|
||||
files_on_host: boolean;
|
||||
/** The git provider domain */
|
||||
git_provider?: string;
|
||||
git_provider: string;
|
||||
/** The repo used as the source of the build */
|
||||
repo?: string;
|
||||
repo: string;
|
||||
/** The branch of the repo */
|
||||
branch?: string;
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** Latest built short commit hash, or null. */
|
||||
built_hash?: string;
|
||||
/** Latest short commit hash, or null. Only for repo based stacks */
|
||||
@@ -501,9 +695,10 @@ export type BuilderConfig =
|
||||
export type Builder = Resource<BuilderConfig, undefined>;
|
||||
|
||||
export interface BuilderListItemInfo {
|
||||
/** 'Server' or 'Aws' */
|
||||
/** 'Url', 'Server', or 'Aws' */
|
||||
builder_type: string;
|
||||
/**
|
||||
* If 'Url': null
|
||||
* If 'Server': the server id
|
||||
* If 'Aws': the instance type (eg. c5.xlarge)
|
||||
*/
|
||||
@@ -658,10 +853,6 @@ export type Procedure = Resource<ProcedureConfig, undefined>;
|
||||
|
||||
export type CopyProcedureResponse = Procedure;
|
||||
|
||||
/** Represents an empty json object: `{}` */
|
||||
export interface NoData {
|
||||
}
|
||||
|
||||
export type CreateActionWebhookResponse = NoData;
|
||||
|
||||
/** Response for [CreateApiKey]. */
|
||||
@@ -985,15 +1176,26 @@ export type Deployment = Resource<DeploymentConfig, undefined>;
|
||||
* - Running -> running.
|
||||
*/
|
||||
export enum DeploymentState {
|
||||
Unknown = "unknown",
|
||||
NotDeployed = "not_deployed",
|
||||
Created = "created",
|
||||
Restarting = "restarting",
|
||||
/** The deployment is currently re/deploying */
|
||||
Deploying = "deploying",
|
||||
/** Container is running */
|
||||
Running = "running",
|
||||
/** Container is created but not running */
|
||||
Created = "created",
|
||||
/** Container is in restart loop */
|
||||
Restarting = "restarting",
|
||||
/** Container is being removed */
|
||||
Removing = "removing",
|
||||
/** Container is paused */
|
||||
Paused = "paused",
|
||||
/** Container is exited */
|
||||
Exited = "exited",
|
||||
/** Container is dead */
|
||||
Dead = "dead",
|
||||
/** The deployment is not deployed (no matching container) */
|
||||
NotDeployed = "not_deployed",
|
||||
/** Server not reachable for status */
|
||||
Unknown = "unknown",
|
||||
}
|
||||
|
||||
export interface DeploymentListItemInfo {
|
||||
@@ -1304,24 +1506,6 @@ export type GetBuildResponse = Build;
|
||||
|
||||
export type GetBuilderResponse = Builder;
|
||||
|
||||
/** Represents the output of some command being run */
|
||||
export interface Log {
|
||||
/** A label for the log */
|
||||
stage: string;
|
||||
/** The command which was executed */
|
||||
command: string;
|
||||
/** The output of the command in the standard channel */
|
||||
stdout: string;
|
||||
/** The output of the command in the error channel */
|
||||
stderr: string;
|
||||
/** Whether the command run was successful */
|
||||
success: boolean;
|
||||
/** The start time of the command execution */
|
||||
start_ts: I64;
|
||||
/** The end time of the command execution */
|
||||
end_ts: I64;
|
||||
}
|
||||
|
||||
export type GetContainerLogResponse = Log;
|
||||
|
||||
export interface DeploymentActionState {
|
||||
@@ -2146,178 +2330,6 @@ export interface Tag {
|
||||
|
||||
export type GetTagResponse = Tag;
|
||||
|
||||
export enum Operation {
|
||||
None = "None",
|
||||
CreateServer = "CreateServer",
|
||||
UpdateServer = "UpdateServer",
|
||||
DeleteServer = "DeleteServer",
|
||||
RenameServer = "RenameServer",
|
||||
StartContainer = "StartContainer",
|
||||
RestartContainer = "RestartContainer",
|
||||
PauseContainer = "PauseContainer",
|
||||
UnpauseContainer = "UnpauseContainer",
|
||||
StopContainer = "StopContainer",
|
||||
DestroyContainer = "DestroyContainer",
|
||||
StartAllContainers = "StartAllContainers",
|
||||
RestartAllContainers = "RestartAllContainers",
|
||||
PauseAllContainers = "PauseAllContainers",
|
||||
UnpauseAllContainers = "UnpauseAllContainers",
|
||||
StopAllContainers = "StopAllContainers",
|
||||
PruneContainers = "PruneContainers",
|
||||
CreateNetwork = "CreateNetwork",
|
||||
DeleteNetwork = "DeleteNetwork",
|
||||
PruneNetworks = "PruneNetworks",
|
||||
DeleteImage = "DeleteImage",
|
||||
PruneImages = "PruneImages",
|
||||
DeleteVolume = "DeleteVolume",
|
||||
PruneVolumes = "PruneVolumes",
|
||||
PruneDockerBuilders = "PruneDockerBuilders",
|
||||
PruneBuildx = "PruneBuildx",
|
||||
PruneSystem = "PruneSystem",
|
||||
CreateStack = "CreateStack",
|
||||
UpdateStack = "UpdateStack",
|
||||
RenameStack = "RenameStack",
|
||||
DeleteStack = "DeleteStack",
|
||||
WriteStackContents = "WriteStackContents",
|
||||
RefreshStackCache = "RefreshStackCache",
|
||||
PullStack = "PullStack",
|
||||
DeployStack = "DeployStack",
|
||||
StartStack = "StartStack",
|
||||
RestartStack = "RestartStack",
|
||||
PauseStack = "PauseStack",
|
||||
UnpauseStack = "UnpauseStack",
|
||||
StopStack = "StopStack",
|
||||
DestroyStack = "DestroyStack",
|
||||
DeployStackService = "DeployStackService",
|
||||
PullStackService = "PullStackService",
|
||||
StartStackService = "StartStackService",
|
||||
RestartStackService = "RestartStackService",
|
||||
PauseStackService = "PauseStackService",
|
||||
UnpauseStackService = "UnpauseStackService",
|
||||
StopStackService = "StopStackService",
|
||||
DestroyStackService = "DestroyStackService",
|
||||
CreateDeployment = "CreateDeployment",
|
||||
UpdateDeployment = "UpdateDeployment",
|
||||
RenameDeployment = "RenameDeployment",
|
||||
DeleteDeployment = "DeleteDeployment",
|
||||
Deploy = "Deploy",
|
||||
PullDeployment = "PullDeployment",
|
||||
StartDeployment = "StartDeployment",
|
||||
RestartDeployment = "RestartDeployment",
|
||||
PauseDeployment = "PauseDeployment",
|
||||
UnpauseDeployment = "UnpauseDeployment",
|
||||
StopDeployment = "StopDeployment",
|
||||
DestroyDeployment = "DestroyDeployment",
|
||||
CreateBuild = "CreateBuild",
|
||||
UpdateBuild = "UpdateBuild",
|
||||
RenameBuild = "RenameBuild",
|
||||
DeleteBuild = "DeleteBuild",
|
||||
RunBuild = "RunBuild",
|
||||
CancelBuild = "CancelBuild",
|
||||
WriteDockerfile = "WriteDockerfile",
|
||||
CreateRepo = "CreateRepo",
|
||||
UpdateRepo = "UpdateRepo",
|
||||
RenameRepo = "RenameRepo",
|
||||
DeleteRepo = "DeleteRepo",
|
||||
CloneRepo = "CloneRepo",
|
||||
PullRepo = "PullRepo",
|
||||
BuildRepo = "BuildRepo",
|
||||
CancelRepoBuild = "CancelRepoBuild",
|
||||
CreateProcedure = "CreateProcedure",
|
||||
UpdateProcedure = "UpdateProcedure",
|
||||
RenameProcedure = "RenameProcedure",
|
||||
DeleteProcedure = "DeleteProcedure",
|
||||
RunProcedure = "RunProcedure",
|
||||
CreateAction = "CreateAction",
|
||||
UpdateAction = "UpdateAction",
|
||||
RenameAction = "RenameAction",
|
||||
DeleteAction = "DeleteAction",
|
||||
RunAction = "RunAction",
|
||||
CreateBuilder = "CreateBuilder",
|
||||
UpdateBuilder = "UpdateBuilder",
|
||||
RenameBuilder = "RenameBuilder",
|
||||
DeleteBuilder = "DeleteBuilder",
|
||||
CreateAlerter = "CreateAlerter",
|
||||
UpdateAlerter = "UpdateAlerter",
|
||||
RenameAlerter = "RenameAlerter",
|
||||
DeleteAlerter = "DeleteAlerter",
|
||||
TestAlerter = "TestAlerter",
|
||||
CreateResourceSync = "CreateResourceSync",
|
||||
UpdateResourceSync = "UpdateResourceSync",
|
||||
RenameResourceSync = "RenameResourceSync",
|
||||
DeleteResourceSync = "DeleteResourceSync",
|
||||
WriteSyncContents = "WriteSyncContents",
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
CreateGitProviderAccount = "CreateGitProviderAccount",
|
||||
UpdateGitProviderAccount = "UpdateGitProviderAccount",
|
||||
DeleteGitProviderAccount = "DeleteGitProviderAccount",
|
||||
CreateDockerRegistryAccount = "CreateDockerRegistryAccount",
|
||||
UpdateDockerRegistryAccount = "UpdateDockerRegistryAccount",
|
||||
DeleteDockerRegistryAccount = "DeleteDockerRegistryAccount",
|
||||
}
|
||||
|
||||
/** An update's status */
|
||||
export enum UpdateStatus {
|
||||
/** The run is in the system but hasn't started yet */
|
||||
Queued = "Queued",
|
||||
/** The run is currently running */
|
||||
InProgress = "InProgress",
|
||||
/** The run is complete */
|
||||
Complete = "Complete",
|
||||
}
|
||||
|
||||
/** Represents an action performed by Komodo. */
|
||||
export interface Update {
|
||||
/**
|
||||
* The Mongo ID of the update.
|
||||
* This field is de/serialized from/to JSON as
|
||||
* `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }`
|
||||
*/
|
||||
_id?: MongoId;
|
||||
/** The operation performed */
|
||||
operation: Operation;
|
||||
/** The time the operation started */
|
||||
start_ts: I64;
|
||||
/** Whether the operation was successful */
|
||||
success: boolean;
|
||||
/**
|
||||
* The user id that triggered the update.
|
||||
*
|
||||
* Also can take these values for operations triggered automatically:
|
||||
* - `Procedure`: The operation was triggered as part of a procedure run
|
||||
* - `Github`: The operation was triggered by a github webhook
|
||||
* - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
|
||||
*/
|
||||
operator: string;
|
||||
/** The target resource to which this update refers */
|
||||
target: ResourceTarget;
|
||||
/** Logs produced as the operation is performed */
|
||||
logs: Log[];
|
||||
/** The time the operation completed. */
|
||||
end_ts?: I64;
|
||||
/**
|
||||
* The status of the update
|
||||
* - `Queued`
|
||||
* - `InProgress`
|
||||
* - `Complete`
|
||||
*/
|
||||
status: UpdateStatus;
|
||||
/** An optional version on the update, ie build version or deployed version. */
|
||||
version?: Version;
|
||||
/** An optional commit hash associated with the update, ie cloned hash or deployed hash. */
|
||||
commit_hash?: string;
|
||||
/** Some unstructured, operation specific data. Not for general usage. */
|
||||
other_data?: string;
|
||||
/** If the update is for resource config update, give the previous toml contents */
|
||||
prev_toml?: string;
|
||||
/** If the update is for resource config update, give the current (at time of Update) toml contents */
|
||||
current_toml?: string;
|
||||
}
|
||||
|
||||
export type GetUpdateResponse = Update;
|
||||
|
||||
/**
|
||||
@@ -3411,6 +3423,8 @@ export interface ProcedureListItemInfo {
|
||||
stages: I64;
|
||||
/** Reflect whether last run successful / currently running. */
|
||||
state: ProcedureState;
|
||||
/** Procedure last successful run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/**
|
||||
* If the procedure has schedule enabled, this is the
|
||||
* next scheduled run time in unix ms.
|
||||
@@ -3457,6 +3471,8 @@ export interface RepoListItemInfo {
|
||||
repo: string;
|
||||
/** The configured branch */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** The repo state */
|
||||
state: RepoState;
|
||||
/** If the repo is cloned, will be the cloned short commit hash. */
|
||||
@@ -3503,6 +3519,8 @@ export interface ResourceSyncListItemInfo {
|
||||
repo: string;
|
||||
/** The branch of the repo */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** Short commit hash of last sync, or empty string */
|
||||
last_sync_hash?: string;
|
||||
/** Commit message of last sync, or empty string */
|
||||
@@ -3515,6 +3533,35 @@ export type ResourceSyncListItem = ResourceListItem<ResourceSyncListItemInfo>;
|
||||
|
||||
export type ListResourceSyncsResponse = ResourceSyncListItem[];
|
||||
|
||||
/** A scheduled Action / Procedure run. */
|
||||
export interface Schedule {
|
||||
/** Procedure or Alerter */
|
||||
target: ResourceTarget;
|
||||
/** Readable name of the target resource */
|
||||
name: string;
|
||||
/** The format of the schedule expression */
|
||||
schedule_format: ScheduleFormat;
|
||||
/** The schedule for the run */
|
||||
schedule: string;
|
||||
/** Whether the scheduled run is enabled */
|
||||
enabled: boolean;
|
||||
/** Custom schedule timezone if it exists */
|
||||
schedule_timezone: string;
|
||||
/** Last run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/** Next scheduled run time in unix ms. */
|
||||
next_scheduled_run?: I64;
|
||||
/**
|
||||
* If there is an error parsing schedule expression,
|
||||
* it will be given here.
|
||||
*/
|
||||
schedule_error?: string;
|
||||
/** Resource tags. */
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type ListSchedulesResponse = Schedule[];
|
||||
|
||||
export type ListSecretsResponse = string[];
|
||||
|
||||
export enum ServerState {
|
||||
@@ -3565,6 +3612,8 @@ export interface StackService {
|
||||
export type ListStackServicesResponse = StackService[];
|
||||
|
||||
export enum StackState {
|
||||
/** The stack is currently re/deploying */
|
||||
Deploying = "deploying",
|
||||
/** All containers are running. */
|
||||
Running = "running",
|
||||
/** All containers are paused */
|
||||
@@ -3583,7 +3632,7 @@ export enum StackState {
|
||||
Unhealthy = "unhealthy",
|
||||
/** The stack is not deployed */
|
||||
Down = "down",
|
||||
/** Server not reachable */
|
||||
/** Server not reachable for status */
|
||||
Unknown = "unknown",
|
||||
}
|
||||
|
||||
@@ -3608,6 +3657,8 @@ export interface StackListItemInfo {
|
||||
repo: string;
|
||||
/** The configured branch */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** The stack state */
|
||||
state: StackState;
|
||||
/** A string given by docker conveying the status of the stack. */
|
||||
@@ -6321,6 +6372,17 @@ export interface ListResourceSyncs {
|
||||
query?: ResourceSyncQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* List configured schedules.
|
||||
* Response: [ListSchedulesResponse].
|
||||
*/
|
||||
export interface ListSchedules {
|
||||
/** Pass Vec of tag ids or tag names */
|
||||
tags?: string[];
|
||||
/** 'All' or 'Any' */
|
||||
tag_behavior?: TagBehavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available secrets from the core config.
|
||||
* Response: [ListSecretsResponse].
|
||||
@@ -6562,9 +6624,9 @@ export interface PermissionToml {
|
||||
* - Execute
|
||||
* - Write
|
||||
*/
|
||||
level: PermissionLevel;
|
||||
level?: PermissionLevel;
|
||||
/** Any [SpecificPermissions](SpecificPermission) on the resource */
|
||||
specific: Array<SpecificPermission>;
|
||||
specific?: Array<SpecificPermission>;
|
||||
}
|
||||
|
||||
export enum PortTypeEnum {
|
||||
@@ -7783,6 +7845,7 @@ export type ReadRequest =
|
||||
| { type: "GetActionActionState", params: GetActionActionState }
|
||||
| { type: "ListActions", params: ListActions }
|
||||
| { type: "ListFullActions", params: ListFullActions }
|
||||
| { type: "ListSchedules", params: ListSchedules }
|
||||
| { type: "GetServersSummary", params: GetServersSummary }
|
||||
| { type: "GetServer", params: GetServer }
|
||||
| { type: "GetServerState", params: GetServerState }
|
||||
@@ -7897,9 +7960,9 @@ export enum SpecificPermission {
|
||||
Attach = "Attach",
|
||||
/**
|
||||
* On **Server**
|
||||
* - Access the `docker inspect` apis
|
||||
* - Access the `container inspect` apis
|
||||
* On **Stack / Deployment**
|
||||
* - Access `docker inspect $container` for associated containers
|
||||
* - Access `container inspect` apis for associated containers
|
||||
*/
|
||||
Inspect = "Inspect",
|
||||
/**
|
||||
|
||||
@@ -6,8 +6,9 @@ use komodo_client::entities::{
|
||||
use resolver_api::Resolve;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Returns `null` if not a repo
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
|
||||
#[response(LatestCommit)]
|
||||
#[response(Option<LatestCommit>)]
|
||||
#[error(serror::Error)]
|
||||
pub struct GetLatestCommit {
|
||||
pub name: String,
|
||||
|
||||
1
frontend/public/client/responses.d.ts
vendored
1
frontend/public/client/responses.d.ts
vendored
@@ -38,6 +38,7 @@ export type ReadResponses = {
|
||||
GetActionActionState: Types.GetActionActionStateResponse;
|
||||
ListActions: Types.ListActionsResponse;
|
||||
ListFullActions: Types.ListFullActionsResponse;
|
||||
ListSchedules: Types.ListSchedulesResponse;
|
||||
GetServersSummary: Types.GetServersSummaryResponse;
|
||||
GetServer: Types.GetServerResponse;
|
||||
GetServerState: Types.GetServerStateResponse;
|
||||
|
||||
482
frontend/public/client/types.d.ts
vendored
482
frontend/public/client/types.d.ts
vendored
@@ -104,11 +104,10 @@ export interface ActionConfig {
|
||||
*/
|
||||
file_contents?: string;
|
||||
}
|
||||
export interface ActionInfo {
|
||||
/** When action was last run */
|
||||
last_run_at?: I64;
|
||||
/** Represents an empty json object: `{}` */
|
||||
export interface NoData {
|
||||
}
|
||||
export type Action = Resource<ActionConfig, ActionInfo>;
|
||||
export type Action = Resource<ActionConfig, NoData>;
|
||||
export interface ResourceListItem<Info> {
|
||||
/** The resource id */
|
||||
id: string;
|
||||
@@ -132,12 +131,12 @@ export declare enum ActionState {
|
||||
Running = "Running"
|
||||
}
|
||||
export interface ActionListItemInfo {
|
||||
/** Action last run timestamp in ms. */
|
||||
last_run_at: I64;
|
||||
/** Whether last action run successful */
|
||||
state: ActionState;
|
||||
/** Action last successful run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/**
|
||||
* If the procedure has schedule enabled, this is the
|
||||
* If the action has schedule enabled, this is the
|
||||
* next scheduled run time in unix ms.
|
||||
*/
|
||||
next_scheduled_run?: I64;
|
||||
@@ -159,6 +158,7 @@ export interface ResourceQuery<T> {
|
||||
names?: string[];
|
||||
/** Pass Vec of tag ids or tag names */
|
||||
tags?: string[];
|
||||
/** 'All' or 'Any' */
|
||||
tag_behavior?: TagBehavior;
|
||||
specific?: T;
|
||||
}
|
||||
@@ -279,11 +279,198 @@ export type BatchExecutionResponseItem = {
|
||||
data: BatchExecutionResponseItemErr;
|
||||
};
|
||||
export type BatchExecutionResponse = BatchExecutionResponseItem[];
|
||||
export declare enum Operation {
|
||||
None = "None",
|
||||
CreateServer = "CreateServer",
|
||||
UpdateServer = "UpdateServer",
|
||||
DeleteServer = "DeleteServer",
|
||||
RenameServer = "RenameServer",
|
||||
StartContainer = "StartContainer",
|
||||
RestartContainer = "RestartContainer",
|
||||
PauseContainer = "PauseContainer",
|
||||
UnpauseContainer = "UnpauseContainer",
|
||||
StopContainer = "StopContainer",
|
||||
DestroyContainer = "DestroyContainer",
|
||||
StartAllContainers = "StartAllContainers",
|
||||
RestartAllContainers = "RestartAllContainers",
|
||||
PauseAllContainers = "PauseAllContainers",
|
||||
UnpauseAllContainers = "UnpauseAllContainers",
|
||||
StopAllContainers = "StopAllContainers",
|
||||
PruneContainers = "PruneContainers",
|
||||
CreateNetwork = "CreateNetwork",
|
||||
DeleteNetwork = "DeleteNetwork",
|
||||
PruneNetworks = "PruneNetworks",
|
||||
DeleteImage = "DeleteImage",
|
||||
PruneImages = "PruneImages",
|
||||
DeleteVolume = "DeleteVolume",
|
||||
PruneVolumes = "PruneVolumes",
|
||||
PruneDockerBuilders = "PruneDockerBuilders",
|
||||
PruneBuildx = "PruneBuildx",
|
||||
PruneSystem = "PruneSystem",
|
||||
CreateStack = "CreateStack",
|
||||
UpdateStack = "UpdateStack",
|
||||
RenameStack = "RenameStack",
|
||||
DeleteStack = "DeleteStack",
|
||||
WriteStackContents = "WriteStackContents",
|
||||
RefreshStackCache = "RefreshStackCache",
|
||||
PullStack = "PullStack",
|
||||
DeployStack = "DeployStack",
|
||||
StartStack = "StartStack",
|
||||
RestartStack = "RestartStack",
|
||||
PauseStack = "PauseStack",
|
||||
UnpauseStack = "UnpauseStack",
|
||||
StopStack = "StopStack",
|
||||
DestroyStack = "DestroyStack",
|
||||
DeployStackService = "DeployStackService",
|
||||
PullStackService = "PullStackService",
|
||||
StartStackService = "StartStackService",
|
||||
RestartStackService = "RestartStackService",
|
||||
PauseStackService = "PauseStackService",
|
||||
UnpauseStackService = "UnpauseStackService",
|
||||
StopStackService = "StopStackService",
|
||||
DestroyStackService = "DestroyStackService",
|
||||
CreateDeployment = "CreateDeployment",
|
||||
UpdateDeployment = "UpdateDeployment",
|
||||
RenameDeployment = "RenameDeployment",
|
||||
DeleteDeployment = "DeleteDeployment",
|
||||
Deploy = "Deploy",
|
||||
PullDeployment = "PullDeployment",
|
||||
StartDeployment = "StartDeployment",
|
||||
RestartDeployment = "RestartDeployment",
|
||||
PauseDeployment = "PauseDeployment",
|
||||
UnpauseDeployment = "UnpauseDeployment",
|
||||
StopDeployment = "StopDeployment",
|
||||
DestroyDeployment = "DestroyDeployment",
|
||||
CreateBuild = "CreateBuild",
|
||||
UpdateBuild = "UpdateBuild",
|
||||
RenameBuild = "RenameBuild",
|
||||
DeleteBuild = "DeleteBuild",
|
||||
RunBuild = "RunBuild",
|
||||
CancelBuild = "CancelBuild",
|
||||
WriteDockerfile = "WriteDockerfile",
|
||||
CreateRepo = "CreateRepo",
|
||||
UpdateRepo = "UpdateRepo",
|
||||
RenameRepo = "RenameRepo",
|
||||
DeleteRepo = "DeleteRepo",
|
||||
CloneRepo = "CloneRepo",
|
||||
PullRepo = "PullRepo",
|
||||
BuildRepo = "BuildRepo",
|
||||
CancelRepoBuild = "CancelRepoBuild",
|
||||
CreateProcedure = "CreateProcedure",
|
||||
UpdateProcedure = "UpdateProcedure",
|
||||
RenameProcedure = "RenameProcedure",
|
||||
DeleteProcedure = "DeleteProcedure",
|
||||
RunProcedure = "RunProcedure",
|
||||
CreateAction = "CreateAction",
|
||||
UpdateAction = "UpdateAction",
|
||||
RenameAction = "RenameAction",
|
||||
DeleteAction = "DeleteAction",
|
||||
RunAction = "RunAction",
|
||||
CreateBuilder = "CreateBuilder",
|
||||
UpdateBuilder = "UpdateBuilder",
|
||||
RenameBuilder = "RenameBuilder",
|
||||
DeleteBuilder = "DeleteBuilder",
|
||||
CreateAlerter = "CreateAlerter",
|
||||
UpdateAlerter = "UpdateAlerter",
|
||||
RenameAlerter = "RenameAlerter",
|
||||
DeleteAlerter = "DeleteAlerter",
|
||||
TestAlerter = "TestAlerter",
|
||||
CreateResourceSync = "CreateResourceSync",
|
||||
UpdateResourceSync = "UpdateResourceSync",
|
||||
RenameResourceSync = "RenameResourceSync",
|
||||
DeleteResourceSync = "DeleteResourceSync",
|
||||
WriteSyncContents = "WriteSyncContents",
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
CreateGitProviderAccount = "CreateGitProviderAccount",
|
||||
UpdateGitProviderAccount = "UpdateGitProviderAccount",
|
||||
DeleteGitProviderAccount = "DeleteGitProviderAccount",
|
||||
CreateDockerRegistryAccount = "CreateDockerRegistryAccount",
|
||||
UpdateDockerRegistryAccount = "UpdateDockerRegistryAccount",
|
||||
DeleteDockerRegistryAccount = "DeleteDockerRegistryAccount"
|
||||
}
|
||||
/** Represents the output of some command being run */
|
||||
export interface Log {
|
||||
/** A label for the log */
|
||||
stage: string;
|
||||
/** The command which was executed */
|
||||
command: string;
|
||||
/** The output of the command in the standard channel */
|
||||
stdout: string;
|
||||
/** The output of the command in the error channel */
|
||||
stderr: string;
|
||||
/** Whether the command run was successful */
|
||||
success: boolean;
|
||||
/** The start time of the command execution */
|
||||
start_ts: I64;
|
||||
/** The end time of the command execution */
|
||||
end_ts: I64;
|
||||
}
|
||||
/** An update's status */
|
||||
export declare enum UpdateStatus {
|
||||
/** The run is in the system but hasn't started yet */
|
||||
Queued = "Queued",
|
||||
/** The run is currently running */
|
||||
InProgress = "InProgress",
|
||||
/** The run is complete */
|
||||
Complete = "Complete"
|
||||
}
|
||||
export interface Version {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
}
|
||||
/** Represents an action performed by Komodo. */
|
||||
export interface Update {
|
||||
/**
|
||||
* The Mongo ID of the update.
|
||||
* This field is de/serialized from/to JSON as
|
||||
* `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }`
|
||||
*/
|
||||
_id?: MongoId;
|
||||
/** The operation performed */
|
||||
operation: Operation;
|
||||
/** The time the operation started */
|
||||
start_ts: I64;
|
||||
/** Whether the operation was successful */
|
||||
success: boolean;
|
||||
/**
|
||||
* The user id that triggered the update.
|
||||
*
|
||||
* Also can take these values for operations triggered automatically:
|
||||
* - `Procedure`: The operation was triggered as part of a procedure run
|
||||
* - `Github`: The operation was triggered by a github webhook
|
||||
* - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
|
||||
*/
|
||||
operator: string;
|
||||
/** The target resource to which this update refers */
|
||||
target: ResourceTarget;
|
||||
/** Logs produced as the operation is performed */
|
||||
logs: Log[];
|
||||
/** The time the operation completed. */
|
||||
end_ts?: I64;
|
||||
/**
|
||||
* The status of the update
|
||||
* - `Queued`
|
||||
* - `InProgress`
|
||||
* - `Complete`
|
||||
*/
|
||||
status: UpdateStatus;
|
||||
/** An optional version on the update, ie build version or deployed version. */
|
||||
version?: Version;
|
||||
/** An optional commit hash associated with the update, ie cloned hash or deployed hash. */
|
||||
commit_hash?: string;
|
||||
/** Some unstructured, operation specific data. Not for general usage. */
|
||||
other_data?: string;
|
||||
/** If the update is for resource config update, give the previous toml contents */
|
||||
prev_toml?: string;
|
||||
/** If the update is for resource config update, give the current (at time of Update) toml contents */
|
||||
current_toml?: string;
|
||||
}
|
||||
export type BoxUpdate = Update;
|
||||
/** Configuration for an image registry */
|
||||
export interface ImageRegistryConfig {
|
||||
/**
|
||||
@@ -465,11 +652,13 @@ export interface BuildListItemInfo {
|
||||
/** Whether build is in files on host mode. */
|
||||
files_on_host: boolean;
|
||||
/** The git provider domain */
|
||||
git_provider?: string;
|
||||
git_provider: string;
|
||||
/** The repo used as the source of the build */
|
||||
repo?: string;
|
||||
repo: string;
|
||||
/** The branch of the repo */
|
||||
branch?: string;
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** Latest built short commit hash, or null. */
|
||||
built_hash?: string;
|
||||
/** Latest short commit hash, or null. Only for repo based stacks */
|
||||
@@ -506,9 +695,10 @@ export type BuilderConfig =
|
||||
};
|
||||
export type Builder = Resource<BuilderConfig, undefined>;
|
||||
export interface BuilderListItemInfo {
|
||||
/** 'Server' or 'Aws' */
|
||||
/** 'Url', 'Server', or 'Aws' */
|
||||
builder_type: string;
|
||||
/**
|
||||
* If 'Url': null
|
||||
* If 'Server': the server id
|
||||
* If 'Aws': the instance type (eg. c5.xlarge)
|
||||
*/
|
||||
@@ -780,9 +970,6 @@ export interface ProcedureConfig {
|
||||
*/
|
||||
export type Procedure = Resource<ProcedureConfig, undefined>;
|
||||
export type CopyProcedureResponse = Procedure;
|
||||
/** Represents an empty json object: `{}` */
|
||||
export interface NoData {
|
||||
}
|
||||
export type CreateActionWebhookResponse = NoData;
|
||||
/** Response for [CreateApiKey]. */
|
||||
export interface CreateApiKeyResponse {
|
||||
@@ -1091,15 +1278,26 @@ export type Deployment = Resource<DeploymentConfig, undefined>;
|
||||
* - Running -> running.
|
||||
*/
|
||||
export declare enum DeploymentState {
|
||||
Unknown = "unknown",
|
||||
NotDeployed = "not_deployed",
|
||||
Created = "created",
|
||||
Restarting = "restarting",
|
||||
/** The deployment is currently re/deploying */
|
||||
Deploying = "deploying",
|
||||
/** Container is running */
|
||||
Running = "running",
|
||||
/** Container is created but not running */
|
||||
Created = "created",
|
||||
/** Container is in restart loop */
|
||||
Restarting = "restarting",
|
||||
/** Container is being removed */
|
||||
Removing = "removing",
|
||||
/** Container is paused */
|
||||
Paused = "paused",
|
||||
/** Container is exited */
|
||||
Exited = "exited",
|
||||
Dead = "dead"
|
||||
/** Container is dead */
|
||||
Dead = "dead",
|
||||
/** The deployment is not deployed (no matching container) */
|
||||
NotDeployed = "not_deployed",
|
||||
/** Server not reachable for status */
|
||||
Unknown = "unknown"
|
||||
}
|
||||
export interface DeploymentListItemInfo {
|
||||
/** The state of the deployment / underlying docker container. */
|
||||
@@ -1444,23 +1642,6 @@ export interface BuildActionState {
|
||||
export type GetBuildActionStateResponse = BuildActionState;
|
||||
export type GetBuildResponse = Build;
|
||||
export type GetBuilderResponse = Builder;
|
||||
/** Represents the output of some command being run */
|
||||
export interface Log {
|
||||
/** A label for the log */
|
||||
stage: string;
|
||||
/** The command which was executed */
|
||||
command: string;
|
||||
/** The output of the command in the standard channel */
|
||||
stdout: string;
|
||||
/** The output of the command in the error channel */
|
||||
stderr: string;
|
||||
/** Whether the command run was successful */
|
||||
success: boolean;
|
||||
/** The start time of the command execution */
|
||||
start_ts: I64;
|
||||
/** The end time of the command execution */
|
||||
end_ts: I64;
|
||||
}
|
||||
export type GetContainerLogResponse = Log;
|
||||
export interface DeploymentActionState {
|
||||
pulling: boolean;
|
||||
@@ -2240,175 +2421,6 @@ export interface Tag {
|
||||
owner?: string;
|
||||
}
|
||||
export type GetTagResponse = Tag;
|
||||
export declare enum Operation {
|
||||
None = "None",
|
||||
CreateServer = "CreateServer",
|
||||
UpdateServer = "UpdateServer",
|
||||
DeleteServer = "DeleteServer",
|
||||
RenameServer = "RenameServer",
|
||||
StartContainer = "StartContainer",
|
||||
RestartContainer = "RestartContainer",
|
||||
PauseContainer = "PauseContainer",
|
||||
UnpauseContainer = "UnpauseContainer",
|
||||
StopContainer = "StopContainer",
|
||||
DestroyContainer = "DestroyContainer",
|
||||
StartAllContainers = "StartAllContainers",
|
||||
RestartAllContainers = "RestartAllContainers",
|
||||
PauseAllContainers = "PauseAllContainers",
|
||||
UnpauseAllContainers = "UnpauseAllContainers",
|
||||
StopAllContainers = "StopAllContainers",
|
||||
PruneContainers = "PruneContainers",
|
||||
CreateNetwork = "CreateNetwork",
|
||||
DeleteNetwork = "DeleteNetwork",
|
||||
PruneNetworks = "PruneNetworks",
|
||||
DeleteImage = "DeleteImage",
|
||||
PruneImages = "PruneImages",
|
||||
DeleteVolume = "DeleteVolume",
|
||||
PruneVolumes = "PruneVolumes",
|
||||
PruneDockerBuilders = "PruneDockerBuilders",
|
||||
PruneBuildx = "PruneBuildx",
|
||||
PruneSystem = "PruneSystem",
|
||||
CreateStack = "CreateStack",
|
||||
UpdateStack = "UpdateStack",
|
||||
RenameStack = "RenameStack",
|
||||
DeleteStack = "DeleteStack",
|
||||
WriteStackContents = "WriteStackContents",
|
||||
RefreshStackCache = "RefreshStackCache",
|
||||
PullStack = "PullStack",
|
||||
DeployStack = "DeployStack",
|
||||
StartStack = "StartStack",
|
||||
RestartStack = "RestartStack",
|
||||
PauseStack = "PauseStack",
|
||||
UnpauseStack = "UnpauseStack",
|
||||
StopStack = "StopStack",
|
||||
DestroyStack = "DestroyStack",
|
||||
DeployStackService = "DeployStackService",
|
||||
PullStackService = "PullStackService",
|
||||
StartStackService = "StartStackService",
|
||||
RestartStackService = "RestartStackService",
|
||||
PauseStackService = "PauseStackService",
|
||||
UnpauseStackService = "UnpauseStackService",
|
||||
StopStackService = "StopStackService",
|
||||
DestroyStackService = "DestroyStackService",
|
||||
CreateDeployment = "CreateDeployment",
|
||||
UpdateDeployment = "UpdateDeployment",
|
||||
RenameDeployment = "RenameDeployment",
|
||||
DeleteDeployment = "DeleteDeployment",
|
||||
Deploy = "Deploy",
|
||||
PullDeployment = "PullDeployment",
|
||||
StartDeployment = "StartDeployment",
|
||||
RestartDeployment = "RestartDeployment",
|
||||
PauseDeployment = "PauseDeployment",
|
||||
UnpauseDeployment = "UnpauseDeployment",
|
||||
StopDeployment = "StopDeployment",
|
||||
DestroyDeployment = "DestroyDeployment",
|
||||
CreateBuild = "CreateBuild",
|
||||
UpdateBuild = "UpdateBuild",
|
||||
RenameBuild = "RenameBuild",
|
||||
DeleteBuild = "DeleteBuild",
|
||||
RunBuild = "RunBuild",
|
||||
CancelBuild = "CancelBuild",
|
||||
WriteDockerfile = "WriteDockerfile",
|
||||
CreateRepo = "CreateRepo",
|
||||
UpdateRepo = "UpdateRepo",
|
||||
RenameRepo = "RenameRepo",
|
||||
DeleteRepo = "DeleteRepo",
|
||||
CloneRepo = "CloneRepo",
|
||||
PullRepo = "PullRepo",
|
||||
BuildRepo = "BuildRepo",
|
||||
CancelRepoBuild = "CancelRepoBuild",
|
||||
CreateProcedure = "CreateProcedure",
|
||||
UpdateProcedure = "UpdateProcedure",
|
||||
RenameProcedure = "RenameProcedure",
|
||||
DeleteProcedure = "DeleteProcedure",
|
||||
RunProcedure = "RunProcedure",
|
||||
CreateAction = "CreateAction",
|
||||
UpdateAction = "UpdateAction",
|
||||
RenameAction = "RenameAction",
|
||||
DeleteAction = "DeleteAction",
|
||||
RunAction = "RunAction",
|
||||
CreateBuilder = "CreateBuilder",
|
||||
UpdateBuilder = "UpdateBuilder",
|
||||
RenameBuilder = "RenameBuilder",
|
||||
DeleteBuilder = "DeleteBuilder",
|
||||
CreateAlerter = "CreateAlerter",
|
||||
UpdateAlerter = "UpdateAlerter",
|
||||
RenameAlerter = "RenameAlerter",
|
||||
DeleteAlerter = "DeleteAlerter",
|
||||
TestAlerter = "TestAlerter",
|
||||
CreateResourceSync = "CreateResourceSync",
|
||||
UpdateResourceSync = "UpdateResourceSync",
|
||||
RenameResourceSync = "RenameResourceSync",
|
||||
DeleteResourceSync = "DeleteResourceSync",
|
||||
WriteSyncContents = "WriteSyncContents",
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
CreateGitProviderAccount = "CreateGitProviderAccount",
|
||||
UpdateGitProviderAccount = "UpdateGitProviderAccount",
|
||||
DeleteGitProviderAccount = "DeleteGitProviderAccount",
|
||||
CreateDockerRegistryAccount = "CreateDockerRegistryAccount",
|
||||
UpdateDockerRegistryAccount = "UpdateDockerRegistryAccount",
|
||||
DeleteDockerRegistryAccount = "DeleteDockerRegistryAccount"
|
||||
}
|
||||
/** An update's status */
|
||||
export declare enum UpdateStatus {
|
||||
/** The run is in the system but hasn't started yet */
|
||||
Queued = "Queued",
|
||||
/** The run is currently running */
|
||||
InProgress = "InProgress",
|
||||
/** The run is complete */
|
||||
Complete = "Complete"
|
||||
}
|
||||
/** Represents an action performed by Komodo. */
|
||||
export interface Update {
|
||||
/**
|
||||
* The Mongo ID of the update.
|
||||
* This field is de/serialized from/to JSON as
|
||||
* `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }`
|
||||
*/
|
||||
_id?: MongoId;
|
||||
/** The operation performed */
|
||||
operation: Operation;
|
||||
/** The time the operation started */
|
||||
start_ts: I64;
|
||||
/** Whether the operation was successful */
|
||||
success: boolean;
|
||||
/**
|
||||
* The user id that triggered the update.
|
||||
*
|
||||
* Also can take these values for operations triggered automatically:
|
||||
* - `Procedure`: The operation was triggered as part of a procedure run
|
||||
* - `Github`: The operation was triggered by a github webhook
|
||||
* - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing.
|
||||
*/
|
||||
operator: string;
|
||||
/** The target resource to which this update refers */
|
||||
target: ResourceTarget;
|
||||
/** Logs produced as the operation is performed */
|
||||
logs: Log[];
|
||||
/** The time the operation completed. */
|
||||
end_ts?: I64;
|
||||
/**
|
||||
* The status of the update
|
||||
* - `Queued`
|
||||
* - `InProgress`
|
||||
* - `Complete`
|
||||
*/
|
||||
status: UpdateStatus;
|
||||
/** An optional version on the update, ie build version or deployed version. */
|
||||
version?: Version;
|
||||
/** An optional commit hash associated with the update, ie cloned hash or deployed hash. */
|
||||
commit_hash?: string;
|
||||
/** Some unstructured, operation specific data. Not for general usage. */
|
||||
other_data?: string;
|
||||
/** If the update is for resource config update, give the previous toml contents */
|
||||
prev_toml?: string;
|
||||
/** If the update is for resource config update, give the current (at time of Update) toml contents */
|
||||
current_toml?: string;
|
||||
}
|
||||
export type GetUpdateResponse = Update;
|
||||
/**
|
||||
* Permission users at the group level.
|
||||
@@ -3390,6 +3402,8 @@ export interface ProcedureListItemInfo {
|
||||
stages: I64;
|
||||
/** Reflect whether last run successful / currently running. */
|
||||
state: ProcedureState;
|
||||
/** Procedure last successful run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/**
|
||||
* If the procedure has schedule enabled, this is the
|
||||
* next scheduled run time in unix ms.
|
||||
@@ -3432,6 +3446,8 @@ export interface RepoListItemInfo {
|
||||
repo: string;
|
||||
/** The configured branch */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** The repo state */
|
||||
state: RepoState;
|
||||
/** If the repo is cloned, will be the cloned short commit hash. */
|
||||
@@ -3474,6 +3490,8 @@ export interface ResourceSyncListItemInfo {
|
||||
repo: string;
|
||||
/** The branch of the repo */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** Short commit hash of last sync, or empty string */
|
||||
last_sync_hash?: string;
|
||||
/** Commit message of last sync, or empty string */
|
||||
@@ -3483,6 +3501,33 @@ export interface ResourceSyncListItemInfo {
|
||||
}
|
||||
export type ResourceSyncListItem = ResourceListItem<ResourceSyncListItemInfo>;
|
||||
export type ListResourceSyncsResponse = ResourceSyncListItem[];
|
||||
/** A scheduled Action / Procedure run. */
|
||||
export interface Schedule {
|
||||
/** Procedure or Alerter */
|
||||
target: ResourceTarget;
|
||||
/** Readable name of the target resource */
|
||||
name: string;
|
||||
/** The format of the schedule expression */
|
||||
schedule_format: ScheduleFormat;
|
||||
/** The schedule for the run */
|
||||
schedule: string;
|
||||
/** Whether the scheduled run is enabled */
|
||||
enabled: boolean;
|
||||
/** Custom schedule timezone if it exists */
|
||||
schedule_timezone: string;
|
||||
/** Last run timestamp in ms. */
|
||||
last_run_at?: I64;
|
||||
/** Next scheduled run time in unix ms. */
|
||||
next_scheduled_run?: I64;
|
||||
/**
|
||||
* If there is an error parsing schedule expression,
|
||||
* it will be given here.
|
||||
*/
|
||||
schedule_error?: string;
|
||||
/** Resource tags. */
|
||||
tags: string[];
|
||||
}
|
||||
export type ListSchedulesResponse = Schedule[];
|
||||
export type ListSecretsResponse = string[];
|
||||
export declare enum ServerState {
|
||||
/** Server is unreachable. */
|
||||
@@ -3526,6 +3571,8 @@ export interface StackService {
|
||||
}
|
||||
export type ListStackServicesResponse = StackService[];
|
||||
export declare enum StackState {
|
||||
/** The stack is currently re/deploying */
|
||||
Deploying = "deploying",
|
||||
/** All containers are running. */
|
||||
Running = "running",
|
||||
/** All containers are paused */
|
||||
@@ -3544,7 +3591,7 @@ export declare enum StackState {
|
||||
Unhealthy = "unhealthy",
|
||||
/** The stack is not deployed */
|
||||
Down = "down",
|
||||
/** Server not reachable */
|
||||
/** Server not reachable for status */
|
||||
Unknown = "unknown"
|
||||
}
|
||||
export interface StackServiceWithUpdate {
|
||||
@@ -3567,6 +3614,8 @@ export interface StackListItemInfo {
|
||||
repo: string;
|
||||
/** The configured branch */
|
||||
branch: string;
|
||||
/** Full link to the repo. */
|
||||
repo_link: string;
|
||||
/** The stack state */
|
||||
state: StackState;
|
||||
/** A string given by docker conveying the status of the stack. */
|
||||
@@ -5984,6 +6033,16 @@ export interface ListResourceSyncs {
|
||||
/** optional structured query to filter syncs. */
|
||||
query?: ResourceSyncQuery;
|
||||
}
|
||||
/**
|
||||
* List configured schedules.
|
||||
* Response: [ListSchedulesResponse].
|
||||
*/
|
||||
export interface ListSchedules {
|
||||
/** Pass Vec of tag ids or tag names */
|
||||
tags?: string[];
|
||||
/** 'All' or 'Any' */
|
||||
tag_behavior?: TagBehavior;
|
||||
}
|
||||
/**
|
||||
* List the available secrets from the core config.
|
||||
* Response: [ListSecretsResponse].
|
||||
@@ -6204,9 +6263,9 @@ export interface PermissionToml {
|
||||
* - Execute
|
||||
* - Write
|
||||
*/
|
||||
level: PermissionLevel;
|
||||
level?: PermissionLevel;
|
||||
/** Any [SpecificPermissions](SpecificPermission) on the resource */
|
||||
specific: Array<SpecificPermission>;
|
||||
specific?: Array<SpecificPermission>;
|
||||
}
|
||||
export declare enum PortTypeEnum {
|
||||
EMPTY = "",
|
||||
@@ -7507,6 +7566,9 @@ export type ReadRequest = {
|
||||
} | {
|
||||
type: "ListFullActions";
|
||||
params: ListFullActions;
|
||||
} | {
|
||||
type: "ListSchedules";
|
||||
params: ListSchedules;
|
||||
} | {
|
||||
type: "GetServersSummary";
|
||||
params: GetServersSummary;
|
||||
@@ -7807,9 +7869,9 @@ export declare enum SpecificPermission {
|
||||
Attach = "Attach",
|
||||
/**
|
||||
* On **Server**
|
||||
* - Access the `docker inspect` apis
|
||||
* - Access the `container inspect` apis
|
||||
* On **Stack / Deployment**
|
||||
* - Access `docker inspect $container` for associated containers
|
||||
* - Access `container inspect` apis for associated containers
|
||||
*/
|
||||
Inspect = "Inspect",
|
||||
/**
|
||||
|
||||
@@ -36,142 +36,6 @@ export var TagBehavior;
|
||||
/** Returns resources which have one or more of the tags */
|
||||
TagBehavior["Any"] = "Any";
|
||||
})(TagBehavior || (TagBehavior = {}));
|
||||
export var BuildState;
|
||||
(function (BuildState) {
|
||||
/** Last build successful (or never built) */
|
||||
BuildState["Ok"] = "Ok";
|
||||
/** Last build failed */
|
||||
BuildState["Failed"] = "Failed";
|
||||
/** Currently building */
|
||||
BuildState["Building"] = "Building";
|
||||
/** Other case */
|
||||
BuildState["Unknown"] = "Unknown";
|
||||
})(BuildState || (BuildState = {}));
|
||||
export var RestartMode;
|
||||
(function (RestartMode) {
|
||||
RestartMode["NoRestart"] = "no";
|
||||
RestartMode["OnFailure"] = "on-failure";
|
||||
RestartMode["Always"] = "always";
|
||||
RestartMode["UnlessStopped"] = "unless-stopped";
|
||||
})(RestartMode || (RestartMode = {}));
|
||||
export var TerminationSignal;
|
||||
(function (TerminationSignal) {
|
||||
TerminationSignal["SigHup"] = "SIGHUP";
|
||||
TerminationSignal["SigInt"] = "SIGINT";
|
||||
TerminationSignal["SigQuit"] = "SIGQUIT";
|
||||
TerminationSignal["SigTerm"] = "SIGTERM";
|
||||
})(TerminationSignal || (TerminationSignal = {}));
|
||||
/**
|
||||
* Variants de/serialized from/to snake_case.
|
||||
*
|
||||
* Eg.
|
||||
* - NotDeployed -> not_deployed
|
||||
* - Restarting -> restarting
|
||||
* - Running -> running.
|
||||
*/
|
||||
export var DeploymentState;
|
||||
(function (DeploymentState) {
|
||||
DeploymentState["Unknown"] = "unknown";
|
||||
DeploymentState["NotDeployed"] = "not_deployed";
|
||||
DeploymentState["Created"] = "created";
|
||||
DeploymentState["Restarting"] = "restarting";
|
||||
DeploymentState["Running"] = "running";
|
||||
DeploymentState["Removing"] = "removing";
|
||||
DeploymentState["Paused"] = "paused";
|
||||
DeploymentState["Exited"] = "exited";
|
||||
DeploymentState["Dead"] = "dead";
|
||||
})(DeploymentState || (DeploymentState = {}));
|
||||
/** Severity level of problem. */
|
||||
export var SeverityLevel;
|
||||
(function (SeverityLevel) {
|
||||
/** No problem. */
|
||||
SeverityLevel["Ok"] = "OK";
|
||||
/** Problem is imminent. */
|
||||
SeverityLevel["Warning"] = "WARNING";
|
||||
/** Problem fully realized. */
|
||||
SeverityLevel["Critical"] = "CRITICAL";
|
||||
})(SeverityLevel || (SeverityLevel = {}));
|
||||
export var Timelength;
|
||||
(function (Timelength) {
|
||||
Timelength["OneSecond"] = "1-sec";
|
||||
Timelength["FiveSeconds"] = "5-sec";
|
||||
Timelength["TenSeconds"] = "10-sec";
|
||||
Timelength["FifteenSeconds"] = "15-sec";
|
||||
Timelength["ThirtySeconds"] = "30-sec";
|
||||
Timelength["OneMinute"] = "1-min";
|
||||
Timelength["TwoMinutes"] = "2-min";
|
||||
Timelength["FiveMinutes"] = "5-min";
|
||||
Timelength["TenMinutes"] = "10-min";
|
||||
Timelength["FifteenMinutes"] = "15-min";
|
||||
Timelength["ThirtyMinutes"] = "30-min";
|
||||
Timelength["OneHour"] = "1-hr";
|
||||
Timelength["TwoHours"] = "2-hr";
|
||||
Timelength["SixHours"] = "6-hr";
|
||||
Timelength["EightHours"] = "8-hr";
|
||||
Timelength["TwelveHours"] = "12-hr";
|
||||
Timelength["OneDay"] = "1-day";
|
||||
Timelength["ThreeDay"] = "3-day";
|
||||
Timelength["OneWeek"] = "1-wk";
|
||||
Timelength["TwoWeeks"] = "2-wk";
|
||||
Timelength["ThirtyDays"] = "30-day";
|
||||
})(Timelength || (Timelength = {}));
|
||||
export var TagColor;
|
||||
(function (TagColor) {
|
||||
TagColor["LightSlate"] = "LightSlate";
|
||||
TagColor["Slate"] = "Slate";
|
||||
TagColor["DarkSlate"] = "DarkSlate";
|
||||
TagColor["LightRed"] = "LightRed";
|
||||
TagColor["Red"] = "Red";
|
||||
TagColor["DarkRed"] = "DarkRed";
|
||||
TagColor["LightOrange"] = "LightOrange";
|
||||
TagColor["Orange"] = "Orange";
|
||||
TagColor["DarkOrange"] = "DarkOrange";
|
||||
TagColor["LightAmber"] = "LightAmber";
|
||||
TagColor["Amber"] = "Amber";
|
||||
TagColor["DarkAmber"] = "DarkAmber";
|
||||
TagColor["LightYellow"] = "LightYellow";
|
||||
TagColor["Yellow"] = "Yellow";
|
||||
TagColor["DarkYellow"] = "DarkYellow";
|
||||
TagColor["LightLime"] = "LightLime";
|
||||
TagColor["Lime"] = "Lime";
|
||||
TagColor["DarkLime"] = "DarkLime";
|
||||
TagColor["LightGreen"] = "LightGreen";
|
||||
TagColor["Green"] = "Green";
|
||||
TagColor["DarkGreen"] = "DarkGreen";
|
||||
TagColor["LightEmerald"] = "LightEmerald";
|
||||
TagColor["Emerald"] = "Emerald";
|
||||
TagColor["DarkEmerald"] = "DarkEmerald";
|
||||
TagColor["LightTeal"] = "LightTeal";
|
||||
TagColor["Teal"] = "Teal";
|
||||
TagColor["DarkTeal"] = "DarkTeal";
|
||||
TagColor["LightCyan"] = "LightCyan";
|
||||
TagColor["Cyan"] = "Cyan";
|
||||
TagColor["DarkCyan"] = "DarkCyan";
|
||||
TagColor["LightSky"] = "LightSky";
|
||||
TagColor["Sky"] = "Sky";
|
||||
TagColor["DarkSky"] = "DarkSky";
|
||||
TagColor["LightBlue"] = "LightBlue";
|
||||
TagColor["Blue"] = "Blue";
|
||||
TagColor["DarkBlue"] = "DarkBlue";
|
||||
TagColor["LightIndigo"] = "LightIndigo";
|
||||
TagColor["Indigo"] = "Indigo";
|
||||
TagColor["DarkIndigo"] = "DarkIndigo";
|
||||
TagColor["LightViolet"] = "LightViolet";
|
||||
TagColor["Violet"] = "Violet";
|
||||
TagColor["DarkViolet"] = "DarkViolet";
|
||||
TagColor["LightPurple"] = "LightPurple";
|
||||
TagColor["Purple"] = "Purple";
|
||||
TagColor["DarkPurple"] = "DarkPurple";
|
||||
TagColor["LightFuchsia"] = "LightFuchsia";
|
||||
TagColor["Fuchsia"] = "Fuchsia";
|
||||
TagColor["DarkFuchsia"] = "DarkFuchsia";
|
||||
TagColor["LightPink"] = "LightPink";
|
||||
TagColor["Pink"] = "Pink";
|
||||
TagColor["DarkPink"] = "DarkPink";
|
||||
TagColor["LightRose"] = "LightRose";
|
||||
TagColor["Rose"] = "Rose";
|
||||
TagColor["DarkRose"] = "DarkRose";
|
||||
})(TagColor || (TagColor = {}));
|
||||
export var Operation;
|
||||
(function (Operation) {
|
||||
Operation["None"] = "None";
|
||||
@@ -296,6 +160,153 @@ export var UpdateStatus;
|
||||
/** The run is complete */
|
||||
UpdateStatus["Complete"] = "Complete";
|
||||
})(UpdateStatus || (UpdateStatus = {}));
|
||||
export var BuildState;
|
||||
(function (BuildState) {
|
||||
/** Last build successful (or never built) */
|
||||
BuildState["Ok"] = "Ok";
|
||||
/** Last build failed */
|
||||
BuildState["Failed"] = "Failed";
|
||||
/** Currently building */
|
||||
BuildState["Building"] = "Building";
|
||||
/** Other case */
|
||||
BuildState["Unknown"] = "Unknown";
|
||||
})(BuildState || (BuildState = {}));
|
||||
export var RestartMode;
|
||||
(function (RestartMode) {
|
||||
RestartMode["NoRestart"] = "no";
|
||||
RestartMode["OnFailure"] = "on-failure";
|
||||
RestartMode["Always"] = "always";
|
||||
RestartMode["UnlessStopped"] = "unless-stopped";
|
||||
})(RestartMode || (RestartMode = {}));
|
||||
export var TerminationSignal;
|
||||
(function (TerminationSignal) {
|
||||
TerminationSignal["SigHup"] = "SIGHUP";
|
||||
TerminationSignal["SigInt"] = "SIGINT";
|
||||
TerminationSignal["SigQuit"] = "SIGQUIT";
|
||||
TerminationSignal["SigTerm"] = "SIGTERM";
|
||||
})(TerminationSignal || (TerminationSignal = {}));
|
||||
/**
|
||||
* Variants de/serialized from/to snake_case.
|
||||
*
|
||||
* Eg.
|
||||
* - NotDeployed -> not_deployed
|
||||
* - Restarting -> restarting
|
||||
* - Running -> running.
|
||||
*/
|
||||
export var DeploymentState;
|
||||
(function (DeploymentState) {
|
||||
/** The deployment is currently re/deploying */
|
||||
DeploymentState["Deploying"] = "deploying";
|
||||
/** Container is running */
|
||||
DeploymentState["Running"] = "running";
|
||||
/** Container is created but not running */
|
||||
DeploymentState["Created"] = "created";
|
||||
/** Container is in restart loop */
|
||||
DeploymentState["Restarting"] = "restarting";
|
||||
/** Container is being removed */
|
||||
DeploymentState["Removing"] = "removing";
|
||||
/** Container is paused */
|
||||
DeploymentState["Paused"] = "paused";
|
||||
/** Container is exited */
|
||||
DeploymentState["Exited"] = "exited";
|
||||
/** Container is dead */
|
||||
DeploymentState["Dead"] = "dead";
|
||||
/** The deployment is not deployed (no matching container) */
|
||||
DeploymentState["NotDeployed"] = "not_deployed";
|
||||
/** Server not reachable for status */
|
||||
DeploymentState["Unknown"] = "unknown";
|
||||
})(DeploymentState || (DeploymentState = {}));
|
||||
/** Severity level of problem. */
|
||||
export var SeverityLevel;
|
||||
(function (SeverityLevel) {
|
||||
/** No problem. */
|
||||
SeverityLevel["Ok"] = "OK";
|
||||
/** Problem is imminent. */
|
||||
SeverityLevel["Warning"] = "WARNING";
|
||||
/** Problem fully realized. */
|
||||
SeverityLevel["Critical"] = "CRITICAL";
|
||||
})(SeverityLevel || (SeverityLevel = {}));
|
||||
export var Timelength;
|
||||
(function (Timelength) {
|
||||
Timelength["OneSecond"] = "1-sec";
|
||||
Timelength["FiveSeconds"] = "5-sec";
|
||||
Timelength["TenSeconds"] = "10-sec";
|
||||
Timelength["FifteenSeconds"] = "15-sec";
|
||||
Timelength["ThirtySeconds"] = "30-sec";
|
||||
Timelength["OneMinute"] = "1-min";
|
||||
Timelength["TwoMinutes"] = "2-min";
|
||||
Timelength["FiveMinutes"] = "5-min";
|
||||
Timelength["TenMinutes"] = "10-min";
|
||||
Timelength["FifteenMinutes"] = "15-min";
|
||||
Timelength["ThirtyMinutes"] = "30-min";
|
||||
Timelength["OneHour"] = "1-hr";
|
||||
Timelength["TwoHours"] = "2-hr";
|
||||
Timelength["SixHours"] = "6-hr";
|
||||
Timelength["EightHours"] = "8-hr";
|
||||
Timelength["TwelveHours"] = "12-hr";
|
||||
Timelength["OneDay"] = "1-day";
|
||||
Timelength["ThreeDay"] = "3-day";
|
||||
Timelength["OneWeek"] = "1-wk";
|
||||
Timelength["TwoWeeks"] = "2-wk";
|
||||
Timelength["ThirtyDays"] = "30-day";
|
||||
})(Timelength || (Timelength = {}));
|
||||
export var TagColor;
|
||||
(function (TagColor) {
|
||||
TagColor["LightSlate"] = "LightSlate";
|
||||
TagColor["Slate"] = "Slate";
|
||||
TagColor["DarkSlate"] = "DarkSlate";
|
||||
TagColor["LightRed"] = "LightRed";
|
||||
TagColor["Red"] = "Red";
|
||||
TagColor["DarkRed"] = "DarkRed";
|
||||
TagColor["LightOrange"] = "LightOrange";
|
||||
TagColor["Orange"] = "Orange";
|
||||
TagColor["DarkOrange"] = "DarkOrange";
|
||||
TagColor["LightAmber"] = "LightAmber";
|
||||
TagColor["Amber"] = "Amber";
|
||||
TagColor["DarkAmber"] = "DarkAmber";
|
||||
TagColor["LightYellow"] = "LightYellow";
|
||||
TagColor["Yellow"] = "Yellow";
|
||||
TagColor["DarkYellow"] = "DarkYellow";
|
||||
TagColor["LightLime"] = "LightLime";
|
||||
TagColor["Lime"] = "Lime";
|
||||
TagColor["DarkLime"] = "DarkLime";
|
||||
TagColor["LightGreen"] = "LightGreen";
|
||||
TagColor["Green"] = "Green";
|
||||
TagColor["DarkGreen"] = "DarkGreen";
|
||||
TagColor["LightEmerald"] = "LightEmerald";
|
||||
TagColor["Emerald"] = "Emerald";
|
||||
TagColor["DarkEmerald"] = "DarkEmerald";
|
||||
TagColor["LightTeal"] = "LightTeal";
|
||||
TagColor["Teal"] = "Teal";
|
||||
TagColor["DarkTeal"] = "DarkTeal";
|
||||
TagColor["LightCyan"] = "LightCyan";
|
||||
TagColor["Cyan"] = "Cyan";
|
||||
TagColor["DarkCyan"] = "DarkCyan";
|
||||
TagColor["LightSky"] = "LightSky";
|
||||
TagColor["Sky"] = "Sky";
|
||||
TagColor["DarkSky"] = "DarkSky";
|
||||
TagColor["LightBlue"] = "LightBlue";
|
||||
TagColor["Blue"] = "Blue";
|
||||
TagColor["DarkBlue"] = "DarkBlue";
|
||||
TagColor["LightIndigo"] = "LightIndigo";
|
||||
TagColor["Indigo"] = "Indigo";
|
||||
TagColor["DarkIndigo"] = "DarkIndigo";
|
||||
TagColor["LightViolet"] = "LightViolet";
|
||||
TagColor["Violet"] = "Violet";
|
||||
TagColor["DarkViolet"] = "DarkViolet";
|
||||
TagColor["LightPurple"] = "LightPurple";
|
||||
TagColor["Purple"] = "Purple";
|
||||
TagColor["DarkPurple"] = "DarkPurple";
|
||||
TagColor["LightFuchsia"] = "LightFuchsia";
|
||||
TagColor["Fuchsia"] = "Fuchsia";
|
||||
TagColor["DarkFuchsia"] = "DarkFuchsia";
|
||||
TagColor["LightPink"] = "LightPink";
|
||||
TagColor["Pink"] = "Pink";
|
||||
TagColor["DarkPink"] = "DarkPink";
|
||||
TagColor["LightRose"] = "LightRose";
|
||||
TagColor["Rose"] = "Rose";
|
||||
TagColor["DarkRose"] = "DarkRose";
|
||||
})(TagColor || (TagColor = {}));
|
||||
export var ContainerStateStatusEnum;
|
||||
(function (ContainerStateStatusEnum) {
|
||||
ContainerStateStatusEnum["Empty"] = "";
|
||||
@@ -441,6 +452,8 @@ export var ServerState;
|
||||
})(ServerState || (ServerState = {}));
|
||||
export var StackState;
|
||||
(function (StackState) {
|
||||
/** The stack is currently re/deploying */
|
||||
StackState["Deploying"] = "deploying";
|
||||
/** All containers are running. */
|
||||
StackState["Running"] = "running";
|
||||
/** All containers are paused */
|
||||
@@ -459,7 +472,7 @@ export var StackState;
|
||||
StackState["Unhealthy"] = "unhealthy";
|
||||
/** The stack is not deployed */
|
||||
StackState["Down"] = "down";
|
||||
/** Server not reachable */
|
||||
/** Server not reachable for status */
|
||||
StackState["Unknown"] = "unknown";
|
||||
})(StackState || (StackState = {}));
|
||||
export var RepoWebhookAction;
|
||||
@@ -527,9 +540,9 @@ export var SpecificPermission;
|
||||
SpecificPermission["Attach"] = "Attach";
|
||||
/**
|
||||
* On **Server**
|
||||
* - Access the `docker inspect` apis
|
||||
* - Access the `container inspect` apis
|
||||
* On **Stack / Deployment**
|
||||
* - Access `docker inspect $container` for associated containers
|
||||
* - Access `container inspect` apis for associated containers
|
||||
*/
|
||||
SpecificPermission["Inspect"] = "Inspect";
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,12 @@ import { RequiredResourceComponents } from "@types";
|
||||
import { Factory, FolderGit, Hammer, Loader2, RefreshCcw } from "lucide-react";
|
||||
import { BuildConfig } from "./config";
|
||||
import { BuildTable } from "./table";
|
||||
import { DeleteResource, NewResource, ResourceLink } from "../common";
|
||||
import {
|
||||
DeleteResource,
|
||||
NewResource,
|
||||
ResourceLink,
|
||||
StandardSource,
|
||||
} from "../common";
|
||||
import { DeploymentTable } from "../deployment/table";
|
||||
import { RunBuild } from "./actions";
|
||||
import {
|
||||
@@ -158,6 +163,34 @@ export const BuildComponents: RequiredResourceComponents = {
|
||||
return <StatusBadge text={state} intent={build_state_intention(state)} />;
|
||||
},
|
||||
|
||||
Info: {
|
||||
Builder: ({ id }) => {
|
||||
const info = useBuild(id)?.info;
|
||||
const builder = useBuilder(info?.builder_id);
|
||||
return builder?.id ? (
|
||||
<ResourceLink type="Builder" id={builder?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
<Factory className="w-4 h-4" />
|
||||
<div>Unknown Builder</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Source: ({ id }) => {
|
||||
const info = useBuild(id)?.info;
|
||||
return <StandardSource info={info} />;
|
||||
},
|
||||
Branch: ({ id }) => {
|
||||
const branch = useBuild(id)?.info.branch;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{branch}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Status: {
|
||||
Hash: ({ id }) => {
|
||||
const info = useFullBuild(id)?.info;
|
||||
@@ -242,39 +275,6 @@ export const BuildComponents: RequiredResourceComponents = {
|
||||
},
|
||||
},
|
||||
|
||||
Info: {
|
||||
Builder: ({ id }) => {
|
||||
const info = useBuild(id)?.info;
|
||||
const builder = useBuilder(info?.builder_id);
|
||||
return builder?.id ? (
|
||||
<ResourceLink type="Builder" id={builder?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
<Factory className="w-4 h-4" />
|
||||
<div>Unknown Builder</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Repo: ({ id }) => {
|
||||
const repo = useBuild(id)?.info.repo;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{repo}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Branch: ({ id }) => {
|
||||
const branch = useBuild(id)?.info.branch;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{branch}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Actions: { RunBuild },
|
||||
|
||||
Page: {},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TableTags } from "@components/tags";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { fmt_version } from "@lib/formatting";
|
||||
import { ResourceLink } from "../common";
|
||||
import { ResourceLink, StandardSource } from "../common";
|
||||
import { BuildComponents } from ".";
|
||||
import { Types } from "komodo_client";
|
||||
import { useSelectedResources } from "@lib/hooks";
|
||||
@@ -19,27 +19,19 @@ export const BuildTable = ({ builds }: { builds: Types.BuildListItem[] }) => {
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
accessorKey: "name",
|
||||
cell: ({ row }) => <ResourceLink type="Build" id={row.original.id} />,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
// header: ({ column }) => (
|
||||
// <SortableHeader column={column} title="Source" />
|
||||
// ),
|
||||
header: "Source",
|
||||
accessorFn: ({ info: { files_on_host, repo } }) => {
|
||||
if (files_on_host) {
|
||||
return "Files on Server";
|
||||
} else if (repo) {
|
||||
return repo;
|
||||
} else {
|
||||
return "UI Defined";
|
||||
}
|
||||
},
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Source" />
|
||||
),
|
||||
accessorKey: "info.repo",
|
||||
cell: ({ row }) => <StandardSource info={row.original.info} />,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,19 +13,53 @@ import {
|
||||
SelectValue,
|
||||
} from "@ui/select";
|
||||
import { Cloud, Bot, Factory } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { BuilderConfig } from "./config";
|
||||
import { DeleteResource, ResourceLink } from "../common";
|
||||
import { BuilderTable } from "./table";
|
||||
import { ResourcePageHeader } from "@components/util";
|
||||
import { GroupActions } from "@components/group-actions";
|
||||
import { useServer } from "../server";
|
||||
import { cn } from "@lib/utils";
|
||||
import {
|
||||
ColorIntention,
|
||||
server_state_intention,
|
||||
stroke_color_class_by_intention,
|
||||
} from "@lib/color";
|
||||
|
||||
export const useBuilder = (id?: string) =>
|
||||
useRead("ListBuilders", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
(d) => d.id === id
|
||||
);
|
||||
|
||||
const Icon = ({ id, size }: { id?: string; size: number }) => {
|
||||
const info = useBuilder(id)?.info;
|
||||
if (info?.builder_type === "Server" && info.instance_type) {
|
||||
return <ServerIcon server_id={info.instance_type} size={size} />;
|
||||
} else {
|
||||
return <Factory className={`w-${size} h-${size}`} />;
|
||||
}
|
||||
};
|
||||
|
||||
const ServerIcon = ({
|
||||
server_id,
|
||||
size,
|
||||
}: {
|
||||
server_id: string;
|
||||
size: number;
|
||||
}) => {
|
||||
const state = useServer(server_id)?.info.state;
|
||||
return (
|
||||
<Factory
|
||||
className={cn(
|
||||
`w-${size} h-${size}`,
|
||||
state && stroke_color_class_by_intention(server_state_intention(state))
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const BuilderInstanceType = ({ id }: { id: string }) => {
|
||||
let info = useBuilder(id)?.info;
|
||||
if (info?.builder_type === "Server") {
|
||||
@@ -35,7 +69,12 @@ export const BuilderInstanceType = ({ id }: { id: string }) => {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return <>{info?.instance_type}</>;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="w-4 h-4" />
|
||||
{info?.instance_type}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,25 +159,23 @@ export const BuilderComponents: RequiredResourceComponents = {
|
||||
<BuilderTable builders={resources as Types.BuilderListItem[]} />
|
||||
),
|
||||
|
||||
Icon: () => <Factory className="w-4 h-4" />,
|
||||
BigIcon: () => <Factory className="w-8 h-8" />,
|
||||
Icon: ({ id }) => <Icon id={id} size={4} />,
|
||||
BigIcon: ({ id }) => <Icon id={id} size={8} />,
|
||||
|
||||
State: () => null,
|
||||
Status: {},
|
||||
|
||||
Info: {
|
||||
Provider: ({ id }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Cloud className="w-4 h-4" />
|
||||
{useBuilder(id)?.info.builder_type}
|
||||
</div>
|
||||
),
|
||||
InstanceType: ({ id }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="w-4 h-4" />
|
||||
<BuilderInstanceType id={id} />
|
||||
</div>
|
||||
),
|
||||
Provider: ({ id }) => {
|
||||
const builder_type = useBuilder(id)?.info.builder_type;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Cloud className="w-4 h-4" />
|
||||
{builder_type}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
InstanceType: ({ id }) => <BuilderInstanceType id={id} />,
|
||||
},
|
||||
|
||||
Actions: {},
|
||||
@@ -151,17 +188,67 @@ export const BuilderComponents: RequiredResourceComponents = {
|
||||
|
||||
ResourcePageHeader: ({ id }) => {
|
||||
const builder = useBuilder(id);
|
||||
|
||||
if (builder?.info.builder_type === "Server" && builder.info.instance_type) {
|
||||
return (
|
||||
<ServerInnerResourcePageHeader
|
||||
builder={builder}
|
||||
server_id={builder.info.instance_type}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ResourcePageHeader
|
||||
intent="None"
|
||||
icon={<Factory className="w-8" />}
|
||||
type="Builder"
|
||||
<InnerResourcePageHeader
|
||||
id={id}
|
||||
name={builder?.name}
|
||||
state={builder?.info.builder_type}
|
||||
status={builder?.info.instance_type}
|
||||
builder={builder}
|
||||
intent="None"
|
||||
icon={<Factory className="w-8 h-8" />}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const ServerInnerResourcePageHeader = ({
|
||||
builder,
|
||||
server_id,
|
||||
}: {
|
||||
builder: Types.BuilderListItem;
|
||||
server_id: string;
|
||||
}) => {
|
||||
const state = useServer(server_id)?.info.state;
|
||||
return (
|
||||
<InnerResourcePageHeader
|
||||
id={builder.id}
|
||||
builder={builder}
|
||||
intent={server_state_intention(state)}
|
||||
icon={<ServerIcon server_id={server_id} size={8} />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const InnerResourcePageHeader = ({
|
||||
id,
|
||||
builder,
|
||||
intent,
|
||||
icon,
|
||||
}: {
|
||||
id: string;
|
||||
builder: Types.BuilderListItem | undefined;
|
||||
intent: ColorIntention;
|
||||
icon: ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<ResourcePageHeader
|
||||
intent={intent}
|
||||
icon={icon}
|
||||
type="Builder"
|
||||
id={id}
|
||||
name={builder?.name}
|
||||
state={builder?.info.builder_type}
|
||||
status={
|
||||
builder?.info.builder_type === "Aws"
|
||||
? builder?.info.instance_type
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
ActionWithDialog,
|
||||
ConfirmButton,
|
||||
CopyButton,
|
||||
RepoLink,
|
||||
TextUpdateMenuSimple,
|
||||
} from "@components/util";
|
||||
import {
|
||||
@@ -29,7 +30,16 @@ import {
|
||||
DialogTrigger,
|
||||
} from "@ui/dialog";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@ui/popover";
|
||||
import { Check, ChevronsUpDown, Copy, SearchX, Trash } from "lucide-react";
|
||||
import {
|
||||
Check,
|
||||
ChevronsUpDown,
|
||||
Copy,
|
||||
Loader2,
|
||||
NotepadText,
|
||||
SearchX,
|
||||
Server,
|
||||
Trash,
|
||||
} from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { ResourceComponents } from ".";
|
||||
@@ -353,6 +363,7 @@ export const DeleteResource = ({
|
||||
}}
|
||||
disabled={isPending}
|
||||
loading={isPending}
|
||||
forceConfirmDialog
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -374,3 +385,36 @@ export const CopyWebhook = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const StandardSource = ({
|
||||
info,
|
||||
}: {
|
||||
info:
|
||||
| {
|
||||
files_on_host: boolean;
|
||||
repo: string;
|
||||
repo_link: string;
|
||||
}
|
||||
| undefined;
|
||||
}) => {
|
||||
if (!info) {
|
||||
return <Loader2 className="w-4 h-4 animate-spin" />;
|
||||
}
|
||||
if (info.files_on_host) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="w-4 h-4" />
|
||||
Files on Server
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (info.repo) {
|
||||
return <RepoLink repo={info.repo} link={info.repo_link} />;
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<NotepadText className="w-4 h-4" />
|
||||
UI Defined
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -169,6 +169,7 @@ export const ImageConfig = ({
|
||||
},
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<BuildVersionSelector
|
||||
buildId={image.params.build_id}
|
||||
|
||||
@@ -245,11 +245,19 @@ export const DeploymentComponents: RequiredResourceComponents = {
|
||||
);
|
||||
},
|
||||
|
||||
Status: {
|
||||
UpdateAvailable: ({ id }) => <UpdateAvailable id={id} />,
|
||||
},
|
||||
|
||||
Info: {
|
||||
Server: ({ id }) => {
|
||||
const info = useDeployment(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
return server?.id ? (
|
||||
<ResourceLink type="Server" id={server?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
<Server className="w-4 h-4" />
|
||||
<div>Unknown Server</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Image: ({ id }) => {
|
||||
const config = useFullDeployment(id)?.config;
|
||||
const info = useDeployment(id)?.info;
|
||||
@@ -271,18 +279,6 @@ export const DeploymentComponents: RequiredResourceComponents = {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Server: ({ id }) => {
|
||||
const info = useDeployment(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
return server?.id ? (
|
||||
<ResourceLink type="Server" id={server?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
<Server className="w-4 h-4" />
|
||||
<div>Unknown Server</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Container: ({ id }) => {
|
||||
const deployment = useDeployment(id);
|
||||
if (
|
||||
@@ -303,6 +299,10 @@ export const DeploymentComponents: RequiredResourceComponents = {
|
||||
},
|
||||
},
|
||||
|
||||
Status: {
|
||||
UpdateAvailable: ({ id }) => <UpdateAvailable id={id} />,
|
||||
},
|
||||
|
||||
Actions: {
|
||||
RunBuild: ({ id }) => {
|
||||
const build_id = useDeployment(id)?.info.build_id;
|
||||
|
||||
@@ -63,8 +63,8 @@ export const DeploymentTable = ({
|
||||
const sb = serverName(b.original.info.server_id);
|
||||
|
||||
if (!sa && !sb) return 0;
|
||||
if (!sa) return -1;
|
||||
if (!sb) return 1;
|
||||
if (!sa) return 1;
|
||||
if (!sb) return -1;
|
||||
|
||||
if (sa > sb) return 1;
|
||||
else if (sa < sb) return -1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useInvalidate, useRead, useWrite } from "@lib/hooks";
|
||||
import { RequiredResourceComponents } from "@types";
|
||||
import { Card } from "@ui/card";
|
||||
import { FolderGit, GitBranch, Loader2, RefreshCcw } from "lucide-react";
|
||||
import { GitBranch, Loader2, RefreshCcw } from "lucide-react";
|
||||
import { RepoConfig } from "./config";
|
||||
import { BuildRepo, CloneRepo, PullRepo } from "./actions";
|
||||
import { DeleteResource, NewResource, ResourceLink } from "../common";
|
||||
@@ -14,7 +14,7 @@ import { cn } from "@lib/utils";
|
||||
import { useServer } from "../server";
|
||||
import { Types } from "komodo_client";
|
||||
import { DashboardPieChart } from "@pages/home/dashboard";
|
||||
import { ResourcePageHeader, StatusBadge } from "@components/util";
|
||||
import { RepoLink, ResourcePageHeader, StatusBadge } from "@components/util";
|
||||
import { Badge } from "@ui/badge";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
import { Button } from "@ui/button";
|
||||
@@ -89,6 +89,43 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
return <StatusBadge text={state} intent={repo_state_intention(state)} />;
|
||||
},
|
||||
|
||||
Info: {
|
||||
Target: ({ id }) => {
|
||||
const info = useRepo(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
const builder = useBuilder(info?.builder_id);
|
||||
return (
|
||||
<div className="flex items-center gap-x-4 gap-y-2 flex-wrap">
|
||||
{server?.id &&
|
||||
(builder?.id ? (
|
||||
<div className="pr-4 text-sm border-r">
|
||||
<ResourceLink type="Server" id={server.id} />
|
||||
</div>
|
||||
) : (
|
||||
<ResourceLink type="Server" id={server.id} />
|
||||
))}
|
||||
{builder?.id && <ResourceLink type="Builder" id={builder.id} />}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Source: ({ id }) => {
|
||||
const info = useRepo(id)?.info;
|
||||
if (!info) {
|
||||
return <Loader2 className="w-4 h-4 animate-spin" />;
|
||||
}
|
||||
return <RepoLink link={info.repo_link} repo={info.repo} />;
|
||||
},
|
||||
Branch: ({ id }) => {
|
||||
const branch = useRepo(id)?.info.branch;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<GitBranch className="w-4 h-4" />
|
||||
{branch}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Status: {
|
||||
Cloned: ({ id }) => {
|
||||
const info = useRepo(id)?.info;
|
||||
@@ -199,45 +236,6 @@ export const RepoComponents: RequiredResourceComponents = {
|
||||
},
|
||||
},
|
||||
|
||||
Info: {
|
||||
Target: ({ id }) => {
|
||||
const info = useRepo(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
const builder = useBuilder(info?.builder_id);
|
||||
return (
|
||||
<div className="flex items-center gap-x-4 gap-y-2 flex-wrap">
|
||||
{server?.id &&
|
||||
(builder?.id ? (
|
||||
<div className="pr-4 text-sm border-r">
|
||||
<ResourceLink type="Server" id={server.id} />
|
||||
</div>
|
||||
) : (
|
||||
<ResourceLink type="Server" id={server.id} />
|
||||
))}
|
||||
{builder?.id && <ResourceLink type="Builder" id={builder.id} />}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Repo: ({ id }) => {
|
||||
const repo = useRepo(id)?.info.repo;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{repo}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Branch: ({ id }) => {
|
||||
const branch = useRepo(id)?.info.branch;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<GitBranch className="w-4 h-4" />
|
||||
{branch}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Actions: { BuildRepo, PullRepo, CloneRepo },
|
||||
|
||||
Page: {},
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TableTags } from "@components/tags";
|
||||
import { RepoComponents } from ".";
|
||||
import { Types } from "komodo_client";
|
||||
import { useSelectedResources } from "@lib/hooks";
|
||||
import { RepoLink } from "@components/util";
|
||||
|
||||
export const RepoTable = ({ repos }: { repos: Types.RepoListItem[] }) => {
|
||||
const [_, setSelectedResources] = useSelectedResources("Repo");
|
||||
@@ -18,32 +19,38 @@ export const RepoTable = ({ repos }: { repos: Types.RepoListItem[] }) => {
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
accessorKey: "name",
|
||||
cell: ({ row }) => <ResourceLink type="Repo" id={row.original.id} />,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.repo",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Repo" />
|
||||
),
|
||||
accessorKey: "info.repo",
|
||||
cell: ({ row }) => (
|
||||
<RepoLink
|
||||
repo={row.original.info.repo}
|
||||
link={row.original.info.repo_link}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.branch",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Branch" />
|
||||
),
|
||||
accessorKey: "info.branch",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.state",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="State" />
|
||||
),
|
||||
accessorKey: "info.state",
|
||||
cell: ({ row }) => <RepoComponents.State id={row.original.id} />,
|
||||
size: 120,
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { atomWithStorage, useLocalStorage, useRead, useUser } from "@lib/hooks";
|
||||
import { RequiredResourceComponents } from "@types";
|
||||
import { Card } from "@ui/card";
|
||||
import { Clock, FolderSync } from "lucide-react";
|
||||
import { DeleteResource, NewResource } from "../common";
|
||||
import { DeleteResource, NewResource, StandardSource } from "../common";
|
||||
import { ResourceSyncTable } from "./table";
|
||||
import { Types } from "komodo_client";
|
||||
import { CommitSync, ExecuteSync, RefreshSync } from "./actions";
|
||||
@@ -174,6 +174,22 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
|
||||
);
|
||||
},
|
||||
|
||||
Info: {
|
||||
Source: ({ id }) => {
|
||||
const info = useResourceSync(id)?.info;
|
||||
return <StandardSource info={info} />;
|
||||
},
|
||||
LastSync: ({ id }) => {
|
||||
const last_ts = useResourceSync(id)?.info.last_sync_ts;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
{last_ts ? fmt_date(new Date(last_ts)) : "Never"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Status: {
|
||||
Hash: ({ id }) => {
|
||||
const info = useFullResourceSync(id)?.info;
|
||||
@@ -232,18 +248,6 @@ export const ResourceSyncComponents: RequiredResourceComponents = {
|
||||
},
|
||||
},
|
||||
|
||||
Info: {
|
||||
LastSync: ({ id }) => {
|
||||
const last_ts = useResourceSync(id)?.info.last_sync_ts;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
{last_ts ? fmt_date(new Date(last_ts)) : "Never"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Actions: { RefreshSync, ExecuteSync, CommitSync },
|
||||
|
||||
Page: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { ResourceLink } from "../common";
|
||||
import { ResourceLink, StandardSource } from "../common";
|
||||
import { TableTags } from "@components/tags";
|
||||
import { Types } from "komodo_client";
|
||||
import { ResourceSyncComponents } from ".";
|
||||
@@ -21,34 +21,35 @@ export const ResourceSyncTable = ({
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
accessorKey: "name",
|
||||
cell: ({ row }) => (
|
||||
<ResourceLink type="ResourceSync" id={row.original.id} />
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.repo",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Repo" />
|
||||
),
|
||||
accessorKey: "info.repo",
|
||||
cell: ({ row }) => <StandardSource info={row.original.info} />,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.branch",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Branch" />
|
||||
),
|
||||
accessorKey: "info.branch",
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.state",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="State" />
|
||||
),
|
||||
accessorKey: "info.state",
|
||||
cell: ({ row }) => (
|
||||
<ResourceSyncComponents.State id={row.original.id} />
|
||||
),
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
RefreshCcw,
|
||||
Pause,
|
||||
Square,
|
||||
AlertCircle,
|
||||
CheckCircle2,
|
||||
} from "lucide-react";
|
||||
import { Section } from "@components/layouts";
|
||||
import { Prune } from "./actions";
|
||||
@@ -40,6 +42,7 @@ import { GroupActions } from "@components/group-actions";
|
||||
import { ServerTerminals } from "@components/terminal/server";
|
||||
import { usePermissions } from "@lib/hooks";
|
||||
import { Card, CardHeader, CardTitle } from "@ui/card";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip";
|
||||
|
||||
export const useServer = (id?: string) =>
|
||||
useRead("ListServers", {}, { refetchInterval: 10_000 }).data?.find(
|
||||
@@ -272,17 +275,56 @@ export const ServerComponents: RequiredResourceComponents = {
|
||||
|
||||
Info: {
|
||||
Version: ({ id }) => {
|
||||
const core_version = useRead("GetVersion", {}).data?.version;
|
||||
const version = useRead(
|
||||
"GetPeripheryVersion",
|
||||
{ server: id },
|
||||
{ refetchInterval: 5000 }
|
||||
).data?.version;
|
||||
const _version =
|
||||
version === undefined || version === "unknown" ? "unknown" : version;
|
||||
const mismatch = !!version && !!core_version && version !== core_version;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center gap-2 cursor-pointer">
|
||||
{mismatch ? (
|
||||
<AlertCircle
|
||||
className={cn(
|
||||
"w-4 h-4",
|
||||
stroke_color_class_by_intention("Critical")
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<CheckCircle2
|
||||
className={cn(
|
||||
"w-4 h-4",
|
||||
stroke_color_class_by_intention("Good")
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{version ?? "Unknown"}
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{mismatch ? (
|
||||
<div>
|
||||
Periphery version <span className="font-bold">mismatch</span>.
|
||||
Expected <span className="font-bold">{core_version}</span>.
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
Periphery and Core version{" "}
|
||||
<span className="font-bold">match</span>.
|
||||
</div>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
if (mismatch) {
|
||||
}
|
||||
return (
|
||||
<div className={cn("flex items-center gap-2")}>
|
||||
<Milestone className="w-4 h-4" />
|
||||
{_version}
|
||||
{version ?? "Unknown"}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -51,8 +51,8 @@ export const ServerTable = ({
|
||||
const sb = resourcesCount(b.original.id);
|
||||
|
||||
if (!sa && !sb) return 0;
|
||||
if (!sa) return -1;
|
||||
if (!sb) return 1;
|
||||
if (!sa) return 1;
|
||||
if (!sb) return -1;
|
||||
|
||||
if (sa > sb) return 1;
|
||||
else if (sa < sb) return -1;
|
||||
|
||||
@@ -9,14 +9,17 @@ import { RequiredResourceComponents } from "@types";
|
||||
import { Card } from "@ui/card";
|
||||
import {
|
||||
CircleArrowUp,
|
||||
FolderGit,
|
||||
Layers,
|
||||
Loader2,
|
||||
NotepadText,
|
||||
RefreshCcw,
|
||||
Server,
|
||||
} from "lucide-react";
|
||||
import { DeleteResource, NewResource, ResourceLink } from "../common";
|
||||
import {
|
||||
DeleteResource,
|
||||
NewResource,
|
||||
ResourceLink,
|
||||
StandardSource,
|
||||
} from "../common";
|
||||
import { StackTable } from "./table";
|
||||
import {
|
||||
border_color_class_by_intention,
|
||||
@@ -207,6 +210,45 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
return <StatusBadge text={state} intent={stack_state_intention(state)} />;
|
||||
},
|
||||
|
||||
Info: {
|
||||
Server: ({ id }) => {
|
||||
const info = useStack(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
return server?.id ? (
|
||||
<ResourceLink type="Server" id={server?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Server className="w-4 h-4" />
|
||||
<div>Unknown Server</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Source: ({ id }) => {
|
||||
const info = useStack(id)?.info;
|
||||
return <StandardSource info={info} />;
|
||||
},
|
||||
// Branch: ({ id }) => {
|
||||
// const config = useFullStack(id)?.config;
|
||||
// const file_contents = config?.file_contents;
|
||||
// if (file_contents || !config?.branch) return null;
|
||||
// return (
|
||||
// <div className="flex items-center gap-2">
|
||||
// <GitBranch className="w-4 h-4" />
|
||||
// {config.branch}
|
||||
// </div>
|
||||
// );
|
||||
// },
|
||||
Services: ({ id }) => {
|
||||
const info = useStack(id)?.info;
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
<div className="font-bold">{info?.services.length}</div>
|
||||
<div>Service{(info?.services.length ?? 0 > 1) ? "s" : ""}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Status: {
|
||||
NoConfig: ({ id }) => {
|
||||
const config = useFullStack(id)?.config;
|
||||
@@ -385,50 +427,6 @@ export const StackComponents: RequiredResourceComponents = {
|
||||
},
|
||||
},
|
||||
|
||||
Info: {
|
||||
Contents: ({ id }) => {
|
||||
const config = useFullStack(id)?.config;
|
||||
const file_contents = config?.file_contents;
|
||||
if (file_contents) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<NotepadText className="w-4 h-4" />
|
||||
Local
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{config?.repo}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
// Branch: ({ id }) => {
|
||||
// const config = useFullStack(id)?.config;
|
||||
// const file_contents = config?.file_contents;
|
||||
// if (file_contents || !config?.branch) return null
|
||||
// return (
|
||||
// <div className="flex items-center gap-2">
|
||||
// <GitBranch className="w-4 h-4" />
|
||||
// {config.branch}
|
||||
// </div>
|
||||
// );
|
||||
// },
|
||||
Server: ({ id }) => {
|
||||
const info = useStack(id)?.info;
|
||||
const server = useServer(info?.server_id);
|
||||
return server?.id ? (
|
||||
<ResourceLink type="Server" id={server?.id} />
|
||||
) : (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Server className="w-4 h-4" />
|
||||
<div>Unknown Server</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Actions: {
|
||||
DeployStack,
|
||||
PullStack,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useRead, useSelectedResources } from "@lib/hooks";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { ResourceLink } from "../common";
|
||||
import { ResourceLink, StandardSource } from "../common";
|
||||
import { TableTags } from "@components/tags";
|
||||
import { StackComponents, UpdateAvailable } from ".";
|
||||
import { Types } from "komodo_client";
|
||||
@@ -25,10 +25,10 @@ export const StackTable = ({ stacks }: { stacks: Types.StackListItem[] }) => {
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Name" />
|
||||
),
|
||||
accessorKey: "name",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
@@ -40,27 +40,35 @@ export const StackTable = ({ stacks }: { stacks: Types.StackListItem[] }) => {
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Server" />
|
||||
),
|
||||
accessorKey: "info.server_id",
|
||||
sortingFn: (a, b) => {
|
||||
const sa = serverName(a.original.info.server_id);
|
||||
const sb = serverName(b.original.info.server_id);
|
||||
|
||||
if (!sa && !sb) return 0;
|
||||
if (!sa) return -1;
|
||||
if (!sb) return 1;
|
||||
if (!sa) return 1;
|
||||
if (!sb) return -1;
|
||||
|
||||
if (sa > sb) return 1;
|
||||
else if (sa < sb) return -1;
|
||||
else return 0;
|
||||
},
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Server" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<ResourceLink type="Server" id={row.original.info.server_id} />
|
||||
),
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Source" />
|
||||
),
|
||||
accessorKey: "info.repo",
|
||||
cell: ({ row }) => <StandardSource info={row.original.info} />,
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
accessorKey: "info.state",
|
||||
header: ({ column }) => (
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { RESOURCE_TARGETS, cn, usableResourcePath } from "@lib/utils";
|
||||
import { SIDEBAR_RESOURCES, cn, usableResourcePath } from "@lib/utils";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
AlertTriangle,
|
||||
Bell,
|
||||
Box,
|
||||
Boxes,
|
||||
CalendarDays,
|
||||
LayoutDashboard,
|
||||
Settings,
|
||||
} from "lucide-react";
|
||||
@@ -43,7 +44,7 @@ export const Sidebar = () => {
|
||||
<Separator className="my-3" />
|
||||
|
||||
<p className="pl-4 pb-1 text-xs text-muted-foreground">Resources</p>
|
||||
{RESOURCE_TARGETS.map((type) => {
|
||||
{SIDEBAR_RESOURCES.map((type) => {
|
||||
const RTIcon = ResourceComponents[type].Icon;
|
||||
const name = type === "ResourceSync" ? "Sync" : type;
|
||||
return (
|
||||
@@ -55,6 +56,7 @@ export const Sidebar = () => {
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<Separator className="my-3" />
|
||||
|
||||
<p className="pl-4 pb-1 text-xs text-muted-foreground">Notifications</p>
|
||||
@@ -68,8 +70,15 @@ export const Sidebar = () => {
|
||||
to="/updates"
|
||||
icon={<Bell className="w-4 h-4" />}
|
||||
/>
|
||||
|
||||
<Separator className="my-3" />
|
||||
|
||||
<SidebarLink
|
||||
label="Schedules"
|
||||
to="/schedules"
|
||||
icon={<CalendarDays className="w-4 h-4" />}
|
||||
/>
|
||||
|
||||
<SidebarLink
|
||||
label="Settings"
|
||||
to="/settings"
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Bell,
|
||||
Box,
|
||||
Boxes,
|
||||
CalendarDays,
|
||||
FileQuestion,
|
||||
FolderTree,
|
||||
Keyboard,
|
||||
@@ -130,15 +131,17 @@ const MobileDropdown = () => {
|
||||
? [<Box className="w-4 h-4" />, "Containers"]
|
||||
: location.pathname === "/settings"
|
||||
? [<Settings className="w-4 h-4" />, "Settings"]
|
||||
: location.pathname === "/alerts"
|
||||
? [<AlertTriangle className="w-4 h-4" />, "Alerts"]
|
||||
: location.pathname === "/updates"
|
||||
? [<Bell className="w-4 h-4" />, "Updates"]
|
||||
: location.pathname.split("/")[1] === "user-groups"
|
||||
? [<Users className="w-4 h-4" />, "User Groups"]
|
||||
: location.pathname.split("/")[1] === "users"
|
||||
? [<User className="w-4 h-4" />, "Users"]
|
||||
: [<FileQuestion className="w-4 h-4" />, "Unknown"];
|
||||
: location.pathname === "/schedules"
|
||||
? [<CalendarDays className="w-4 h-4" />, "Schedules"]
|
||||
: location.pathname === "/alerts"
|
||||
? [<AlertTriangle className="w-4 h-4" />, "Alerts"]
|
||||
: location.pathname === "/updates"
|
||||
? [<Bell className="w-4 h-4" />, "Updates"]
|
||||
: location.pathname.split("/")[1] === "user-groups"
|
||||
? [<Users className="w-4 h-4" />, "User Groups"]
|
||||
: location.pathname.split("/")[1] === "users"
|
||||
? [<User className="w-4 h-4" />, "Users"]
|
||||
: [<FileQuestion className="w-4 h-4" />, "Unknown"];
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@@ -202,6 +205,12 @@ const MobileDropdown = () => {
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownLinkItem
|
||||
label="Schedules"
|
||||
icon={<CalendarDays className="w-4 h-4" />}
|
||||
to="/schedules"
|
||||
/>
|
||||
|
||||
<DropdownLinkItem
|
||||
label="Settings"
|
||||
icon={<Settings className="w-4 h-4" />}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Copy,
|
||||
Database,
|
||||
Edit2,
|
||||
FolderGit,
|
||||
HardDrive,
|
||||
Loader2,
|
||||
LogOut,
|
||||
@@ -140,6 +141,7 @@ export const ActionWithDialog = ({
|
||||
additional,
|
||||
targetClassName,
|
||||
variant,
|
||||
forceConfirmDialog,
|
||||
}: {
|
||||
name: string;
|
||||
title: string;
|
||||
@@ -158,13 +160,18 @@ export const ActionWithDialog = ({
|
||||
| "ghost"
|
||||
| null
|
||||
| undefined;
|
||||
/**
|
||||
* For some ops (Delete), force confirm dialog
|
||||
* even if disabled.
|
||||
*/
|
||||
forceConfirmDialog?: boolean;
|
||||
}) => {
|
||||
const disable_confirm_dialog =
|
||||
useRead("GetCoreInfo", {}).data?.disable_confirm_dialog ?? false;
|
||||
const [open, setOpen] = useState(false);
|
||||
const [input, setInput] = useState("");
|
||||
|
||||
if (disable_confirm_dialog) {
|
||||
if (!forceConfirmDialog && disable_confirm_dialog) {
|
||||
return (
|
||||
<ConfirmButton
|
||||
variant={variant}
|
||||
@@ -1079,3 +1086,18 @@ export const NotFound = ({ type }: { type: UsableResource | undefined }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const RepoLink = ({ repo, link }: { repo: string; link: string }) => {
|
||||
return (
|
||||
<a
|
||||
target="_blank"
|
||||
href={link}
|
||||
className="text-sm cursor-pointer hover:underline"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FolderGit className="w-4 h-4" />
|
||||
{repo}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -130,9 +130,9 @@ export const soft_text_color_class_by_intention = (
|
||||
};
|
||||
|
||||
export const server_state_intention: (
|
||||
status?: Types.ServerState
|
||||
) => ColorIntention = (status) => {
|
||||
switch (status) {
|
||||
state?: Types.ServerState
|
||||
) => ColorIntention = (state) => {
|
||||
switch (state) {
|
||||
case Types.ServerState.Ok:
|
||||
return "Good";
|
||||
case Types.ServerState.NotOk:
|
||||
@@ -150,6 +150,8 @@ export const deployment_state_intention: (
|
||||
switch (state) {
|
||||
case undefined:
|
||||
return "None";
|
||||
case Types.DeploymentState.Deploying:
|
||||
return "Warning";
|
||||
case Types.DeploymentState.Running:
|
||||
return "Good";
|
||||
case Types.DeploymentState.NotDeployed:
|
||||
@@ -222,6 +224,8 @@ export const stack_state_intention = (state?: Types.StackState) => {
|
||||
switch (state) {
|
||||
case undefined:
|
||||
return "None";
|
||||
case Types.StackState.Deploying:
|
||||
return "Warning";
|
||||
case Types.StackState.Running:
|
||||
return "Good";
|
||||
case Types.StackState.Paused:
|
||||
|
||||
@@ -278,6 +278,7 @@ const on_update = (
|
||||
|
||||
if (update.target.type === "Procedure") {
|
||||
invalidate(
|
||||
["ListSchedules"],
|
||||
["ListProcedures"],
|
||||
["ListFullProcedures"],
|
||||
["GetProceduresSummary"],
|
||||
@@ -287,6 +288,7 @@ const on_update = (
|
||||
|
||||
if (update.target.type === "Action") {
|
||||
invalidate(
|
||||
["ListSchedules"],
|
||||
["ListActions"],
|
||||
["ListFullActions"],
|
||||
["GetActionsSummary"],
|
||||
|
||||
@@ -26,6 +26,12 @@ export const RESOURCE_TARGETS: UsableResource[] = [
|
||||
"ResourceSync",
|
||||
];
|
||||
|
||||
export const SETTINGS_RESOURCES: UsableResource[] = ["Builder", "Alerter"];
|
||||
|
||||
export const SIDEBAR_RESOURCES: UsableResource[] = RESOURCE_TARGETS.filter(
|
||||
(target) => !SETTINGS_RESOURCES.includes(target)
|
||||
);
|
||||
|
||||
export function env_to_text(envVars: Types.EnvironmentVar[] | undefined) {
|
||||
return envVars?.reduce(
|
||||
(prev, { variable, value }) =>
|
||||
|
||||
@@ -29,17 +29,18 @@ export const homeViewAtom = atomWithStorage<HomeView>(
|
||||
"Dashboard"
|
||||
);
|
||||
|
||||
init_monaco().then(() =>
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
// <React.StrictMode>
|
||||
<QueryClientProvider client={query_client}>
|
||||
<WebsocketProvider>
|
||||
<ThemeProvider>
|
||||
<Router />
|
||||
<Toaster />
|
||||
</ThemeProvider>
|
||||
</WebsocketProvider>
|
||||
</QueryClientProvider>
|
||||
// </React.StrictMode>
|
||||
)
|
||||
// Don't need to await this to render.
|
||||
init_monaco();
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
// <React.StrictMode>
|
||||
<QueryClientProvider client={query_client}>
|
||||
<WebsocketProvider>
|
||||
<ThemeProvider>
|
||||
<Router />
|
||||
<Toaster />
|
||||
</ThemeProvider>
|
||||
</WebsocketProvider>
|
||||
</QueryClientProvider>
|
||||
// </React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ const FALLBACK_ALERT_TYPES = [
|
||||
"AwsBuilderTerminationFailed",
|
||||
];
|
||||
|
||||
export const AlertsPage = () => {
|
||||
export default function AlertsPage() {
|
||||
const [page, setPage] = useState(0);
|
||||
const [params, setParams] = useSearchParams();
|
||||
|
||||
@@ -211,4 +211,4 @@ export const AlertsPage = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Input } from "@ui/input";
|
||||
import { Box, Search } from "lucide-react";
|
||||
import { Fragment, useCallback, useMemo, useState } from "react";
|
||||
|
||||
export const ContainersPage = () => {
|
||||
export default function ContainersPage() {
|
||||
const [search, setSearch] = useState("");
|
||||
const searchSplit = search
|
||||
.toLowerCase()
|
||||
@@ -183,4 +183,4 @@ export const ContainersPage = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Input } from "@ui/input";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export const AllResources = () => {
|
||||
export default function AllResources() {
|
||||
const [search, setSearch] = useState("");
|
||||
const tags = useTagsFilter();
|
||||
const noResources = useNoResources();
|
||||
@@ -63,7 +63,7 @@ export const AllResources = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const TableSection = ({
|
||||
type,
|
||||
|
||||
@@ -24,7 +24,7 @@ import { Link } from "react-router-dom";
|
||||
import { UpdateAvailable as StackUpdateAvailable } from "@components/resources/stack";
|
||||
import { UpdateAvailable as DeploymentUpdateAvailable } from "@components/resources/deployment";
|
||||
|
||||
export const Dashboard = () => {
|
||||
export default function Dashboard() {
|
||||
const noResources = useNoResources();
|
||||
const user = useUser().data!;
|
||||
return (
|
||||
@@ -59,7 +59,7 @@ export const Dashboard = () => {
|
||||
</Page>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const ResourceRow = ({ type }: { type: UsableResource }) => {
|
||||
const _recents = useUser().data?.recents?.[type]?.slice(0, 6);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { homeViewAtom } from "@main";
|
||||
import { useAtom } from "jotai";
|
||||
import { Dashboard } from "./dashboard";
|
||||
import { AllResources } from "./all_resources";
|
||||
import { Tree } from "./tree";
|
||||
import { useSetTitle } from "@lib/hooks";
|
||||
import { lazy } from "react";
|
||||
|
||||
export const Home = () => {
|
||||
const Dashboard = lazy(() => import("./dashboard"));
|
||||
const AllResources = lazy(() => import("./all_resources"));
|
||||
const Tree = lazy(() => import("./tree"));
|
||||
|
||||
export default function Home() {
|
||||
useSetTitle();
|
||||
const [view] = useAtom(homeViewAtom);
|
||||
switch (view) {
|
||||
@@ -16,4 +18,4 @@ export const Home = () => {
|
||||
case "Tree":
|
||||
return <Tree />;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Link } from "react-router-dom";
|
||||
|
||||
const searchAtom = atom("");
|
||||
|
||||
export const Tree = () => {
|
||||
export default function Tree() {
|
||||
const [search, setSearch] = useAtom(searchAtom);
|
||||
const tags = useTagsFilter();
|
||||
const servers = useRead("ListServers", { query: { tags } }).data;
|
||||
@@ -37,14 +37,12 @@ export const Tree = () => {
|
||||
>
|
||||
<Section>
|
||||
<div className="grid gap-6">
|
||||
{servers?.map((server) => (
|
||||
<Server key={server.id} id={server.id} />
|
||||
))}
|
||||
{servers?.map((server) => <Server key={server.id} id={server.id} />)}
|
||||
</div>
|
||||
</Section>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const Server = ({ id }: { id: string }) => {
|
||||
const [search] = useAtom(searchAtom);
|
||||
|
||||
@@ -62,7 +62,7 @@ const useExchangeToken = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const Login = () => {
|
||||
export default function Login() {
|
||||
const options = useLoginOptions().data;
|
||||
const [creds, set] = useState({ username: "", password: "" });
|
||||
const userInvalidate = useUserInvalidate();
|
||||
@@ -253,4 +253,4 @@ export const Login = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
useResourceParamType,
|
||||
useSetTitle,
|
||||
} from "@lib/hooks";
|
||||
import { usableResourcePath } from "@lib/utils";
|
||||
import { SETTINGS_RESOURCES, usableResourcePath } from "@lib/utils";
|
||||
import { Types } from "komodo_client";
|
||||
import { UsableResource } from "@types";
|
||||
import { Button } from "@ui/button";
|
||||
@@ -22,14 +22,14 @@ import { Link, useParams } from "react-router-dom";
|
||||
import { ResourceNotifications } from "./resource-notifications";
|
||||
import { NotFound } from "@components/util";
|
||||
|
||||
export const Resource = () => {
|
||||
export default function Resource() {
|
||||
const type = useResourceParamType()!;
|
||||
const id = useParams().id as string;
|
||||
|
||||
if (!type || !id) return null;
|
||||
|
||||
return <ResourceInner type={type} id={id} />;
|
||||
};
|
||||
}
|
||||
|
||||
const ResourceInner = ({ type, id }: { type: UsableResource; id: string }) => {
|
||||
const resources = useRead(`List${type}s`, {}).data;
|
||||
@@ -59,7 +59,14 @@ const ResourceInner = ({ type, id }: { type: UsableResource; id: string }) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="w-full flex items-center justify-between mb-12">
|
||||
<Link to={"/" + usableResourcePath(type)}>
|
||||
<Link
|
||||
to={
|
||||
"/" +
|
||||
(SETTINGS_RESOURCES.includes(type)
|
||||
? "settings"
|
||||
: usableResourcePath(type))
|
||||
}
|
||||
>
|
||||
<Button className="gap-2" variant="secondary">
|
||||
<ChevronLeft className="w-4" />
|
||||
Back
|
||||
@@ -138,20 +145,24 @@ export const ResourceHeader = ({
|
||||
{statusEntries.map(([key, Status]) => (
|
||||
<Status key={key} id={id} />
|
||||
))}
|
||||
{links?.map((link) => (
|
||||
<a
|
||||
key={link}
|
||||
target="_blank"
|
||||
href={link}
|
||||
className="flex gap-2 items-center pr-4 text-sm border-r cursor-pointer hover:underline last:pr-0 last:border-none"
|
||||
>
|
||||
<LinkIcon className="w-4" />
|
||||
<div className="max-w-[150px] lg:max-w-[250px] overflow-hidden overflow-ellipsis">
|
||||
{link}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{links && links.length > 0 && (
|
||||
<div className="flex items-center gap-x-4 gap-y-2 flex-wrap px-4 py-0">
|
||||
{links?.map((link) => (
|
||||
<a
|
||||
key={link}
|
||||
target="_blank"
|
||||
href={link}
|
||||
className="flex gap-2 items-center pr-4 text-sm border-r cursor-pointer hover:underline last:pr-0 last:border-none"
|
||||
>
|
||||
<LinkIcon className="w-4" />
|
||||
<div className="max-w-[150px] lg:max-w-[250px] text-nowrap overflow-hidden overflow-ellipsis">
|
||||
{link}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 flex-wrap p-4 pt-0">
|
||||
<p className="text-sm text-muted-foreground">Tags:</p>
|
||||
<ResourceTags
|
||||
|
||||
@@ -16,12 +16,14 @@ import { useState } from "react";
|
||||
import { Search } from "lucide-react";
|
||||
import { NotFound } from "@components/util";
|
||||
import { Switch } from "@ui/switch";
|
||||
import { UsableResource } from "@types";
|
||||
|
||||
export const Resources = () => {
|
||||
export default function Resources({ _type }: { _type?: UsableResource }) {
|
||||
const is_admin = useUser().data?.admin ?? false;
|
||||
const disable_non_admin_create =
|
||||
useRead("GetCoreInfo", {}).data?.disable_non_admin_create ?? true;
|
||||
const type = useResourceParamType()!;
|
||||
const __type = useResourceParamType()!;
|
||||
const type = _type ? _type : __type;
|
||||
const name = type === "ResourceSync" ? "Resource Sync" : type;
|
||||
useSetTitle(name + "s");
|
||||
const [search, set] = useState("");
|
||||
@@ -94,4 +96,4 @@ export const Resources = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
146
frontend/src/pages/schedules.tsx
Normal file
146
frontend/src/pages/schedules.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import { Page } from "@components/layouts";
|
||||
import { ResourceLink } from "@components/resources/common";
|
||||
import { TableTags, TagsFilter } from "@components/tags";
|
||||
import {
|
||||
usePermissions,
|
||||
useRead,
|
||||
useSetTitle,
|
||||
useTags,
|
||||
useWrite,
|
||||
} from "@lib/hooks";
|
||||
import { filterBySplit } from "@lib/utils";
|
||||
import { UsableResource } from "@types";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { Input } from "@ui/input";
|
||||
import { Switch } from "@ui/switch";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
import { CalendarDays, Search } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SchedulesPage() {
|
||||
useSetTitle("Schedules");
|
||||
const [search, set] = useState("");
|
||||
const { tags } = useTags();
|
||||
const schedules = useRead("ListSchedules", { tags }).data;
|
||||
const filtered = filterBySplit(schedules ?? [], search, (item) => item.name);
|
||||
return (
|
||||
<Page
|
||||
icon={<CalendarDays className="w-8" />}
|
||||
title="Schedules"
|
||||
subtitle={
|
||||
<div className="text-muted-foreground">
|
||||
See an overview of your scheduled tasks.
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-wrap gap-4 items-center justify-end">
|
||||
<div className="flex items-center gap-4 flex-wrap">
|
||||
<TagsFilter />
|
||||
<div className="relative">
|
||||
<Search className="w-4 absolute top-[50%] left-3 -translate-y-[50%] text-muted-foreground" />
|
||||
<Input
|
||||
value={search}
|
||||
onChange={(e) => set(e.target.value)}
|
||||
placeholder="search..."
|
||||
className="pl-8 w-[200px] lg:w-[300px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DataTable
|
||||
tableKey="schedules"
|
||||
data={filtered}
|
||||
columns={[
|
||||
{
|
||||
size: 200,
|
||||
accessorKey: "name",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Target" />
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<ResourceLink
|
||||
type={row.original.target.type as UsableResource}
|
||||
id={row.original.target.id}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
size: 200,
|
||||
accessorKey: "schedule",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Schedule" />
|
||||
),
|
||||
},
|
||||
{
|
||||
size: 200,
|
||||
accessorKey: "next_scheduled_run",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Next Run" />
|
||||
),
|
||||
sortingFn: (a, b) => {
|
||||
const sa = a.original.next_scheduled_run;
|
||||
const sb = b.original.next_scheduled_run;
|
||||
|
||||
if (!sa && !sb) return 0;
|
||||
if (!sa) return 1;
|
||||
if (!sb) return -1;
|
||||
|
||||
if (sa > sb) return 1;
|
||||
else if (sa < sb) return -1;
|
||||
else return 0;
|
||||
},
|
||||
cell: ({ row }) =>
|
||||
row.original.next_scheduled_run
|
||||
? new Date(row.original.next_scheduled_run).toLocaleString()
|
||||
: "Not Scheduled",
|
||||
},
|
||||
{
|
||||
size: 100,
|
||||
accessorKey: "enabled",
|
||||
header: ({ column }) => (
|
||||
<SortableHeader column={column} title="Enabled" />
|
||||
),
|
||||
cell: ({ row: { original: schedule } }) => (
|
||||
<ScheduleEnableSwitch
|
||||
type={schedule.target.type as UsableResource}
|
||||
id={schedule.target.id}
|
||||
enabled={schedule.enabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Tags",
|
||||
cell: ({ row }) => <TableTags tag_ids={row.original.tags} />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
const ScheduleEnableSwitch = ({
|
||||
type,
|
||||
id,
|
||||
enabled,
|
||||
}: {
|
||||
type: UsableResource;
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
}) => {
|
||||
const { canWrite } = usePermissions({ type, id });
|
||||
const { toast } = useToast();
|
||||
const { mutate } = useWrite(`Update${type}`, {
|
||||
onSuccess: () => toast({ title: "Updated Schedule enabled." }),
|
||||
});
|
||||
return (
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onCheckedChange={(enabled) =>
|
||||
mutate({ id, config: { schedule_enabled: enabled } })
|
||||
}
|
||||
disabled={!canWrite}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -31,7 +31,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
|
||||
import { ContainerTerminal } from "@components/terminal/container";
|
||||
import { ContainerInspect } from "./inspect";
|
||||
|
||||
export const ContainerPage = () => {
|
||||
export default function ContainerPage() {
|
||||
const { type, id, container } = useParams() as {
|
||||
type: string;
|
||||
id: string;
|
||||
@@ -43,7 +43,7 @@ export const ContainerPage = () => {
|
||||
return (
|
||||
<ContainerPageInner id={id} container={decodeURIComponent(container)} />
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const ContainerPageInner = ({
|
||||
id,
|
||||
|
||||
@@ -27,7 +27,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import { MonacoEditor } from "@components/monaco";
|
||||
|
||||
export const ImagePage = () => {
|
||||
export default function ImagePage() {
|
||||
const { type, id, image } = useParams() as {
|
||||
type: string;
|
||||
id: string;
|
||||
@@ -37,7 +37,7 @@ export const ImagePage = () => {
|
||||
return <div>This resource type does not have any images.</div>;
|
||||
}
|
||||
return <ImagePageInner id={id} image={decodeURIComponent(image)} />;
|
||||
};
|
||||
}
|
||||
|
||||
const ImagePageInner = ({
|
||||
id,
|
||||
|
||||
@@ -28,7 +28,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import { MonacoEditor } from "@components/monaco";
|
||||
|
||||
export const NetworkPage = () => {
|
||||
export default function NetworkPage() {
|
||||
const { type, id, network } = useParams() as {
|
||||
type: string;
|
||||
id: string;
|
||||
@@ -38,7 +38,7 @@ export const NetworkPage = () => {
|
||||
return <div>This resource type does not have any networks.</div>;
|
||||
}
|
||||
return <NetworkPageInner id={id} network={decodeURIComponent(network)} />;
|
||||
};
|
||||
}
|
||||
|
||||
const NetworkPageInner = ({
|
||||
id,
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import { MonacoEditor } from "@components/monaco";
|
||||
|
||||
export const VolumePage = () => {
|
||||
export default function VolumePage() {
|
||||
const { type, id, volume } = useParams() as {
|
||||
type: string;
|
||||
id: string;
|
||||
@@ -30,7 +30,7 @@ export const VolumePage = () => {
|
||||
return <div>This resource type does not have any volumes.</div>;
|
||||
}
|
||||
return <VolumePageInner id={id} volume={decodeURIComponent(volume)} />;
|
||||
};
|
||||
}
|
||||
|
||||
const VolumePageInner = ({
|
||||
id,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useAtom } from "jotai";
|
||||
import { atomWithStorage, useUser } from "@lib/hooks";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
|
||||
import { Page } from "@components/layouts";
|
||||
import { ExportButton } from "@components/export";
|
||||
import Resources from "@pages/resources";
|
||||
import { Variables } from "./variables";
|
||||
import { Tags } from "./tags";
|
||||
import { UsersPage } from "./users";
|
||||
import { Profile } from "./profile";
|
||||
import { Page } from "@components/layouts";
|
||||
import { ProvidersPage } from "./providers";
|
||||
import { ExportButton } from "@components/export";
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
type SettingsView = "Variables" | "Tags" | "Providers" | "Users" | "Profile";
|
||||
|
||||
@@ -15,7 +16,7 @@ const viewAtom = atomWithStorage<SettingsView>("settings-view-v2", "Variables");
|
||||
|
||||
export const useSettingsView = () => useAtom<SettingsView>(viewAtom);
|
||||
|
||||
export const Settings = () => {
|
||||
export default function Settings() {
|
||||
const user = useUser().data;
|
||||
const [view, setView] = useSettingsView();
|
||||
const currentView =
|
||||
@@ -33,6 +34,8 @@ export const Settings = () => {
|
||||
<TabsList className="justify-start w-fit">
|
||||
<TabsTrigger value="Variables">Variables</TabsTrigger>
|
||||
<TabsTrigger value="Tags">Tags</TabsTrigger>
|
||||
<TabsTrigger value="Builders">Builders</TabsTrigger>
|
||||
<TabsTrigger value="Alerters">Alerters</TabsTrigger>
|
||||
{user?.admin && (
|
||||
<TabsTrigger value="Providers">Providers</TabsTrigger>
|
||||
)}
|
||||
@@ -49,6 +52,12 @@ export const Settings = () => {
|
||||
<TabsContent value="Tags">
|
||||
<Tags />
|
||||
</TabsContent>
|
||||
<TabsContent value="Builders">
|
||||
<Resources _type="Builder" />
|
||||
</TabsContent>
|
||||
<TabsContent value="Alerters">
|
||||
<Resources _type="Alerter" />
|
||||
</TabsContent>
|
||||
{user?.admin && (
|
||||
<TabsContent value="Providers">
|
||||
<ProvidersPage />
|
||||
@@ -65,4 +74,4 @@ export const Settings = () => {
|
||||
</Tabs>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const Actions: { [action: string]: IdServiceComponent } = {
|
||||
DestroyStack,
|
||||
};
|
||||
|
||||
export const StackServicePage = () => {
|
||||
export default function StackServicePage() {
|
||||
const { type, id, service } = useParams() as {
|
||||
type: string;
|
||||
id: string;
|
||||
@@ -58,7 +58,7 @@ export const StackServicePage = () => {
|
||||
return <div>This resource type does not have any services.</div>;
|
||||
}
|
||||
return <StackServicePageInner stack_id={id} service={service} />;
|
||||
};
|
||||
}
|
||||
|
||||
const StackServicePageInner = ({
|
||||
stack_id,
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
} from "@ui/select";
|
||||
import { ResourceSelector } from "@components/resources/common";
|
||||
|
||||
export const UpdatesPage = () => {
|
||||
export default function UpdatesPage() {
|
||||
const [page, setPage] = useState(0);
|
||||
const [params, setParams] = useSearchParams();
|
||||
|
||||
@@ -168,7 +168,7 @@ export const UpdatesPage = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const Updates = () => {
|
||||
const type = useResourceParamType()!;
|
||||
|
||||
@@ -23,7 +23,7 @@ import { useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Switch } from "@ui/switch";
|
||||
|
||||
export const UserGroupPage = () => {
|
||||
export default function UserGroupPage() {
|
||||
const { toast } = useToast();
|
||||
const inv = useInvalidate();
|
||||
const group_id = useParams().id as string;
|
||||
@@ -143,7 +143,7 @@ export const UserGroupPage = () => {
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const AddUserToGroup = ({ group_id }: { group_id: string }) => {
|
||||
const inv = useInvalidate();
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Link, useParams } from "react-router-dom";
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardContent, CardHeader } from "@ui/card";
|
||||
|
||||
export const UserPage = () => {
|
||||
export default function UserPage() {
|
||||
const admin_user = useUser().data;
|
||||
const { toast } = useToast();
|
||||
const inv = useInvalidate();
|
||||
@@ -128,7 +128,7 @@ export const UserPage = () => {
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const ApiKeysTable = ({ user_id }: { user_id: string }) => {
|
||||
const keys = useRead("ListApiKeysForServiceUser", { user: user_id }).data;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AUTH_TOKEN_STORAGE_KEY } from "@main";
|
||||
import { Button } from "@ui/button";
|
||||
import { UserX } from "lucide-react";
|
||||
|
||||
export const UserDisabled = () => {
|
||||
export default function UserDisabled() {
|
||||
return (
|
||||
<div className="w-full h-screen flex justify-center items-center">
|
||||
<div className="flex flex-col gap-4 justify-center items-center">
|
||||
@@ -20,4 +20,4 @@ export const UserDisabled = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,73 +1,29 @@
|
||||
import { Layout } from "@components/layouts";
|
||||
import { useUser } from "@lib/hooks";
|
||||
import { Login } from "@pages/login";
|
||||
import { Resource } from "@pages/resource";
|
||||
import { Resources } from "@pages/resources";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
import { Tree } from "@pages/home/tree";
|
||||
import { UpdatesPage } from "@pages/updates";
|
||||
import { AllResources } from "@pages/home/all_resources";
|
||||
import { UserDisabled } from "@pages/user_disabled";
|
||||
import { Home } from "@pages/home";
|
||||
import { AlertsPage } from "@pages/alerts";
|
||||
import { UserPage } from "@pages/user";
|
||||
import { UserGroupPage } from "@pages/user-group";
|
||||
import { Settings } from "@pages/settings";
|
||||
import { StackServicePage } from "@pages/stack-service";
|
||||
import { NetworkPage } from "@pages/server-info/network";
|
||||
import { ImagePage } from "@pages/server-info/image";
|
||||
import { VolumePage } from "@pages/server-info/volume";
|
||||
import { ContainerPage } from "@pages/server-info/container";
|
||||
import { ContainersPage } from "@pages/containers";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||
|
||||
const ROUTER = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <Layout />,
|
||||
children: [
|
||||
{ path: "", element: <Home /> },
|
||||
{ path: "settings", element: <Settings /> },
|
||||
{ path: "tree", element: <Tree /> },
|
||||
{ path: "alerts", element: <AlertsPage /> },
|
||||
{ path: "updates", element: <UpdatesPage /> },
|
||||
{ path: "updates", element: <UpdatesPage /> },
|
||||
{ path: "containers", element: <ContainersPage /> },
|
||||
{ path: "resources", element: <AllResources /> },
|
||||
{ path: "user-groups/:id", element: <UserGroupPage /> },
|
||||
{
|
||||
path: "users",
|
||||
children: [{ path: ":id", element: <UserPage /> }],
|
||||
},
|
||||
{
|
||||
path: ":type",
|
||||
children: [
|
||||
{ path: "", element: <Resources /> },
|
||||
{ path: ":id", element: <Resource /> },
|
||||
{
|
||||
path: ":id/service/:service",
|
||||
element: <StackServicePage />,
|
||||
},
|
||||
{
|
||||
path: ":id/container/:container",
|
||||
element: <ContainerPage />,
|
||||
},
|
||||
{
|
||||
path: ":id/network/:network",
|
||||
element: <NetworkPage />,
|
||||
},
|
||||
{
|
||||
path: ":id/image/:image",
|
||||
element: <ImagePage />,
|
||||
},
|
||||
{
|
||||
path: ":id/volume/:volume",
|
||||
element: <VolumePage />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
// Lazy import pages
|
||||
const Resources = lazy(() => import("@pages/resources"));
|
||||
const Resource = lazy(() => import("@pages/resource"));
|
||||
const Login = lazy(() => import("@pages/login"));
|
||||
const Tree = lazy(() => import("@pages/home/tree"));
|
||||
const UpdatesPage = lazy(() => import("@pages/updates"));
|
||||
const AllResources = lazy(() => import("@pages/home/all_resources"));
|
||||
const UserDisabled = lazy(() => import("@pages/user_disabled"));
|
||||
const Home = lazy(() => import("@pages/home"));
|
||||
const AlertsPage = lazy(() => import("@pages/alerts"));
|
||||
const UserPage = lazy(() => import("@pages/user"));
|
||||
const UserGroupPage = lazy(() => import("@pages/user-group"));
|
||||
const Settings = lazy(() => import("@pages/settings"));
|
||||
const StackServicePage = lazy(() => import("@pages/stack-service"));
|
||||
const NetworkPage = lazy(() => import("@pages/server-info/network"));
|
||||
const ImagePage = lazy(() => import("@pages/server-info/image"));
|
||||
const VolumePage = lazy(() => import("@pages/server-info/volume"));
|
||||
const ContainerPage = lazy(() => import("@pages/server-info/container"));
|
||||
const ContainersPage = lazy(() => import("@pages/containers"));
|
||||
const SchedulesPage = lazy(() => import("@pages/schedules"));
|
||||
|
||||
export const Router = () => {
|
||||
const { data: user, isLoading, error } = useUser();
|
||||
@@ -76,5 +32,47 @@ export const Router = () => {
|
||||
if (!user || error) return <Login />;
|
||||
if (!user.enabled) return <UserDisabled />;
|
||||
|
||||
return <RouterProvider router={ROUTER} />;
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="w-[100vw] h-[100vh] flex items-center justify-center">
|
||||
<Loader2 className="w-16 h-16 animate-spin" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route path="" element={<Home />} />
|
||||
<Route path="settings" element={<Settings />} />
|
||||
<Route path="tree" element={<Tree />} />
|
||||
<Route path="alerts" element={<AlertsPage />} />
|
||||
<Route path="updates" element={<UpdatesPage />} />
|
||||
<Route path="containers" element={<ContainersPage />} />
|
||||
<Route path="resources" element={<AllResources />} />
|
||||
<Route path="schedules" element={<SchedulesPage />} />
|
||||
<Route path="user-groups/:id" element={<UserGroupPage />} />
|
||||
<Route path="users/:id" element={<UserPage />} />
|
||||
<Route path=":type">
|
||||
<Route path="" element={<Resources />} />
|
||||
<Route path=":id" element={<Resource />} />
|
||||
<Route
|
||||
path=":id/service/:service"
|
||||
element={<StackServicePage />}
|
||||
/>
|
||||
<Route
|
||||
path=":id/container/:container"
|
||||
element={<ContainerPage />}
|
||||
/>
|
||||
<Route path=":id/network/:network" element={<NetworkPage />} />
|
||||
<Route path=":id/image/:image" element={<ImagePage />} />
|
||||
<Route path=":id/volume/:volume" element={<VolumePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
// return <RouterProvider router={ROUTER} />;
|
||||
};
|
||||
|
||||
@@ -117,7 +117,7 @@ pub async fn commit_file_inner(
|
||||
let push_log = run_komodo_command(
|
||||
"Push",
|
||||
repo_dir,
|
||||
format!("git push -f --set-upstream origin {branch}"),
|
||||
format!("git push --set-upstream origin {branch}"),
|
||||
)
|
||||
.await;
|
||||
res.logs.push(push_log);
|
||||
@@ -170,7 +170,7 @@ pub async fn commit_all(
|
||||
let push_log = run_komodo_command(
|
||||
"Push",
|
||||
repo_dir,
|
||||
format!("git push -f --set-upstream origin {branch}"),
|
||||
format!("git push --set-upstream origin {branch}"),
|
||||
)
|
||||
.await;
|
||||
res.logs.push(push_log);
|
||||
|
||||
@@ -54,10 +54,11 @@ where
|
||||
T: Into<CloneArgs> + std::fmt::Debug,
|
||||
{
|
||||
let args: CloneArgs = clone_args.into();
|
||||
let folder_path = args.path(repo_dir);
|
||||
let repo_dir = args.path(repo_dir);
|
||||
let repo_url = args.remote_url(access_token.as_deref())?;
|
||||
|
||||
// Acquire the path lock
|
||||
let lock = pull_cache().get_lock(folder_path.clone()).await;
|
||||
let lock = pull_cache().get_lock(repo_dir.clone()).await;
|
||||
|
||||
// Lock the path lock, prevents simultaneous pulls by
|
||||
// ensuring simultaneous pulls will wait for first to finish
|
||||
@@ -73,10 +74,10 @@ where
|
||||
let mut logs = Vec::new();
|
||||
|
||||
// Check for '.git' path to see if the folder is initialized as a git repo
|
||||
let dot_git_path = folder_path.join(".git");
|
||||
let dot_git_path = repo_dir.join(".git");
|
||||
if !dot_git_path.exists() {
|
||||
crate::init::init_folder_as_repo(
|
||||
&folder_path,
|
||||
&repo_dir,
|
||||
&args,
|
||||
access_token.as_deref(),
|
||||
&mut logs,
|
||||
@@ -92,12 +93,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let repo_url = args.remote_url(access_token.as_deref())?;
|
||||
|
||||
// Set remote url
|
||||
let mut set_remote = run_komodo_command(
|
||||
"Set git remote",
|
||||
folder_path.as_ref(),
|
||||
repo_dir.as_ref(),
|
||||
format!("git remote set-url origin {repo_url}"),
|
||||
)
|
||||
.await;
|
||||
@@ -122,7 +121,7 @@ where
|
||||
|
||||
let checkout = run_komodo_command(
|
||||
"Checkout branch",
|
||||
folder_path.as_ref(),
|
||||
repo_dir.as_ref(),
|
||||
format!("git checkout -f {}", args.branch),
|
||||
)
|
||||
.await;
|
||||
@@ -138,7 +137,7 @@ where
|
||||
|
||||
let pull_log = run_komodo_command(
|
||||
"Git pull",
|
||||
folder_path.as_ref(),
|
||||
repo_dir.as_ref(),
|
||||
format!("git pull --rebase --force origin {}", args.branch),
|
||||
)
|
||||
.await;
|
||||
@@ -155,7 +154,7 @@ where
|
||||
if let Some(commit) = args.commit {
|
||||
let reset_log = run_komodo_command(
|
||||
"Set commit",
|
||||
folder_path.as_ref(),
|
||||
repo_dir.as_ref(),
|
||||
format!("git reset --hard {commit}"),
|
||||
)
|
||||
.await;
|
||||
@@ -163,7 +162,7 @@ where
|
||||
}
|
||||
|
||||
let (hash, message) =
|
||||
match get_commit_hash_log(&folder_path).await {
|
||||
match get_commit_hash_log(&repo_dir).await {
|
||||
Ok((log, hash, message)) => {
|
||||
logs.push(log);
|
||||
(Some(hash), Some(message))
|
||||
@@ -184,7 +183,7 @@ where
|
||||
environment,
|
||||
env_file_path,
|
||||
secrets,
|
||||
&folder_path,
|
||||
&repo_dir,
|
||||
&mut logs,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Runfile | https://crates.io/crates/runnables-cli
|
||||
|
||||
[dev-frontend]
|
||||
description = "starts the frontend in dev mode"
|
||||
path = "frontend"
|
||||
|
||||
Reference in New Issue
Block a user