immich-tools/src/main.rs

114 lines
3.8 KiB
Rust

include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
use crate::args::Commands;
use args::{AssetsCommands, PeopleCommands, ServerCommands};
use clap::Parser;
use color_eyre::eyre::{Result, WrapErr};
use color_eyre::Section;
use commands::delete_assets::delete_assets;
use commands::list_assets::list_assets;
use commands::missing_date_of_birth::missing_date_of_birth;
use commands::server_features::server_features;
use commands::server_version::server_version;
use commands::sync_date_of_birth::sync_date_of_birth;
use config::Config;
use context::Context;
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 context;
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()
.wrap_err_with(|| "Invalid configuration or insufficient command line arguments")?;
let client = get_client(&conf.server_url, &conf.api_key)?;
let ctx = Context::builder()
.client(client)
.dry_run(args.dry_run)
.no_confirm(args.no_confirm)
.build();
validate_client_connection(&ctx.client).await?;
match &args.command {
Commands::Assets { assets_command } => match assets_command {
AssetsCommands::Delete { offline } => delete_assets(ctx, *offline).await,
AssetsCommands::List { offline } => list_assets(ctx, *offline).await,
},
Commands::People { people_command } => match people_command {
PeopleCommands::MissingDateOfBirths {} => missing_date_of_birth(ctx).await,
PeopleCommands::SyncDateOfBirths { vcard: _ } => {
sync_date_of_birth(ctx, &conf.people.sync_date_of_births.vcard).await
}
},
Commands::Server { server_command } => match server_command {
ServerCommands::Features {} => server_features(ctx).await,
ServerCommands::Version {} => server_version(ctx).await,
},
}
}
fn get_client(url: &str, api_key: &str) -> Result<Client> {
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(())
}