mirror of
https://github.com/moghtech/komodo.git
synced 2026-10-28 16:00:38 +00:00
* start 1.19.1 * deploy 1.19.1-dev-1 * Global Auto Update rustdoc * support stack additional files * deploy 1.19.1-dev-2 * Fe support additional file language detection * fix tsc * Fix: Example code blocks got interpreted as rust code, leading to compilation errors (#743) * Enhanced Server Stats Dashboard with Performance Optimizations (#746) * Improve the layout of server mini stats in the dashboard. - Server stats and tags made siblings for clearer responsibilities - Changed margin to padding - Unreachable indicator made into an overlay of the stats * feat: optimize dashboard server stats with lazy loading and smart server availability checks - Add enabled prop to ServerStatsMini for conditional data fetching - Implement server availability check (only fetch stats for Ok servers, not NotOk/Disabled) - Prevent 500 errors by avoiding API calls to offline servers - Increase polling interval from 10s to 15s and add 5s stale time - Add useMemo for expensive calculations to reduce re-renders - Add conditional overlay rendering for unreachable servers - Only render stats when showServerStats preference is enabled * fix: show disabled servers with overlay instead of hiding component - Maintain consistent layout by showing disabled state overlay - Prevent UX inconsistency where disabled servers disappeared entirely * fix: show button height * feat: add enhance card animations * cleanup * gen types * deploy 1.19.1-dev-3 * add .ini * deploy 1.19.1-dev-4 * simple configure action args as JSON * server enabled actually defaults false * SendAlert via Action / CLI * fix clippy if let string * deploy 1.19.1-dev-5 * improve cli ergonomics * gen types and fix responses formatting * Add RunStackService API implementing `docker compose run` (#732) * Add RunStackService API implementing `docker compose run` * Add working Procedure configuration * Remove `km execute run` alias. Remove redundant ``#[serde(default)]` on `Option`. * Refactor command from `String` to `Vec<String>` * Implement proper shell escaping * bump deps * Update configuration.md - fix typo: "affect" -> "effect" (#747) * clean up SendAlert doc * deploy 1.19.1-dev-6 * env file args won't double pass env file * deploy 1.19.1-dev-7 * Add Enter Key Support for Dialog Confirmations (#750) * start 1.19.1 * deploy 1.19.1-dev-1 * Implement usePromptHotkeys for enhanced dialog interactions and UX * Refactor usePromptHotkeys to enhance confirm button detection and improve UX * Remove forceConfirmDialog prop from ActionWithDialog and related logic for cleaner implementation * Add dialog descriptions to ConfirmUpdate and ActionWithDialog for better clarity and resolve warnings * fix * Restore forceConfirmDialog prop to ActionWithDialog for enhanced confirmation handling * cleanup * Remove conditional className logic from ConfirmButton --------- Co-authored-by: mbecker20 <max@mogh.tech> * Support complex file depency action resolution * get FE compile * deploy 1.19.1-dev-8 * implement additional file dependency configuration * deploy 1.19.1-dev-9 * UI default file dependency None * default additional file requires is None * deploy 1.19.1-dev-10 * rename additional_files => config_files for clarity * deploy 1.19.1-dev-11 * fix skip serializing if None * deploy 1.19.1-dev-12 * stack file dependency toml parsing aliases * fmt * Add: Server Version Mismatch Warnings & Alert System (#748) * start 1.19.1 * deploy 1.19.1-dev-1 * feat: implement version mismatch warnings in server UI - Replace orange warning colors with yellow for better visibility - Add version mismatch detection that shows warnings instead of OK status Implement responsive "VERSION MISMATCH" badge layout - Update server dashboard to include warning counts - Add backend version comparison logic for GetServersSummary * feat: add warning count to server summary and update backup documentation link * feat: add server version mismatch alert handling and update server summary invalidation logic * fix: correct version mismatch alert config and disabled server display - Use send_version_mismatch_alerts instead of send_unreachable_alerts - Show 'Unknown' instead of 'Disabled' for disabled server versions - Remove commented VersionAlert and Alerts UI components - Update version to 1.19.0 * cleanup * Update TypeScript types after merge * cleanup * cleanup * cleanup * Add "ServerVersionMismatch" to alert types * Adjust color classes for warning states and revert server update invalidation logic --------- Co-authored-by: mbecker20 <max@mogh.tech> * backend for build multi registry push support * deploy 1.19.1-dev-13 * build multi registry configuration * deploy 1.19.1-dev-14 * fix invalid tokens JSON * DeployStackIfChanged restarts also update stack.info.deployed_contents * update deployed services comments * deploy 1.19.1-dev-15 * Enhance server monitoring with load average data and new server monitoring table (#761) * add monitoring page * initial table * moving monitoring table to servers * add cpu load average * typeshare doesnt allow tuples * fix GetHistoricalServerStats * add loadAvg to the server monitoring table * improve styling * add load average chart * multiple colors for average loads chart * make load average chart line and non-stacked * cleanup * use server thresholds * cleanup * Change "Dependents:" to "Services:" in config file service dependency selector * deploy 1.19.1-dev-16 * 1.19.1 --------- Co-authored-by: mbecker20 <max@mogh.tech> Co-authored-by: Marcel Pfennig <82059270+MP-Tool@users.noreply.github.com> Co-authored-by: Brian Bradley <brian.bradley.p@gmail.com> Co-authored-by: Ravi Wolter-Krishan <rkn@gedikas.net> Co-authored-by: jack <45038833+jackra1n@users.noreply.github.com>
207 lines
5.5 KiB
Rust
207 lines
5.5 KiB
Rust
use std::str::FromStr;
|
|
|
|
use anyhow::{Context, anyhow};
|
|
use komodo_client::entities::{
|
|
action::Action,
|
|
alert::Alert,
|
|
alerter::Alerter,
|
|
api_key::ApiKey,
|
|
build::Build,
|
|
builder::Builder,
|
|
config::DatabaseConfig,
|
|
deployment::Deployment,
|
|
permission::Permission,
|
|
procedure::Procedure,
|
|
provider::{DockerRegistryAccount, GitProviderAccount},
|
|
repo::Repo,
|
|
server::Server,
|
|
stack::Stack,
|
|
stats::SystemStatsRecord,
|
|
sync::ResourceSync,
|
|
tag::Tag,
|
|
update::Update,
|
|
user::{User, UserConfig},
|
|
user_group::UserGroup,
|
|
variable::Variable,
|
|
};
|
|
use mongo_indexed::{create_index, create_unique_index};
|
|
use mungos::{
|
|
init::MongoBuilder,
|
|
mongodb::{
|
|
Collection, Database,
|
|
bson::{doc, oid::ObjectId},
|
|
},
|
|
};
|
|
|
|
pub use mongo_indexed;
|
|
pub use mungos;
|
|
|
|
pub mod utils;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Client {
|
|
pub users: Collection<User>,
|
|
pub user_groups: Collection<UserGroup>,
|
|
pub permissions: Collection<Permission>,
|
|
pub api_keys: Collection<ApiKey>,
|
|
pub tags: Collection<Tag>,
|
|
pub variables: Collection<Variable>,
|
|
pub git_accounts: Collection<GitProviderAccount>,
|
|
pub registry_accounts: Collection<DockerRegistryAccount>,
|
|
pub updates: Collection<Update>,
|
|
pub alerts: Collection<Alert>,
|
|
pub stats: Collection<SystemStatsRecord>,
|
|
// RESOURCES
|
|
pub servers: Collection<Server>,
|
|
pub deployments: Collection<Deployment>,
|
|
pub builds: Collection<Build>,
|
|
pub builders: Collection<Builder>,
|
|
pub repos: Collection<Repo>,
|
|
pub procedures: Collection<Procedure>,
|
|
pub actions: Collection<Action>,
|
|
pub alerters: Collection<Alerter>,
|
|
pub resource_syncs: Collection<ResourceSync>,
|
|
pub stacks: Collection<Stack>,
|
|
//
|
|
pub db: Database,
|
|
}
|
|
|
|
impl Client {
|
|
pub async fn new(
|
|
config: &DatabaseConfig,
|
|
) -> anyhow::Result<Client> {
|
|
let db = init(config).await?;
|
|
Self::from_database(db).await
|
|
}
|
|
|
|
pub async fn from_database(db: Database) -> anyhow::Result<Client> {
|
|
let client = Client {
|
|
users: mongo_indexed::collection(&db, true).await?,
|
|
user_groups: mongo_indexed::collection(&db, true).await?,
|
|
permissions: mongo_indexed::collection(&db, true).await?,
|
|
api_keys: mongo_indexed::collection(&db, true).await?,
|
|
tags: mongo_indexed::collection(&db, true).await?,
|
|
variables: mongo_indexed::collection(&db, true).await?,
|
|
git_accounts: mongo_indexed::collection(&db, true).await?,
|
|
registry_accounts: mongo_indexed::collection(&db, true).await?,
|
|
updates: mongo_indexed::collection(&db, true).await?,
|
|
alerts: mongo_indexed::collection(&db, true).await?,
|
|
stats: mongo_indexed::collection(&db, true).await?,
|
|
// RESOURCES
|
|
servers: resource_collection(&db, "Server").await?,
|
|
deployments: resource_collection(&db, "Deployment").await?,
|
|
builds: resource_collection(&db, "Build").await?,
|
|
builders: resource_collection(&db, "Builder").await?,
|
|
repos: resource_collection(&db, "Repo").await?,
|
|
alerters: resource_collection(&db, "Alerter").await?,
|
|
procedures: resource_collection(&db, "Procedure").await?,
|
|
actions: resource_collection(&db, "Action").await?,
|
|
resource_syncs: resource_collection(&db, "ResourceSync")
|
|
.await?,
|
|
stacks: resource_collection(&db, "Stack").await?,
|
|
//
|
|
db,
|
|
};
|
|
Ok(client)
|
|
}
|
|
|
|
/// Updates a user's password using a DB call.
|
|
pub async fn set_user_password(
|
|
&self,
|
|
user: &User,
|
|
password: &str,
|
|
) -> anyhow::Result<()> {
|
|
let UserConfig::Local { .. } = user.config else {
|
|
return Err(anyhow!(
|
|
"User is not a 'Local' (username / password) user"
|
|
));
|
|
};
|
|
if password.is_empty() {
|
|
return Err(anyhow!("Password cannot be empty."));
|
|
}
|
|
let id = ObjectId::from_str(&user.id)
|
|
.context("User id not valid ObjectId.")?;
|
|
let hashed_password =
|
|
hash_password(password).context("Failed to hash password")?;
|
|
self
|
|
.users
|
|
.update_one(
|
|
doc! { "_id": id },
|
|
doc! { "$set": {
|
|
"config.data.password": hashed_password
|
|
} },
|
|
)
|
|
.await
|
|
.context("Failed to update user password on database.")?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Initializes unindexed database handle.
|
|
pub async fn init(
|
|
DatabaseConfig {
|
|
uri,
|
|
address,
|
|
username,
|
|
password,
|
|
app_name,
|
|
db_name,
|
|
}: &DatabaseConfig,
|
|
) -> anyhow::Result<Database> {
|
|
let mut client = MongoBuilder::default().app_name(app_name);
|
|
|
|
match (
|
|
!uri.is_empty(),
|
|
!address.is_empty(),
|
|
!username.is_empty(),
|
|
!password.is_empty(),
|
|
) {
|
|
(true, _, _, _) => {
|
|
client = client.uri(uri);
|
|
}
|
|
(_, true, true, true) => {
|
|
client = client
|
|
.address(address)
|
|
.username(username)
|
|
.password(password);
|
|
}
|
|
(_, true, _, _) => {
|
|
client = client.address(address);
|
|
}
|
|
_ => {
|
|
return Err(anyhow!(
|
|
"'config.database' not configured correctly. must pass either 'config.database.uri', or 'config.database.address' + 'config.database.username' + 'config.database.password'"
|
|
));
|
|
}
|
|
}
|
|
|
|
let client = client
|
|
.build()
|
|
.await
|
|
.context("Failed to initialize database connection.")?;
|
|
|
|
Ok(client.database(db_name))
|
|
}
|
|
|
|
async fn resource_collection<T: Send + Sync>(
|
|
db: &Database,
|
|
collection_name: &str,
|
|
) -> anyhow::Result<Collection<T>> {
|
|
let coll = db.collection::<T>(collection_name);
|
|
|
|
create_unique_index(&coll, "name").await?;
|
|
|
|
create_index(&coll, "tags").await?;
|
|
|
|
Ok(coll)
|
|
}
|
|
|
|
const BCRYPT_COST: u32 = 10;
|
|
pub fn hash_password<P>(password: P) -> anyhow::Result<String>
|
|
where
|
|
P: AsRef<[u8]>,
|
|
{
|
|
bcrypt::hash(password, BCRYPT_COST)
|
|
.context("failed to hash password")
|
|
}
|