Allow importing of recipes to NextCloud

Closes #1
This commit is contained in:
Matteo Settenvini 2022-07-03 01:02:12 +02:00
parent 3c27383bbc
commit cde6c7fe9f
Signed by: matteo
GPG Key ID: 8576CC1AD97D42DF
4 changed files with 91 additions and 4 deletions

1
Cargo.lock generated
View File

@ -116,6 +116,7 @@ name = "cooking-schedule"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64",
"clap", "clap",
"directories", "directories",
"reqwest", "reqwest",

View File

@ -19,6 +19,9 @@ version = "0.11"
[dependencies.anyhow] [dependencies.anyhow]
version = "1.0" version = "1.0"
[dependencies.base64]
version = "0.13"
[dependencies.clap] [dependencies.clap]
version = "3.2" version = "3.2"
features = ["cargo"] features = ["cargo"]

View File

@ -17,14 +17,14 @@ pub struct Credentials {
#[serde(skip)] #[serde(skip)]
config_file: PathBuf, config_file: PathBuf,
servers: HashMap<String, Server>, pub servers: HashMap<String, Server>,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Server { pub struct Server {
url: String, pub url: String,
login_name: String, pub login_name: String,
password: String, pub password: String,
} }
impl Credentials { impl Credentials {
@ -48,6 +48,7 @@ impl Credentials {
pub fn add(&mut self, server: Url) -> Result<()> { pub fn add(&mut self, server: Url) -> Result<()> {
let http_client = reqwest::blocking::Client::builder() let http_client = reqwest::blocking::Client::builder()
.https_only(true)
.user_agent(USER_AGENT) .user_agent(USER_AGENT)
.build()?; .build()?;

View File

@ -6,11 +6,16 @@ mod constants;
use { use {
self::config::Config, self::config::Config,
anyhow::anyhow,
base64::write::EncoderWriter as Base64Encoder,
clap::{arg, command, ArgMatches, Command}, clap::{arg, command, ArgMatches, Command},
reqwest::Url, reqwest::Url,
std::io::Write,
}; };
fn parse_args() -> ArgMatches { fn parse_args() -> ArgMatches {
let server_arg = arg!(-s --server <server> "NextCloud server to connect to").required(false);
command!() command!()
.propagate_version(true) .propagate_version(true)
.subcommand_required(true) .subcommand_required(true)
@ -20,6 +25,12 @@ fn parse_args() -> ArgMatches {
.about("Authenticate against the provided NextCloud server") .about("Authenticate against the provided NextCloud server")
.arg(arg!(<server> "NextCloud server to connect to")), .arg(arg!(<server> "NextCloud server to connect to")),
) )
.subcommand(
Command::new("import")
.about("Import the given URLs into NextCloud's cookbook")
.arg(server_arg)
.arg(arg!(<url> ... "One or more URLs each pointing to page with a recipe to import in NextCloud")),
)
.get_matches() .get_matches()
} }
@ -41,8 +52,79 @@ async fn main() -> anyhow::Result<()> {
Ok(()) Ok(())
})?; })?;
} }
Some(("import", sub_matches)) => {
let server_name = sub_matches
.get_one::<String>("server")
.unwrap_or_else(|| {
let servers = &configuration.credentials.servers;
match servers.len() {
0 => panic!("No NextCloud server set up yet, use '{} init' first", env!("CARGO_BIN_NAME")),
1 => servers.iter().next().unwrap().0,
_ => panic!("More than one NextCloud server set up, use the '--server' option to specify which one to use. Known servers: {:#?}", servers.keys().collect::<Vec<_>>()),
}
});
let api_client = ApiClient::new(&server_name, &configuration)?;
for url in sub_matches
.get_many::<String>("url")
.expect("At least one url is required")
{
let response = api_client
.client
.post(api_client.base_url.join("apps/cookbook/import")?)
.json(&serde_json::json!({
"url": url,
}))
.send()
.await?;
println!("{:#?}", response); // TODO
}
}
_ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
}; };
Ok(()) Ok(())
} }
struct ApiClient {
pub base_url: Url,
pub client: reqwest::Client,
}
impl ApiClient {
pub fn new(server_name: &str, configuration: &Config) -> anyhow::Result<Self> {
let server = configuration
.credentials
.servers
.get(server_name)
.ok_or_else(|| {
anyhow!(
"Unknown server {}. Did you use '{} init' first? Known servers: {:#?}",
server_name,
env!("CARGO_BIN_NAME"),
configuration.credentials.servers.keys().collect::<Vec<_>>()
)
})?;
use reqwest::header;
let mut default_headers = header::HeaderMap::new();
let mut auth_header = b"Basic ".to_vec();
{
let mut encoder = Base64Encoder::new(&mut auth_header, base64::STANDARD);
write!(encoder, "{}:{}", server.login_name, server.password).unwrap();
}
let mut auth_header = header::HeaderValue::from_bytes(&auth_header)?;
auth_header.set_sensitive(true);
default_headers.insert(header::AUTHORIZATION, auth_header);
let client = reqwest::Client::builder()
.user_agent(constants::USER_AGENT)
.default_headers(default_headers)
.build()?;
Ok(ApiClient {
base_url: Url::parse(&server.url)?,
client: client,
})
}
}