1use anyhow::Result;
2use client_core::{
3 api::ApiClient, authenticated_client::AuthenticatedClient, backup::BackupManager,
4 config::AppConfig, constants::config, container::DockerManager, database::Database,
5 upgrade::UpgradeManager,
6};
7use rust_i18n::t;
8use std::path::{Path, PathBuf};
9use std::sync::Arc;
10
11use crate::cli::Commands;
12use crate::commands;
13use tracing::debug;
14
15#[derive(Clone)]
16pub struct CliApp {
17 pub config_path: PathBuf,
18 pub config: Arc<AppConfig>,
19 pub database: Arc<Database>,
20 pub api_client: Arc<ApiClient>,
21 pub authenticated_client: Arc<AuthenticatedClient>,
22 pub docker_manager: Arc<DockerManager>,
23 pub backup_manager: Arc<BackupManager>,
24 pub upgrade_manager: Arc<UpgradeManager>,
25}
26
27impl CliApp {
28 pub async fn new_with_auto_config() -> Result<Self> {
30 let config = Arc::new(AppConfig::find_and_load_config()?);
31
32 Self::new_with_config(config, PathBuf::from("config.toml")).await
33 }
34
35 pub async fn new_with_config_path<P: AsRef<Path>>(config_path: P) -> Result<Self> {
37 let config_path = config_path.as_ref();
38 let config = if config_path.exists() {
39 Arc::new(AppConfig::load_from_file(config_path)?)
40 } else {
41 Arc::new(AppConfig::find_and_load_config()?)
43 };
44
45 Self::new_with_config(config, config_path.to_path_buf()).await
46 }
47
48 async fn new_with_config(config: Arc<AppConfig>, config_path: PathBuf) -> Result<Self> {
50 config.ensure_cache_dirs()?;
52
53 let db_path = config::get_database_path();
55 let database = Arc::new(Database::connect(&db_path).await?);
56 debug!("Database connected: {}", db_path.display());
57
58 if !database.is_database_initialized().await? {
60 return Err(anyhow::anyhow!(
61 t!("app.database_not_initialized").to_string(),
62 ));
63 }
64
65 let server_base_url = client_core::constants::api::get_base_url();
67 let authenticated_client =
68 Arc::new(AuthenticatedClient::new(database.clone(), server_base_url).await?);
69
70 let client_id = database.get_api_client_id().await?;
72 let api_client = Arc::new(ApiClient::new(
73 client_id.clone(),
74 Some(authenticated_client.clone()),
75 ));
76
77 let docker_manager = Arc::new(DockerManager::with_project(
79 PathBuf::from(&config.docker.compose_file),
80 PathBuf::from(&config.docker.env_file),
81 None,
82 )?);
83
84 let backup_manager = Arc::new(BackupManager::new(
85 PathBuf::from(&config.backup.storage_dir),
86 database.clone(),
87 docker_manager.clone(),
88 )?);
89 let upgrade_manager = Arc::new(UpgradeManager::new(
90 config.clone(),
91 config_path.clone(),
92 api_client.clone(),
93 database.clone(),
94 ));
95
96 Ok(Self {
97 config_path,
98 config,
99 database,
100 api_client,
101 authenticated_client,
102 docker_manager,
103 backup_manager,
104 upgrade_manager,
105 })
106 }
107
108 pub async fn run_command(&mut self, command: Commands) -> Result<()> {
110 match command {
111 Commands::Status => commands::run_status(self).await,
112 Commands::ApiInfo => commands::run_api_info(self).await,
113 Commands::Init { .. } => unreachable!(), Commands::CheckUpdate(check_update_cmd) => {
115 commands::handle_check_update_command(check_update_cmd)
116 .await
117 .map_err(|e| {
118 anyhow::anyhow!(
119 t!("app.check_update_failed", error = e.to_string()).to_string()
120 )
121 })
122 }
123 Commands::Upgrade { subcommand, args } => {
124 match subcommand {
125 Some(crate::cli::UpgradeSubcommand::Download { full: _ }) => {
126 commands::run_download_with_config(&self.config)
127 .await
128 .map_err(|e| {
129 client_core::DuckError::custom(
130 t!("app.upgrade_failed", error = e.to_string()).to_string(),
131 )
132 })?;
133 }
134 Some(crate::cli::UpgradeSubcommand::Check { force }) => {
135 let args = crate::cli::UpgradeArgs { force, check: true };
136 commands::run_upgrade(self, args).await.map_err(|e| {
137 client_core::DuckError::custom(
138 t!("app.upgrade_failed", error = e.to_string()).to_string(),
139 )
140 })?;
141 }
142 None => {
143 commands::run_upgrade(self, args).await.map_err(|e| {
144 client_core::DuckError::custom(
145 t!("app.upgrade_failed", error = e.to_string()).to_string(),
146 )
147 })?;
148 }
149 }
150 Ok(())
151 }
152 Commands::ListBackups => commands::run_list_backups(self).await,
153 Commands::Rollback {
154 backup_id,
155 force,
156 list_json,
157 rollback_data,
158 } => {
159 commands::backup::run_rollback(
160 self,
161 backup_id,
162 force,
163 list_json,
164 true,
165 rollback_data,
166 )
167 .await
168 }
169 Commands::DockerService(docker_cmd) => {
170 commands::run_docker_service_command(self, docker_cmd).await
171 }
172 Commands::Ducker { args } => commands::run_ducker(args).await,
173 Commands::AutoBackup(auto_backup_cmd) => {
174 commands::handle_auto_backup(self, &auto_backup_cmd).await
175 }
176 Commands::AutoUpgradeDeploy(auto_upgrade_deploy_cmd) => {
177 commands::handle_auto_upgrade_deploy_command(self, auto_upgrade_deploy_cmd).await
178 }
179 Commands::Cache(cache_cmd) => commands::handle_cache_command(self, cache_cmd).await,
180 Commands::DiffSql {
181 old_sql,
182 new_sql,
183 old_version,
184 new_version,
185 output,
186 } => commands::run_diff_sql(old_sql, new_sql, old_version, new_version, output).await,
187 }
188 }
189}