From 1f830818210da6966ec20558d8dfe9a3fdb7b8b9 Mon Sep 17 00:00:00 2001 From: Lu Yang Date: Thu, 22 Aug 2024 20:33:06 +0100 Subject: [PATCH] Make creation modal button text configurable --- lapdev-api/src/router.rs | 7 ++++ lapdev-api/src/server.rs | 27 +++++++++++--- lapdev-conductor/src/server.rs | 23 ++++++++---- lapdev-dashboard/src/account.rs | 56 +++++++++++++++------------- lapdev-dashboard/src/app.rs | 9 +++++ lapdev-dashboard/src/cluster.rs | 10 ++--- lapdev-dashboard/src/license.rs | 4 +- lapdev-dashboard/src/modal.rs | 5 ++- lapdev-dashboard/src/nav.rs | 24 +++++++++--- lapdev-dashboard/src/organization.rs | 4 +- lapdev-dashboard/src/project.rs | 2 +- lapdev-dashboard/src/quota.rs | 2 +- lapdev-dashboard/src/ssh_key.rs | 2 +- lapdev-dashboard/src/usage.rs | 7 +++- lapdev-dashboard/src/workspace.rs | 2 +- lapdev-enterprise/src/usage.rs | 24 +++++++++--- 16 files changed, 142 insertions(+), 66 deletions(-) diff --git a/lapdev-api/src/router.rs b/lapdev-api/src/router.rs index 84a7938..ef4af56 100644 --- a/lapdev-api/src/router.rs +++ b/lapdev-api/src/router.rs @@ -8,6 +8,7 @@ use axum::{ }; use axum_client_ip::SecureClientIpSource; use axum_extra::{headers, TypedHeader}; +use hyper::StatusCode; use lapdev_db::entities; use lapdev_proxy_http::forward::ProxyForward; use lapdev_rpc::error::ApiError; @@ -35,8 +36,14 @@ fn private_routes() -> Router { ) } +async fn not_found() -> impl IntoResponse { + StatusCode::NOT_FOUND +} + fn v1_api_routes(additional_router: Option>) -> Router { let router = Router::new() + .route("/", any(not_found)) + .route("/*0", any(not_found)) .route("/cluster_info", get(admin::get_cluster_info)) .route("/auth_providers", get(admin::get_auth_providers)) .route("/hostnames", get(admin::get_hostnames)) diff --git a/lapdev-api/src/server.rs b/lapdev-api/src/server.rs index 2b4914d..42ceaf1 100644 --- a/lapdev-api/src/server.rs +++ b/lapdev-api/src/server.rs @@ -1,4 +1,7 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; use anyhow::{anyhow, Context, Result}; use axum::{extract::Request, Router}; @@ -38,6 +41,9 @@ struct Cli { /// The folder for putting logs #[clap(short, long, action, value_hint = clap::ValueHint::AnyPath)] logs_folder: Option, + /// The folder for putting data + #[clap(short, long, action, value_hint = clap::ValueHint::AnyPath)] + data_folder: Option, /// Don't run db migration on startup #[clap(short, long, action)] no_migration: bool, @@ -45,15 +51,23 @@ struct Cli { pub async fn start(additional_router: Option>) { let cli = Cli::parse(); + let data_folder = cli + .data_folder + .clone() + .unwrap_or_else(|| PathBuf::from("/var/lib/lapdev")); - let _result = setup_log(&cli).await; + let _result = setup_log(&cli, &data_folder).await; - if let Err(e) = run(&cli, additional_router).await { + if let Err(e) = run(&cli, additional_router, data_folder).await { tracing::error!("lapdev api start server error: {e:#}"); } } -async fn run(cli: &Cli, additional_router: Option>) -> Result<()> { +async fn run( + cli: &Cli, + additional_router: Option>, + data_folder: PathBuf, +) -> Result<()> { let config_file = cli .config_file .clone() @@ -68,7 +82,7 @@ async fn run(cli: &Cli, additional_router: Option>) -> Result< .ok_or_else(|| anyhow!("can't find database url in your config file"))?; let db = DbApi::new(&db_url, cli.no_migration).await?; - let conductor = Conductor::new(LAPDEV_VERSION, db.clone()).await?; + let conductor = Conductor::new(LAPDEV_VERSION, db.clone(), data_folder).await?; let ssh_proxy_port = config.ssh_proxy_port.unwrap_or(2222); { @@ -164,11 +178,12 @@ async fn run(cli: &Cli, additional_router: Option>) -> Result< async fn setup_log( cli: &Cli, + data_folder: &Path, ) -> Result { let folder = cli .logs_folder .clone() - .unwrap_or_else(|| PathBuf::from("/var/lib/lapdev/logs")); + .unwrap_or_else(|| data_folder.join("logs")); tokio::fs::create_dir_all(&folder).await?; let file_appender = tracing_appender::rolling::Builder::new() .max_log_files(30) diff --git a/lapdev-conductor/src/server.rs b/lapdev-conductor/src/server.rs index 87ce90b..7ae16bb 100644 --- a/lapdev-conductor/src/server.rs +++ b/lapdev-conductor/src/server.rs @@ -111,6 +111,7 @@ pub struct Conductor { rpcs: Arc>>, ws_hosts: Arc>>, region: Arc>, + data_folder: PathBuf, pub hostnames: Arc>>, pub cpu_overcommit: Arc>, // updates for a single prebuild, including the image building outputs @@ -129,10 +130,10 @@ pub struct Conductor { } impl Conductor { - pub async fn new(version: &str, db: DbApi) -> Result { - tokio::fs::create_dir_all("/var/lib/lapdev/projects/") + pub async fn new(version: &str, db: DbApi, data_folder: PathBuf) -> Result { + tokio::fs::create_dir_all(data_folder.join("projects")) .await - .with_context(|| "trying to create /var/lib/lapdev/projects/")?; + .with_context(|| format!("trying to create {:?}", data_folder.join("projects")))?; let cpu_overcommit = db .get_config(LAPDEV_CPU_OVERCOMMIT) .await @@ -144,6 +145,7 @@ impl Conductor { let enterprise = Arc::new(Enterprise::new(db.clone()).await?); let hostnames = enterprise.get_hostnames().await.unwrap_or_default(); let conductor = Self { + data_folder, version: version.to_string(), rpcs: Default::default(), rpc_aborts: Default::default(), @@ -548,8 +550,9 @@ impl Conductor { let local_repo_url = repo_url.to_string(); let repo_url = repo_url.to_string(); let auth = auth.clone(); + let data_folder = self.data_folder.clone(); tokio::task::spawn_blocking(move || { - let repo = git2::Repository::init("/var/lib/lapdev")?; + let repo = git2::Repository::init(data_folder)?; let mut remote = repo.remote_anonymous(&repo_url)?; let mut cbs = RemoteCallbacks::new(); @@ -680,7 +683,7 @@ impl Conductor { .await?; txn.commit().await?; - let path = PathBuf::from(format!("/var/lib/lapdev/projects/{id}")); + let path = self.data_folder.join(format!("projects/{id}")); let url = repo.url.clone(); tokio::fs::create_dir_all(&path).await?; clone_repo(url, path, repo.auth.clone()).await?; @@ -700,9 +703,10 @@ impl Conductor { user_agent: Option, ) -> Result, ApiError> { let project_id = project.id; + let data_folder = self.data_folder.clone(); let (previous_branches, branches) = tokio::task::spawn_blocking( move || -> Result<(Vec, Vec), ApiError> { - let path = PathBuf::from(format!("/var/lib/lapdev/projects/{project_id}")); + let path = data_folder.join(format!("projects/{project_id}")); let repo = git2::Repository::open(path)?; let previous_branches = repo_branches(&repo)?; @@ -1236,7 +1240,10 @@ impl Conductor { async fn prepare_repo(&self, repo: &RepoDetails, temp_repo_dir: &Path) -> Result<(), ApiError> { if let Some(project) = repo.project.as_ref() { - let src_repo_dir = format!("/var/lib/lapdev/projects/{}/.", project.id); + let src_repo_dir = self.data_folder.join(format!("projects/{}/.", project.id)); + let src_repo_dir = src_repo_dir + .to_str() + .ok_or_else(|| anyhow!("can't get repo dir"))?; let temp_repo_dir = temp_repo_dir .to_str() .ok_or_else(|| anyhow!("can't get repo dir"))?; @@ -2836,7 +2843,7 @@ impl Conductor { } } - let path = PathBuf::from(format!("/var/lib/lapdev/projects/{}", project.id)); + let path = self.data_folder.join(format!("projects/{}", project.id)); tokio::fs::remove_dir_all(path).await?; Ok(()) diff --git a/lapdev-dashboard/src/account.rs b/lapdev-dashboard/src/account.rs index 30e7a44..ed17bcb 100644 --- a/lapdev-dashboard/src/account.rs +++ b/lapdev-dashboard/src/account.rs @@ -50,7 +50,7 @@ pub fn Login() -> impl IntoView { view! {
- - + } } diff --git a/lapdev-dashboard/src/license.rs b/lapdev-dashboard/src/license.rs index 8cc6166..cf75ca7 100644 --- a/lapdev-dashboard/src/license.rs +++ b/lapdev-dashboard/src/license.rs @@ -117,7 +117,7 @@ pub fn UpdateLicenseView(update_counter: RwSignal) -> impl IntoView { > Update Enterprise License - + } } @@ -202,6 +202,6 @@ pub fn SignLicenseView() -> impl IntoView { > Sign New License - + } } diff --git a/lapdev-dashboard/src/modal.rs b/lapdev-dashboard/src/modal.rs index 9820683..ba5e356 100644 --- a/lapdev-dashboard/src/modal.rs +++ b/lapdev-dashboard/src/modal.rs @@ -44,7 +44,8 @@ pub fn CreationModal( modal_hidden: RwSignal, action: Action<(), Result<(), ErrorResponse>>, body: T, - is_update: bool, + update_text: Option, + updating_text: Option, create_button_hidden: bool, ) -> impl IntoView where @@ -129,7 +130,7 @@ where - { move || if create_pending.get() { if is_update {"Updating"} else { "Creating" } } else if is_update { "Update" } else { "Create" } } + { move || if create_pending.get() { updating_text.clone().unwrap_or_else(|| "Updating".to_string()) } else { update_text.clone().unwrap_or_else(|| "Update".to_string()) } } - + } } diff --git a/lapdev-dashboard/src/usage.rs b/lapdev-dashboard/src/usage.rs index 167051f..728947b 100644 --- a/lapdev-dashboard/src/usage.rs +++ b/lapdev-dashboard/src/usage.rs @@ -86,7 +86,12 @@ pub fn UsageView() -> impl IntoView { let error = create_rw_signal(None); + let counter = create_rw_signal(0); + let get_action = create_action(move |()| async move { + counter.update(|c| { + *c += 1; + }); error.set(None); let result = get_org_usage( from_date.get_untracked(), @@ -228,7 +233,7 @@ pub fn UsageView() -> impl IntoView { diff --git a/lapdev-dashboard/src/workspace.rs b/lapdev-dashboard/src/workspace.rs index 30780a8..d210a5b 100644 --- a/lapdev-dashboard/src/workspace.rs +++ b/lapdev-dashboard/src/workspace.rs @@ -893,7 +893,7 @@ pub fn NewWorkspaceModal( }; view! { - + } } diff --git a/lapdev-enterprise/src/usage.rs b/lapdev-enterprise/src/usage.rs index 31ae3b2..b91b0a8 100644 --- a/lapdev-enterprise/src/usage.rs +++ b/lapdev-enterprise/src/usage.rs @@ -132,6 +132,8 @@ impl Usage { machine_types }; + let now = Utc::now().into(); + let records = records .into_iter() .map(|(record, user)| { @@ -139,23 +141,35 @@ impl Usage { .get(&record.machine_type_id) .map(|m| m.name.clone()) .unwrap_or_default(); + + let usage_start = record.start; + let usage_start = if usage_start < start { + start + } else { + usage_start + }; + + let usage_end = record.end.unwrap_or(now); + let usage_end = if usage_end > end { end } else { usage_end }; + let duration = usage_end - usage_start; + let duration = duration.num_seconds().max(0) as usize; + let cost = duration * record.cost_per_second.max(0) as usize; + UsageRecord { id: record.id, start: record.start, resource_kind: record.resource_kind.clone(), resource_name: record.resource_name.clone(), machine_type, - duration: record.duration.unwrap_or(0) as usize, - cost: record.total_cost as usize, + duration, + cost, user: user.clone().and_then(|user| user.name).unwrap_or_default(), avatar: user.and_then(|user| user.avatar_url).unwrap_or_default(), } }) .collect(); - let total_cost = self - .get_cost(organization, user, start, end, Utc::now().into()) - .await?; + let total_cost = self.get_cost(organization, user, start, end, now).await?; Ok(UsageResult { total_items: items_and_pages.number_of_items, num_pages: items_and_pages.number_of_pages,