use crate::fs::*; use crate::notification::NotificationData::{CompressError, PermissionDenied, WebApiUnavailable}; use crate::notification::{MaybeError, NotificationData}; use log::info; use std::io; use tokio::fs::{File, OpenOptions}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use zstd::{DEFAULT_COMPRESSION_LEVEL, Decoder, Encoder}; // const API_URL: &str = "http://127.0.0.1:8089/"; const API_URL: &str = "https://learning.micialware.ru/"; pub async fn send_data(id: String) -> MaybeError { let db_file = db_path(); let data = tokio::fs::read(db_file) .await .map_err(|_| WebApiUnavailable)?; let data = compress(data)?; let api_url = API_URL.to_string(); let id_url = format!("{api_url}upload/stream/{id}"); let client = reqwest::Client::new(); let new_version = client .post(&id_url) .body(data) .send() .await .map_err(|_| WebApiUnavailable)? .text() .await .map_err(|_| WebApiUnavailable)?; set_local_version(new_version.parse::().unwrap_or_default()).await?; Ok(()) } fn compress(data: Vec) -> Result, NotificationData> { let mut encoder = Encoder::new(Vec::new(), DEFAULT_COMPRESSION_LEVEL).map_err(|_| CompressError)?; io::copy(&mut &data[..], &mut encoder).map_err(|_| PermissionDenied)?; encoder.finish().map_err(|_| CompressError) } pub async fn load_data(id: String, temp: bool) -> MaybeError { let api_url = API_URL.to_string(); let id_url = format!("{api_url}download/{id}"); let client = reqwest::Client::new(); let data = client .get(&id_url) .send() .await .map_err(|_| WebApiUnavailable)? .bytes() .await .map_err(|_| WebApiUnavailable)? .to_vec(); let data = decompress(data)?; let path = app_data_dir(); let file_name = if temp { "data.db.tmp" } else { "data.db" }; let db_file = path.join(file_name); tokio::fs::write(db_file, data) .await .map_err(|_| PermissionDenied)?; let version_url = format!("{API_URL}{id}/version"); let version = client .get(&version_url) .send() .await .map_err(|_| WebApiUnavailable)? .text() .await .map_err(|_| WebApiUnavailable)?; set_local_version(version.parse::().unwrap_or_default()).await?; Ok(()) } pub fn decompress(data: Vec) -> Result, NotificationData> { let mut decoder = Decoder::new(&data[..]).map_err(|_| CompressError)?; let mut decompressed = Vec::new(); io::copy(&mut decoder, &mut decompressed).map_err(|_| PermissionDenied)?; Ok(decompressed) } pub async fn first_sync() -> Result { let id_url = API_URL.to_string() + "generate"; let client = reqwest::Client::new(); let id = client .get(&id_url) .send() .await .map_err(|_| WebApiUnavailable)? .text() .await .map_err(|_| WebApiUnavailable)?; info!("id: {}", id); Ok(id) } pub async fn get_web_version(key: &str) -> Result { let id_url = format!("{API_URL}{key}/version"); let client = reqwest::Client::new(); let version = client .get(&id_url) .send() .await .map_err(|_| WebApiUnavailable)? .text() .await .map_err(|_| WebApiUnavailable)?; info!("version: {}", version); Ok(version.parse::().unwrap_or_default()) } pub async fn get_local_version() -> Result { let file = db_meta_path(); if file.exists() { let mut data = String::new(); File::open(file.clone()) .await .map_err(|_| PermissionDenied)? .read_to_string(&mut data) .await .map_err(|_| PermissionDenied)?; return Ok(data.parse::().unwrap_or_default()); } let mut file = OpenOptions::new() .write(true) .create(true) .truncate(true) .open(file) .await .map_err(|_| PermissionDenied)?; file.write_all("0".as_bytes()) .await .map_err(|_| PermissionDenied)?; Ok(0) } pub async fn set_local_version(version: u32) -> Result<(), NotificationData> { let file_path = db_meta_path(); let mut file = OpenOptions::new() .write(true) .create(true) .truncate(true) .open(file_path) .await .map_err(|_| PermissionDenied)?; file.write_all(version.to_string().as_bytes()) .await .map_err(|_| PermissionDenied)?; Ok(()) }