mirror of
https://github.com/moghtech/komodo.git
synced 2026-10-31 05:05:57 -05:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a5290e124 | ||
|
|
538e11ef3b | ||
|
|
9208e9bf86 | ||
|
|
31640e3df8 | ||
|
|
42751d1b90 | ||
|
|
673ab5c17d | ||
|
|
ad00f38758 | ||
|
|
b2459b5a64 | ||
|
|
5923dd0e22 | ||
|
|
51294edd73 |
Generated
+13
-13
@@ -1131,7 +1131,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "command"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"komodo_client",
|
||||
"shlex",
|
||||
@@ -1462,7 +1462,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
|
||||
|
||||
[[package]]
|
||||
name = "database"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@@ -1732,7 +1732,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "encoding"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -1774,7 +1774,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"formatting",
|
||||
@@ -1903,7 +1903,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "formatting"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"mogh_error",
|
||||
]
|
||||
@@ -2069,7 +2069,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "git"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"command",
|
||||
@@ -2663,7 +2663,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "interpolate"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -2789,7 +2789,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_cli"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2817,7 +2817,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_client"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2856,7 +2856,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_core"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -2929,7 +2929,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_periphery"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -3977,7 +3977,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"encoding",
|
||||
@@ -5881,7 +5881,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "transport"
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
||||
+1
-4
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.0.0-dev-111"
|
||||
version = "2.0.0-dev-112"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -18,9 +18,6 @@ homepage = "https://komo.do"
|
||||
[profile.release]
|
||||
strip = "debuginfo"
|
||||
|
||||
# [patch.crates-io]
|
||||
# mogh_logger = { path = "../lib/crates/logger" }
|
||||
|
||||
[workspace.dependencies]
|
||||
# LOCAL
|
||||
komodo_client = { path = "client/core/rs" }
|
||||
|
||||
@@ -18,7 +18,7 @@ RUN cargo build -p komodo_core --release && \
|
||||
cargo strip
|
||||
|
||||
# Build Frontend
|
||||
FROM node:20.12-alpine AS frontend-builder
|
||||
FROM node:22.12-alpine AS frontend-builder
|
||||
WORKDIR /builder
|
||||
COPY ./frontend ./frontend
|
||||
COPY ./client/core/ts ./client
|
||||
|
||||
@@ -7,7 +7,7 @@ ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
FROM ${BINARIES_IMAGE} AS binaries
|
||||
|
||||
# Build Frontend
|
||||
FROM node:20.12-alpine AS frontend-builder
|
||||
FROM node:22.12-alpine AS frontend-builder
|
||||
WORKDIR /builder
|
||||
COPY ./frontend ./frontend
|
||||
COPY ./client/core/ts ./client
|
||||
|
||||
@@ -422,6 +422,10 @@ pub async fn check_deployment_for_update_inner(
|
||||
|
||||
let (image, account, token) = match deployment.config.image {
|
||||
DeploymentImage::Image { image } => {
|
||||
if image.contains('@') {
|
||||
// Images with a hardcoded digest can't have update.
|
||||
return Ok(false)
|
||||
}
|
||||
let domain = extract_registry_domain(&image)?;
|
||||
let account =
|
||||
optional_string(&deployment.config.image_registry_account);
|
||||
|
||||
@@ -750,7 +750,10 @@ pub async fn check_stack_for_update_inner(
|
||||
let cache = image_digest_cache();
|
||||
|
||||
for service in &mut stack.info.latest_services {
|
||||
if service.image.is_empty() {
|
||||
if service.image.is_empty() ||
|
||||
// Images with a hardcoded digest can't have update.
|
||||
service.image.contains('@')
|
||||
{
|
||||
service.image_digest = None;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::Context as _;
|
||||
use formatting::format_serror;
|
||||
use komodo_client::entities::{
|
||||
docker::config::SwarmConfig, update::Log,
|
||||
docker::config::SwarmConfigDetails, update::Log,
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::swarm::*;
|
||||
@@ -17,7 +17,7 @@ impl Resolve<crate::api::Args> for InspectSwarmConfig {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &crate::api::Args,
|
||||
) -> anyhow::Result<SwarmConfig> {
|
||||
) -> anyhow::Result<SwarmConfigDetails> {
|
||||
inspect_swarm_config(&self.config).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use futures_util::{TryStreamExt as _, stream::FuturesUnordered};
|
||||
use komodo_client::entities::{
|
||||
all_logs_success,
|
||||
docker::{
|
||||
config::{SwarmConfig, SwarmConfigListItem},
|
||||
config::{SwarmConfigDetails, SwarmConfigListItem},
|
||||
service::{SwarmService, SwarmServiceListItem},
|
||||
},
|
||||
random_string,
|
||||
@@ -65,7 +65,7 @@ pub async fn list_swarm_configs(
|
||||
|
||||
pub async fn inspect_swarm_config(
|
||||
config: &str,
|
||||
) -> anyhow::Result<SwarmConfig> {
|
||||
) -> anyhow::Result<SwarmConfigDetails> {
|
||||
let res = run_komodo_standard_command(
|
||||
"Inspect Swarm Config",
|
||||
None,
|
||||
@@ -79,14 +79,15 @@ pub async fn inspect_swarm_config(
|
||||
)));
|
||||
}
|
||||
|
||||
let mut res = serde_json::from_str::<Vec<SwarmConfig>>(&res.stdout)
|
||||
.context(
|
||||
"Failed to parse 'docker config inspect' response from json",
|
||||
)?
|
||||
.pop()
|
||||
.with_context(|| {
|
||||
format!("Did not find any config matching {config}")
|
||||
})?;
|
||||
let mut res =
|
||||
serde_json::from_str::<Vec<SwarmConfigDetails>>(&res.stdout)
|
||||
.context(
|
||||
"Failed to parse 'docker config inspect' response from json",
|
||||
)?
|
||||
.pop()
|
||||
.with_context(|| {
|
||||
format!("Did not find any config matching {config}")
|
||||
})?;
|
||||
|
||||
// Convert data back to readable / editable format
|
||||
res.spec.iter_mut().next().map(|spec| {
|
||||
|
||||
@@ -5,7 +5,7 @@ use typeshare::typeshare;
|
||||
use crate::entities::{
|
||||
SearchCombinator, U64,
|
||||
docker::{
|
||||
config::{SwarmConfig, SwarmConfigListItem},
|
||||
config::{SwarmConfigDetails, SwarmConfigListItem},
|
||||
network::NetworkListItem,
|
||||
node::{SwarmNode, SwarmNodeListItem},
|
||||
secret::{SwarmSecret, SwarmSecretListItem},
|
||||
@@ -643,7 +643,7 @@ pub struct InspectSwarmConfig {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type InspectSwarmConfigResponse = SwarmConfig;
|
||||
pub type InspectSwarmConfigResponse = SwarmConfigDetails;
|
||||
|
||||
//
|
||||
|
||||
|
||||
@@ -37,12 +37,16 @@ pub struct SwarmConfigListItem {
|
||||
}
|
||||
|
||||
/// Swarm config details.
|
||||
///
|
||||
/// This would be just "SwarmConfig", but that would
|
||||
/// conflict with the Swarm (Komodo resource) Config type,
|
||||
/// which is also SwarmConfig.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
pub struct SwarmConfig {
|
||||
pub struct SwarmConfigDetails {
|
||||
#[serde(rename = "ID")]
|
||||
pub id: Option<String>,
|
||||
|
||||
|
||||
+58
-52
@@ -2716,43 +2716,19 @@ export interface SwarmActionState {
|
||||
|
||||
export type GetSwarmActionStateResponse = SwarmActionState;
|
||||
|
||||
export type U64 = number;
|
||||
|
||||
/** The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. The client must send the version number along with the modified specification when updating these objects. This approach ensures safe concurrency and determinism in that the change on the object may not be applied if the version number has changed from the last read. In other words, if two update requests specify the same base version, only one of the requests can succeed. As a result, two separate update requests that happen at the same time will not unintentionally overwrite each other. */
|
||||
export interface ObjectVersion {
|
||||
Index?: U64;
|
||||
}
|
||||
|
||||
/** Driver represents a driver (network, logging, secrets). */
|
||||
export interface Driver {
|
||||
/** Name of the driver. */
|
||||
Name: string;
|
||||
/** Key/value map of driver-specific options. */
|
||||
Options?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ConfigSpec {
|
||||
/** User-defined name of the config. */
|
||||
Name?: string;
|
||||
/** User-defined key/value metadata. */
|
||||
Labels?: Record<string, string>;
|
||||
/**
|
||||
* Data is the data to store as a config, formatted as a Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
* It must be empty if the Driver field is set, in which case the data is loaded from an external secret store.
|
||||
* The maximum allowed size is 500KB, as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
|
||||
*/
|
||||
Data?: string;
|
||||
/** Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template. If no driver is set, no templating is used. */
|
||||
Templating?: Driver;
|
||||
}
|
||||
|
||||
/** Swarm config details. */
|
||||
export interface SwarmConfig {
|
||||
ID?: string;
|
||||
Version?: ObjectVersion;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
Spec?: ConfigSpec;
|
||||
/**
|
||||
* The Servers which are swarm manager nodes.
|
||||
* If a Server is not reachable or gives error,
|
||||
* tries the next Server.
|
||||
*/
|
||||
server_ids?: string[];
|
||||
/** Configure quick links that are displayed in the resource header */
|
||||
links?: string[];
|
||||
/** Whether to send alerts about the swarm health. */
|
||||
send_unhealthy_alerts: boolean;
|
||||
/** Scheduled maintenance windows during which alerts will be suppressed. */
|
||||
maintenance_windows?: MaintenanceWindow[];
|
||||
}
|
||||
|
||||
export interface SwarmInfo {
|
||||
@@ -3530,6 +3506,13 @@ export enum SwarmState {
|
||||
Unknown = "Unknown",
|
||||
}
|
||||
|
||||
export type U64 = number;
|
||||
|
||||
/** The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. The client must send the version number along with the modified specification when updating these objects. This approach ensures safe concurrency and determinism in that the change on the object may not be applied if the version number has changed from the last read. In other words, if two update requests specify the same base version, only one of the requests can succeed. As a result, two separate update requests that happen at the same time will not unintentionally overwrite each other. */
|
||||
export interface ObjectVersion {
|
||||
Index?: U64;
|
||||
}
|
||||
|
||||
/** Describes a permission the user has to accept upon installing the plugin. */
|
||||
export interface PluginPrivilege {
|
||||
Name?: string;
|
||||
@@ -4516,7 +4499,45 @@ export type InspectStackSwarmInfoResponse = SwarmStack;
|
||||
|
||||
export type InspectStackSwarmServiceResponse = SwarmService;
|
||||
|
||||
export type InspectSwarmConfigResponse = SwarmConfig;
|
||||
/** Driver represents a driver (network, logging, secrets). */
|
||||
export interface Driver {
|
||||
/** Name of the driver. */
|
||||
Name: string;
|
||||
/** Key/value map of driver-specific options. */
|
||||
Options?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ConfigSpec {
|
||||
/** User-defined name of the config. */
|
||||
Name?: string;
|
||||
/** User-defined key/value metadata. */
|
||||
Labels?: Record<string, string>;
|
||||
/**
|
||||
* Data is the data to store as a config, formatted as a Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
* It must be empty if the Driver field is set, in which case the data is loaded from an external secret store.
|
||||
* The maximum allowed size is 500KB, as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
|
||||
*/
|
||||
Data?: string;
|
||||
/** Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template. If no driver is set, no templating is used. */
|
||||
Templating?: Driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swarm config details.
|
||||
*
|
||||
* This would be just "SwarmConfig", but that would
|
||||
* conflict with the Swarm (Komodo resource) Config type,
|
||||
* which is also SwarmConfig.
|
||||
*/
|
||||
export interface SwarmConfigDetails {
|
||||
ID?: string;
|
||||
Version?: ObjectVersion;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
Spec?: ConfigSpec;
|
||||
}
|
||||
|
||||
export type InspectSwarmConfigResponse = SwarmConfigDetails;
|
||||
|
||||
export enum NodeSpecRoleEnum {
|
||||
EMPTY = "",
|
||||
@@ -9910,21 +9931,6 @@ export interface StopStack {
|
||||
services?: string[];
|
||||
}
|
||||
|
||||
export interface SwarmConfig {
|
||||
/**
|
||||
* The Servers which are swarm manager nodes.
|
||||
* If a Server is not reachable or gives error,
|
||||
* tries the next Server.
|
||||
*/
|
||||
server_ids?: string[];
|
||||
/** Configure quick links that are displayed in the resource header */
|
||||
links?: string[];
|
||||
/** Whether to send alerts about the swarm health. */
|
||||
send_unhealthy_alerts: boolean;
|
||||
/** Scheduled maintenance windows during which alerts will be suppressed. */
|
||||
maintenance_windows?: MaintenanceWindow[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Swarm stack service list item.
|
||||
* Returned by `docker stack services --format json <NAME>`
|
||||
|
||||
@@ -5,7 +5,7 @@ use komodo_client::entities::{
|
||||
deployment::Deployment,
|
||||
docker::{
|
||||
SwarmLists,
|
||||
config::SwarmConfig,
|
||||
config::SwarmConfigDetails,
|
||||
node::{NodeSpecAvailabilityEnum, NodeSpecRoleEnum, SwarmNode},
|
||||
secret::SwarmSecret,
|
||||
service::SwarmService,
|
||||
@@ -265,7 +265,7 @@ pub struct InspectSwarmTask {
|
||||
// ========
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
|
||||
#[response(SwarmConfig)]
|
||||
#[response(SwarmConfigDetails)]
|
||||
#[error(anyhow::Error)]
|
||||
pub struct InspectSwarmConfig {
|
||||
pub config: String,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:20.12-alpine AS builder
|
||||
FROM node:22.12-alpine AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
|
||||
+45
-44
@@ -11,66 +11,67 @@
|
||||
"build-client": "cd ../client/core/ts && yarn && yarn build && yarn link"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "0.27.9",
|
||||
"@floating-ui/react": "0.27.16",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@radix-ui/react-checkbox": "1.3.2",
|
||||
"@radix-ui/react-dialog": "1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.15",
|
||||
"@radix-ui/react-hover-card": "1.1.14",
|
||||
"@radix-ui/react-checkbox": "1.3.3",
|
||||
"@radix-ui/react-dialog": "1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.16",
|
||||
"@radix-ui/react-hover-card": "1.1.15",
|
||||
"@radix-ui/react-icons": "1.3.2",
|
||||
"@radix-ui/react-label": "2.1.7",
|
||||
"@radix-ui/react-popover": "1.1.14",
|
||||
"@radix-ui/react-progress": "1.1.7",
|
||||
"@radix-ui/react-select": "2.2.5",
|
||||
"@radix-ui/react-separator": "1.1.7",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-switch": "1.2.5",
|
||||
"@radix-ui/react-tabs": "1.1.12",
|
||||
"@radix-ui/react-toast": "1.2.14",
|
||||
"@radix-ui/react-toggle": "1.1.9",
|
||||
"@radix-ui/react-toggle-group": "1.1.10",
|
||||
"@tanstack/react-query": "5.77.2",
|
||||
"@radix-ui/react-label": "2.1.8",
|
||||
"@radix-ui/react-popover": "1.1.15",
|
||||
"@radix-ui/react-progress": "1.1.8",
|
||||
"@radix-ui/react-select": "2.2.6",
|
||||
"@radix-ui/react-separator": "1.1.8",
|
||||
"@radix-ui/react-slot": "1.2.4",
|
||||
"@radix-ui/react-switch": "1.2.6",
|
||||
"@radix-ui/react-tabs": "1.1.13",
|
||||
"@radix-ui/react-toast": "1.2.15",
|
||||
"@radix-ui/react-toggle": "1.1.10",
|
||||
"@radix-ui/react-toggle-group": "1.1.11",
|
||||
"@tanstack/react-query": "5.90.20",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@xterm/addon-fit": "0.10.0",
|
||||
"@xterm/xterm": "5.5.0",
|
||||
"@types/node": "^25.0.10",
|
||||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "6.0.0",
|
||||
"ansi-to-html": "0.7.2",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
"cmdk": "1.1.1",
|
||||
"jotai": "2.12.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lucide-react": "0.511.0",
|
||||
"monaco-editor": "0.52.2",
|
||||
"jotai": "2.16.2",
|
||||
"lucide-react": "0.563.0",
|
||||
"monaco-editor": "0.55.1",
|
||||
"monaco-yaml": "5.4.0",
|
||||
"prettier": "3.5.3",
|
||||
"react": "19.1.0",
|
||||
"prettier": "3.8.1",
|
||||
"react": "19.2.3",
|
||||
"react-charts": "3.0.0-beta.57",
|
||||
"react-dom": "19.1.0",
|
||||
"react-minimal-pie-chart": "9.1.0",
|
||||
"react-router-dom": "7.6.1",
|
||||
"react-dom": "19.2.3",
|
||||
"react-minimal-pie-chart": "9.1.2",
|
||||
"react-router-dom": "7.13.0",
|
||||
"react-xtermjs": "1.0.10",
|
||||
"sanitize-html": "2.17.0",
|
||||
"shell-quote": "1.8.1",
|
||||
"shell-quote": "1.8.3",
|
||||
"tailwind-merge": "2.6.0",
|
||||
"tailwindcss-animate": "1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "19.1.6",
|
||||
"@types/react-dom": "19.1.5",
|
||||
"@types/react": "19.2.9",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/sanitize-html": "2.16.0",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.33.0",
|
||||
"@typescript-eslint/parser": "8.33.0",
|
||||
"@vitejs/plugin-react": "4.5.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"eslint": "9.27.0",
|
||||
"eslint-plugin-react-hooks": "5.2.0",
|
||||
"eslint-plugin-react-refresh": "0.4.20",
|
||||
"postcss": "8.5.3",
|
||||
"tailwindcss": "3.4.17",
|
||||
"typescript": "5.8.3",
|
||||
"vite": "6.0.7",
|
||||
"vite-tsconfig-paths": "5.1.4"
|
||||
"@types/shell-quote": "1.7.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.53.1",
|
||||
"@typescript-eslint/parser": "8.53.1",
|
||||
"@vitejs/plugin-react": "5.1.2",
|
||||
"autoprefixer": "10.4.23",
|
||||
"dotenv": "17.2.3",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-plugin-react-hooks": "7.0.1",
|
||||
"eslint-plugin-react-refresh": "0.4.26",
|
||||
"postcss": "8.5.6",
|
||||
"tailwindcss": "3.4.19",
|
||||
"typescript": "5.9.3",
|
||||
"vite": "7.3.1",
|
||||
"vite-tsconfig-paths": "6.0.5"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
Vendored
+53
-47
@@ -2852,39 +2852,19 @@ export type GetStackResponse = Stack;
|
||||
export interface SwarmActionState {
|
||||
}
|
||||
export type GetSwarmActionStateResponse = SwarmActionState;
|
||||
export type U64 = number;
|
||||
/** The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. The client must send the version number along with the modified specification when updating these objects. This approach ensures safe concurrency and determinism in that the change on the object may not be applied if the version number has changed from the last read. In other words, if two update requests specify the same base version, only one of the requests can succeed. As a result, two separate update requests that happen at the same time will not unintentionally overwrite each other. */
|
||||
export interface ObjectVersion {
|
||||
Index?: U64;
|
||||
}
|
||||
/** Driver represents a driver (network, logging, secrets). */
|
||||
export interface Driver {
|
||||
/** Name of the driver. */
|
||||
Name: string;
|
||||
/** Key/value map of driver-specific options. */
|
||||
Options?: Record<string, string>;
|
||||
}
|
||||
export interface ConfigSpec {
|
||||
/** User-defined name of the config. */
|
||||
Name?: string;
|
||||
/** User-defined key/value metadata. */
|
||||
Labels?: Record<string, string>;
|
||||
/**
|
||||
* Data is the data to store as a config, formatted as a Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
* It must be empty if the Driver field is set, in which case the data is loaded from an external secret store.
|
||||
* The maximum allowed size is 500KB, as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
|
||||
*/
|
||||
Data?: string;
|
||||
/** Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template. If no driver is set, no templating is used. */
|
||||
Templating?: Driver;
|
||||
}
|
||||
/** Swarm config details. */
|
||||
export interface SwarmConfig {
|
||||
ID?: string;
|
||||
Version?: ObjectVersion;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
Spec?: ConfigSpec;
|
||||
/**
|
||||
* The Servers which are swarm manager nodes.
|
||||
* If a Server is not reachable or gives error,
|
||||
* tries the next Server.
|
||||
*/
|
||||
server_ids?: string[];
|
||||
/** Configure quick links that are displayed in the resource header */
|
||||
links?: string[];
|
||||
/** Whether to send alerts about the swarm health. */
|
||||
send_unhealthy_alerts: boolean;
|
||||
/** Scheduled maintenance windows during which alerts will be suppressed. */
|
||||
maintenance_windows?: MaintenanceWindow[];
|
||||
}
|
||||
export interface SwarmInfo {
|
||||
}
|
||||
@@ -3608,6 +3588,11 @@ export declare enum SwarmState {
|
||||
/** Unknown case */
|
||||
Unknown = "Unknown"
|
||||
}
|
||||
export type U64 = number;
|
||||
/** The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. The client must send the version number along with the modified specification when updating these objects. This approach ensures safe concurrency and determinism in that the change on the object may not be applied if the version number has changed from the last read. In other words, if two update requests specify the same base version, only one of the requests can succeed. As a result, two separate update requests that happen at the same time will not unintentionally overwrite each other. */
|
||||
export interface ObjectVersion {
|
||||
Index?: U64;
|
||||
}
|
||||
/** Describes a permission the user has to accept upon installing the plugin. */
|
||||
export interface PluginPrivilege {
|
||||
Name?: string;
|
||||
@@ -4500,7 +4485,42 @@ export interface SwarmStack {
|
||||
}
|
||||
export type InspectStackSwarmInfoResponse = SwarmStack;
|
||||
export type InspectStackSwarmServiceResponse = SwarmService;
|
||||
export type InspectSwarmConfigResponse = SwarmConfig;
|
||||
/** Driver represents a driver (network, logging, secrets). */
|
||||
export interface Driver {
|
||||
/** Name of the driver. */
|
||||
Name: string;
|
||||
/** Key/value map of driver-specific options. */
|
||||
Options?: Record<string, string>;
|
||||
}
|
||||
export interface ConfigSpec {
|
||||
/** User-defined name of the config. */
|
||||
Name?: string;
|
||||
/** User-defined key/value metadata. */
|
||||
Labels?: Record<string, string>;
|
||||
/**
|
||||
* Data is the data to store as a config, formatted as a Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
* It must be empty if the Driver field is set, in which case the data is loaded from an external secret store.
|
||||
* The maximum allowed size is 500KB, as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
|
||||
*/
|
||||
Data?: string;
|
||||
/** Templating driver, if applicable Templating controls whether and how to evaluate the config payload as a template. If no driver is set, no templating is used. */
|
||||
Templating?: Driver;
|
||||
}
|
||||
/**
|
||||
* Swarm config details.
|
||||
*
|
||||
* This would be just "SwarmConfig", but that would
|
||||
* conflict with the Swarm (Komodo resource) Config type,
|
||||
* which is also SwarmConfig.
|
||||
*/
|
||||
export interface SwarmConfigDetails {
|
||||
ID?: string;
|
||||
Version?: ObjectVersion;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
Spec?: ConfigSpec;
|
||||
}
|
||||
export type InspectSwarmConfigResponse = SwarmConfigDetails;
|
||||
export declare enum NodeSpecRoleEnum {
|
||||
EMPTY = "",
|
||||
WORKER = "worker",
|
||||
@@ -9362,20 +9382,6 @@ export interface StopStack {
|
||||
*/
|
||||
services?: string[];
|
||||
}
|
||||
export interface SwarmConfig {
|
||||
/**
|
||||
* The Servers which are swarm manager nodes.
|
||||
* If a Server is not reachable or gives error,
|
||||
* tries the next Server.
|
||||
*/
|
||||
server_ids?: string[];
|
||||
/** Configure quick links that are displayed in the resource header */
|
||||
links?: string[];
|
||||
/** Whether to send alerts about the swarm health. */
|
||||
send_unhealthy_alerts: boolean;
|
||||
/** Scheduled maintenance windows during which alerts will be suppressed. */
|
||||
maintenance_windows?: MaintenanceWindow[];
|
||||
}
|
||||
/**
|
||||
* Swarm stack service list item.
|
||||
* Returned by `docker stack services --format json <NAME>`
|
||||
|
||||
@@ -131,7 +131,7 @@ export const Terminal = ({
|
||||
debounce = setTimeout(() => {
|
||||
if (!viewport) return;
|
||||
viewport.scrollTop = viewport.scrollHeight - viewport.clientHeight;
|
||||
}, 500);
|
||||
}, 500) as any;
|
||||
});
|
||||
},
|
||||
on_close: () => {
|
||||
|
||||
@@ -68,7 +68,7 @@ export const useLogin = <
|
||||
return useMutation({
|
||||
mutationKey: [type],
|
||||
mutationFn: (params: P) => komodo_client().auth.login<T, R>(type, params),
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, v, c) => {
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, ...args) => {
|
||||
console.log("Login error:", e);
|
||||
const msg = e.result.error ?? "Unknown error. See console.";
|
||||
const detail = e.result?.trace
|
||||
@@ -83,7 +83,7 @@ export const useLogin = <
|
||||
description: `${msg_log}See console for details`,
|
||||
variant: "destructive",
|
||||
});
|
||||
config?.onError && config.onError(e, v, c);
|
||||
config?.onError && config.onError(e, ...args);
|
||||
},
|
||||
...config,
|
||||
});
|
||||
@@ -105,7 +105,7 @@ export const useManageAuth = <
|
||||
return useMutation({
|
||||
mutationKey: [type],
|
||||
mutationFn: (params: P) => komodo_client().auth.manage<T, R>(type, params),
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, v, c) => {
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, ...args) => {
|
||||
console.log("Manage auth error:", e);
|
||||
const msg = e.result.error ?? "Unknown error. See console.";
|
||||
const detail = e.result?.trace
|
||||
@@ -120,7 +120,7 @@ export const useManageAuth = <
|
||||
description: `${msg_log}See console for details`,
|
||||
variant: "destructive",
|
||||
});
|
||||
config?.onError && config.onError(e, v, c);
|
||||
config?.onError && config.onError(e, ...args);
|
||||
},
|
||||
...config,
|
||||
});
|
||||
@@ -267,7 +267,7 @@ export const useWrite = <
|
||||
return useMutation({
|
||||
mutationKey: [type],
|
||||
mutationFn: (params: P) => komodo_client().write<T, R>(type, params),
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, v, c) => {
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, ...args) => {
|
||||
console.log("Write error:", e);
|
||||
const msg = e.result.error ?? "Unknown error. See console.";
|
||||
const detail = e.result?.trace
|
||||
@@ -282,7 +282,7 @@ export const useWrite = <
|
||||
description: `${msg_log}See console for details`,
|
||||
variant: "destructive",
|
||||
});
|
||||
config?.onError && config.onError(e, v, c);
|
||||
config?.onError && config.onError(e, ...args);
|
||||
},
|
||||
...config,
|
||||
});
|
||||
@@ -304,7 +304,7 @@ export const useExecute = <
|
||||
return useMutation({
|
||||
mutationKey: [type],
|
||||
mutationFn: (params: P) => komodo_client().execute<T, R>(type, params),
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, v, c) => {
|
||||
onError: (e: { result: { error?: string; trace?: string[] } }, ...args) => {
|
||||
console.log("Execute error:", e);
|
||||
const msg = e.result.error ?? "Unknown error. See console.";
|
||||
const detail = e.result?.trace
|
||||
@@ -319,7 +319,7 @@ export const useExecute = <
|
||||
description: `${msg_log}See console for details`,
|
||||
variant: "destructive",
|
||||
});
|
||||
config?.onError && config.onError(e, v, c);
|
||||
config?.onError && config.onError(e, ...args);
|
||||
},
|
||||
...config,
|
||||
});
|
||||
|
||||
+16
-16
@@ -40,7 +40,7 @@ const onMessageHandlers: {
|
||||
|
||||
export const useWebsocketMessages = (
|
||||
key: string,
|
||||
handler: (update: Types.UpdateListItem) => void
|
||||
handler: (update: Types.UpdateListItem) => void,
|
||||
) => {
|
||||
onMessageHandlers[key] = handler;
|
||||
useEffect(() => {
|
||||
@@ -66,7 +66,7 @@ export const WebsocketProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const on_update_fn = useCallback(
|
||||
(update: Types.UpdateListItem) => on_update(update, invalidate),
|
||||
[invalidate]
|
||||
[invalidate],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -89,7 +89,7 @@ export const WebsocketProvider = ({ children }: { children: ReactNode }) => {
|
||||
console.info(count, "| Automatically triggering reconnect");
|
||||
reconnect();
|
||||
}
|
||||
}, 5_000);
|
||||
}, 5_000) as any;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -103,7 +103,7 @@ export const WebsocketProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const on_update = (
|
||||
update: Types.UpdateListItem,
|
||||
invalidate: ReturnType<typeof useInvalidate>
|
||||
invalidate: ReturnType<typeof useInvalidate>,
|
||||
) => {
|
||||
const Components = ResourceComponents[update.target.type as UsableResource];
|
||||
const title = Components ? (
|
||||
@@ -178,7 +178,7 @@ const on_update = (
|
||||
["InspectDockerImage"],
|
||||
["ListDockerVolumes"],
|
||||
["InspectDockerVolume"],
|
||||
["GetResourceMatchingContainer"]
|
||||
["GetResourceMatchingContainer"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ const on_update = (
|
||||
["ListSwarmConfigs"],
|
||||
["InspectSwarmConfig"],
|
||||
["ListSwarmSecrets"],
|
||||
["InspectSwarmSecret"]
|
||||
["InspectSwarmSecret"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ const on_update = (
|
||||
["GetServersSummary"],
|
||||
["GetServer"],
|
||||
["GetServerState"],
|
||||
["GetHistoricalServerStats"]
|
||||
["GetHistoricalServerStats"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ const on_update = (
|
||||
["SearchStackLog", { stack: update.target.id }],
|
||||
["GetStack"],
|
||||
["ListStackServices"],
|
||||
["GetResourceMatchingContainer"]
|
||||
["GetResourceMatchingContainer"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ const on_update = (
|
||||
["GetDeploymentLog", { deployment: update.target.id }],
|
||||
["SearchDeploymentLog", { deployment: update.target.id }],
|
||||
["GetDeploymentContainer"],
|
||||
["GetResourceMatchingContainer"]
|
||||
["GetResourceMatchingContainer"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ const on_update = (
|
||||
["GetBuildsSummary"],
|
||||
["GetBuildMonthlyStats"],
|
||||
["GetBuild"],
|
||||
["ListBuildVersions"]
|
||||
["ListBuildVersions"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ const on_update = (
|
||||
["ListRepos"],
|
||||
["ListFullRepos"],
|
||||
["GetReposSummary"],
|
||||
["GetRepo"]
|
||||
["GetRepo"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ const on_update = (
|
||||
["ListProcedures"],
|
||||
["ListFullProcedures"],
|
||||
["GetProceduresSummary"],
|
||||
["GetProcedure"]
|
||||
["GetProcedure"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ const on_update = (
|
||||
["ListActions"],
|
||||
["ListFullActions"],
|
||||
["GetActionsSummary"],
|
||||
["GetAction"]
|
||||
["GetAction"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ const on_update = (
|
||||
["ListResourceSyncs"],
|
||||
["ListFullResourceSyncs"],
|
||||
["GetResourceSyncsSummary"],
|
||||
["GetResourceSync"]
|
||||
["GetResourceSync"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ const on_update = (
|
||||
["ListBuilders"],
|
||||
["ListFullBuilders"],
|
||||
["GetBuildersSummary"],
|
||||
["GetBuilder"]
|
||||
["GetBuilder"],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ const on_update = (
|
||||
["ListAlerters"],
|
||||
["ListFullAlerters"],
|
||||
["GetAlertersSummary"],
|
||||
["GetAlerter"]
|
||||
["GetAlerter"],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -323,41 +323,4 @@ export const terminalLink = ({
|
||||
case "Deployment":
|
||||
return `/deployments/${target.params.deployment}/terminal/${name}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const base64urlToArrayBuffer = (base64url: any) => {
|
||||
// Convert from URL-safe base64 to normal base64
|
||||
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
|
||||
const pad =
|
||||
base64.length % 4 === 0 ? "" : "=".repeat(4 - (base64.length % 4));
|
||||
const bstr = atob(base64 + pad);
|
||||
const bytes = new Uint8Array(bstr.length);
|
||||
for (let i = 0; i < bstr.length; i++) {
|
||||
bytes[i] = bstr.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
};
|
||||
|
||||
export const arrayBufferToBase64url = (buffer: any) => {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = "";
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
const base64 = btoa(binary);
|
||||
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
||||
};
|
||||
|
||||
export const preparePasskeyCredential = (data: any) => {
|
||||
return {
|
||||
...data,
|
||||
publicKey: {
|
||||
...data.publicKey,
|
||||
challenge: base64urlToArrayBuffer(data.publicKey.challenge),
|
||||
allowCredentials: data.publicKey.allowCredentials?.map((cred: any) => ({
|
||||
...cred,
|
||||
id: base64urlToArrayBuffer(cred.id),
|
||||
})),
|
||||
},
|
||||
};
|
||||
};
|
||||
};
|
||||
+18
-18
@@ -7,13 +7,13 @@ export async function init_monaco() {
|
||||
fetch(`/client/${file}${extension}`)
|
||||
.then((res) => res.text())
|
||||
.then((dts) =>
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
monaco.typescript.typescriptDefaults.addExtraLib(
|
||||
dts,
|
||||
`file:///client/${file}${extension}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
`file:///client/${file}${extension}`,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
promises.push(
|
||||
Promise.all(
|
||||
@@ -21,13 +21,13 @@ export async function init_monaco() {
|
||||
fetch(`/${file}`)
|
||||
.then((res) => res.text())
|
||||
.then((dts) =>
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
monaco.typescript.typescriptDefaults.addExtraLib(
|
||||
dts,
|
||||
`file:///${file}`
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
`file:///${file}`,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -37,17 +37,17 @@ export async function init_monaco() {
|
||||
moduleDetection?: "force" | "auto" | "legacy" | 3 | 2 | 1; // string or numeric enum
|
||||
};
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
module: monaco.languages.typescript.ModuleKind.ESNext,
|
||||
target: monaco.languages.typescript.ScriptTarget.ESNext,
|
||||
monaco.typescript.typescriptDefaults.setCompilerOptions({
|
||||
module: monaco.typescript.ModuleKind.ESNext,
|
||||
target: monaco.typescript.ScriptTarget.ESNext,
|
||||
allowNonTsExtensions: true,
|
||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
moduleResolution: monaco.typescript.ModuleResolutionKind.NodeJs,
|
||||
typeRoots: ["index.d.ts"],
|
||||
allowTopLevelAwait: true,
|
||||
moduleDetection: "force",
|
||||
} as monaco.languages.typescript.CompilerOptions & Partial<ExtraOptions>);
|
||||
} as monaco.typescript.CompilerOptions & Partial<ExtraOptions>);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
||||
monaco.typescript.typescriptDefaults.setDiagnosticsOptions({
|
||||
diagnosticCodesToIgnore: [
|
||||
// Allows top level await
|
||||
1375,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { cn, usableResourcePath } from "@lib/utils";
|
||||
import { Types } from "komodo_client";
|
||||
import { RequiredResourceComponents, UsableResource } from "@types";
|
||||
import { DataTable, SortableHeader } from "@ui/data-table";
|
||||
import { AlertTriangle, Box, Circle, History } from "lucide-react";
|
||||
import { AlertTriangle, LayoutDashboard, Circle, History } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { UpdateAvailable as StackUpdateAvailable } from "@components/resources/stack";
|
||||
import { UpdateAvailable as DeploymentUpdateAvailable } from "@components/resources/deployment";
|
||||
@@ -40,7 +40,7 @@ export default function Dashboard() {
|
||||
<ActiveResources />
|
||||
<Page
|
||||
title="Dashboard"
|
||||
icon={<Box className="w-8 h-8" />}
|
||||
icon={<LayoutDashboard className="w-8 h-8" />}
|
||||
actions={
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<ShowTables />
|
||||
@@ -108,7 +108,7 @@ const TableSection = ({
|
||||
|
||||
const filtered = useFilterResources(
|
||||
resources as Types.ResourceListItem<unknown>[],
|
||||
search
|
||||
search,
|
||||
);
|
||||
|
||||
let count = filtered.length;
|
||||
@@ -164,7 +164,7 @@ const ResourceRow = ({ type }: { type: UsableResource }) => {
|
||||
const _recents = useUser().data?.recents?.[type]?.slice(0, 6);
|
||||
const _resources = useRead(`List${type}s`, {}).data;
|
||||
const recents = _recents?.filter(
|
||||
(recent) => !_resources?.every((resource) => resource.id !== recent)
|
||||
(recent) => !_resources?.every((resource) => resource.id !== recent),
|
||||
);
|
||||
const resources = _resources
|
||||
?.filter((r) => !recents?.includes(r.id))
|
||||
@@ -238,7 +238,7 @@ const RecentCard = ({
|
||||
className={cn(
|
||||
"w-full px-3 py-2 border rounded-md hover:bg-accent/25 hover:-translate-y-1 transition-all duration-1000 linear flex flex-col justify-between",
|
||||
showServerStats ? "min-h-32" : "h-20",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -256,7 +256,7 @@ const RecentCard = ({
|
||||
"overflow-hidden w-full transition-all duration-1000 linear",
|
||||
showServerStats
|
||||
? "max-h-40 opacity-100 py-2"
|
||||
: "max-h-0 opacity-0 py-0"
|
||||
: "max-h-0 opacity-0 py-0",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
@@ -274,7 +274,7 @@ const RecentCard = ({
|
||||
const ActiveResources = () => {
|
||||
const builds =
|
||||
useRead("ListBuilds", {}).data?.filter(
|
||||
(build) => build.info.state === Types.BuildState.Building
|
||||
(build) => build.info.state === Types.BuildState.Building,
|
||||
) ?? [];
|
||||
const repos =
|
||||
useRead("ListRepos", {}).data?.filter((repo) =>
|
||||
@@ -282,15 +282,15 @@ const ActiveResources = () => {
|
||||
Types.RepoState.Building,
|
||||
Types.RepoState.Cloning,
|
||||
Types.RepoState.Pulling,
|
||||
].includes(repo.info.state)
|
||||
].includes(repo.info.state),
|
||||
) ?? [];
|
||||
const procedures =
|
||||
useRead("ListProcedures", {}).data?.filter(
|
||||
(procedure) => procedure.info.state === Types.ProcedureState.Running
|
||||
(procedure) => procedure.info.state === Types.ProcedureState.Running,
|
||||
) ?? [];
|
||||
const actions =
|
||||
useRead("ListActions", {}).data?.filter(
|
||||
(action) => action.info.state === Types.ActionState.Running
|
||||
(action) => action.info.state === Types.ActionState.Running,
|
||||
) ?? [];
|
||||
const global_auto_updates =
|
||||
useRead("ListUpdates", {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useRef, useState } from "react";
|
||||
import { ThemeToggle } from "@ui/theme";
|
||||
import { KOMODO_BASE_URL, sanitize_query } from "@main";
|
||||
import { KeyRound, Loader2, X } from "lucide-react";
|
||||
import { cn, preparePasskeyCredential } from "@lib/utils";
|
||||
import { cn } from "@lib/utils";
|
||||
import { MoghAuth } from "komodo_client";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function Login({
|
||||
case "Passkey":
|
||||
setPasskeyPending(true);
|
||||
return navigator.credentials
|
||||
.get(preparePasskeyCredential(data))
|
||||
.get(MoghAuth.Passkey.prepareRequestChallengeResponse(data))
|
||||
.then((credential) => completePasskeyLogin({ credential }))
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
|
||||
@@ -2,8 +2,8 @@ import { ConfirmButton } from "@components/util";
|
||||
import { useManageAuth, useUserInvalidate } from "@lib/hooks";
|
||||
import { Button } from "@ui/button";
|
||||
import { Fingerprint, Trash } from "lucide-react";
|
||||
import { Types } from "komodo_client";
|
||||
import { base64urlToArrayBuffer, cn } from "@lib/utils";
|
||||
import { MoghAuth, Types } from "komodo_client";
|
||||
import { cn } from "@lib/utils";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
|
||||
export const EnrollPasskey = ({ user }: { user: Types.User }) => {
|
||||
@@ -32,22 +32,8 @@ export const EnrollPasskey = ({ user }: { user: Types.User }) => {
|
||||
|
||||
const { mutate: begin_enrollment } = useManageAuth("BeginPasskeyEnrollment", {
|
||||
onSuccess: (challenge) => {
|
||||
const formatted_challenge = {
|
||||
...challenge,
|
||||
publicKey: {
|
||||
...challenge.publicKey,
|
||||
challenge: base64urlToArrayBuffer(challenge.publicKey.challenge),
|
||||
user: {
|
||||
...challenge.publicKey.user,
|
||||
id: base64urlToArrayBuffer(challenge.publicKey.user.id),
|
||||
},
|
||||
excludeCredentials: challenge.publicKey.excludeCredentials?.map(
|
||||
(cred: any) => ({ ...cred, id: base64urlToArrayBuffer(cred.id) }),
|
||||
),
|
||||
},
|
||||
};
|
||||
navigator.credentials
|
||||
.create(formatted_challenge)
|
||||
.create(MoghAuth.Passkey.prepareCreationChallengeResponse(challenge))
|
||||
.then((credential) => confirm_enrollment({ credential }))
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
export default {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./pages/**/*.{ts,tsx}",
|
||||
@@ -250,5 +250,5 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate")],
|
||||
plugins: [import("tailwindcss-animate")],
|
||||
};
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { defineConfig } from "vite";
|
||||
import tspaths from "vite-tsconfig-paths";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config({ path: ".env.development" });
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tspaths()],
|
||||
server: { allowedHosts: [process.env.ALLOWED_HOST] },
|
||||
});
|
||||
|
||||
+1072
-747
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user