include!(concat!(env!("OUT_DIR"), "/codegen.rs")); use crate::args::Commands; use args::{PeopleCommands, ServerCommands}; use clap::Parser; use color_eyre::eyre::{Result, WrapErr}; use color_eyre::Section; use commands::missing_date_of_birth::missing_date_of_birth; use commands::server_version::server_version; use commands::sync_date_of_birth::sync_date_of_birth; use config::Config; use directories::ProjectDirs; use figment::providers::{Env, Format, Serialized, Toml}; use figment::Figment; use figment_file_provider_adapter::FileAdapter; use log::*; use reqwest::header; mod args; mod commands; mod config; mod utils; #[tokio::main] async fn main() -> Result<()> { color_eyre::install()?; pretty_env_logger::init(); let mut conf_extractor = Figment::new(); if let Some(project_dirs) = ProjectDirs::from("fr", "enoent", "Immich Tools") { let config_file_path = project_dirs.config_dir().join("config.toml"); if config_file_path.exists() { debug!("Reading configuration from {:?}", config_file_path); conf_extractor = conf_extractor.merge(FileAdapter::wrap(Toml::file_exact(config_file_path))); } } else { warn!("Unable to determine configuration file path"); } let args = args::Opts::parse(); let conf: Config = conf_extractor .merge(FileAdapter::wrap(Env::prefixed("IMMICH_TOOLS_"))) .merge(Serialized::defaults(&args)) .extract()?; let client = get_client(&conf.server_url, &conf.api_key)?; validate_client_connection(&client).await?; match &args.command { Commands::People { people_command } => match people_command { PeopleCommands::MissingDateOfBirths {} => missing_date_of_birth(&client).await, PeopleCommands::SyncDateOfBirths { vcard: _ } => { sync_date_of_birth( &client, &conf.people.sync_date_of_births.vcard, args.dry_run, ) .await } }, Commands::Server { server_command } => match server_command { ServerCommands::Version {} => server_version(&client).await, }, } } fn get_client(url: &str, api_key: &str) -> Result { let mut headers = header::HeaderMap::new(); let mut auth_value = header::HeaderValue::from_str(api_key)?; auth_value.set_sensitive(true); headers.insert("x-api-key", auth_value); Ok(Client::new_with_client( url, reqwest::Client::builder() .default_headers(headers) .build() .unwrap(), )) } async fn validate_client_connection(client: &Client) -> Result<()> { client .validate_access_token() .await .wrap_err_with(|| "Unable to connect to Immich") .with_suggestion(|| "The API key might be invalid") .with_suggestion(|| "There server URL might be invalid, it should likely end in /api") .with_note(|| format!("The provided server URL was {}", client.baseurl())) .with_note(|| { format!( "The API version for this client is {}, make sure the server version is compatible", client.api_version() ) })?; Ok(()) }