polaris-mirror/src/app/ddns.rs
2024-08-01 00:09:21 -07:00

85 lines
2.1 KiB
Rust

use base64::prelude::*;
use log::{debug, error};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use crate::app::Error;
use crate::db::DB;
const DDNS_UPDATE_URL: &str = "https://ydns.io/api/v1/update/";
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Config {
pub ddns_host: String,
pub ddns_username: String,
pub ddns_password: String,
}
#[derive(Clone)]
pub struct Manager {
db: DB,
}
impl Manager {
pub fn new(db: DB) -> Self {
Self { db }
}
async fn update_my_ip(&self) -> Result<(), Error> {
let config = self.config().await?;
if config.ddns_host.is_empty() || config.ddns_username.is_empty() {
debug!("Skipping DDNS update because credentials are missing");
return Ok(());
}
let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.ddns_host);
let credentials = format!("{}:{}", &config.ddns_username, &config.ddns_password);
let response = ureq::get(full_url.as_str())
.set(
"Authorization",
&format!("Basic {}", BASE64_STANDARD_NO_PAD.encode(credentials)),
)
.call();
match response {
Ok(_) => Ok(()),
Err(ureq::Error::Status(code, _)) => Err(Error::UpdateQueryFailed(code)),
Err(ureq::Error::Transport(_)) => Err(Error::UpdateQueryTransport),
}
}
pub async fn config(&self) -> Result<Config, Error> {
Ok(sqlx::query_as!(
Config,
"SELECT ddns_host, ddns_username, ddns_password FROM config"
)
.fetch_one(self.db.connect().await?.as_mut())
.await?)
}
pub async fn set_config(&self, new_config: &Config) -> Result<(), Error> {
sqlx::query!(
"UPDATE config SET ddns_host = $1, ddns_username = $2, ddns_password = $3",
new_config.ddns_host,
new_config.ddns_username,
new_config.ddns_password
)
.execute(self.db.connect().await?.as_mut())
.await?;
Ok(())
}
pub fn begin_periodic_updates(&self) {
tokio::spawn({
let ddns = self.clone();
async move {
loop {
if let Err(e) = ddns.update_my_ip().await {
error!("Dynamic DNS update error: {:?}", e);
}
tokio::time::sleep(Duration::from_secs(60 * 30)).await;
}
}
});
}
}