Автоматизация создания установщика для Arch, доработка API
This commit is contained in:
Generated
+3
@@ -344,8 +344,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
@@ -3212,6 +3214,7 @@ dependencies = [
|
||||
"aes",
|
||||
"block-modes",
|
||||
"block-padding 0.3.3",
|
||||
"chrono",
|
||||
"hex",
|
||||
"magic-crypt",
|
||||
"rand 0.8.5",
|
||||
|
||||
@@ -22,6 +22,7 @@ hex = "0.4"
|
||||
aes = "0.7.5"
|
||||
block-modes = "0.8.1"
|
||||
block-padding = "0.3.3"
|
||||
chrono = "0.4.38"
|
||||
|
||||
[features]
|
||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||
|
||||
@@ -22,7 +22,7 @@ pub fn decrypt(key: &[u8], encrypted_data: &[u8]) -> String {
|
||||
String::from_utf8(decrypted_ciphertext).unwrap() // Вывод результата в виде строки
|
||||
}
|
||||
|
||||
/// Примеры
|
||||
// Примеры
|
||||
//println!("TEST ENC: {:?}", crate::aes_enc_dec::encrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), "{\"command\": \"status1\"}"));
|
||||
//println!("TEST DEC: {:?}\n", crate::aes_enc_dec::decrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), &crate::aes_enc_dec::encrypt(&hex::decode(crate::multi_tcp_listener::get_token()).unwrap(), "{\"command\": \"status1\"}")));
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ pub fn check_or_create_config_file(app_handle: tauri::AppHandle) {
|
||||
|
||||
let data = String::from(format!("{}\
|
||||
\"port\": 49494,\
|
||||
\"autostart\": true,\
|
||||
\"autostart\": false,\
|
||||
\"token\": \"{}\"\
|
||||
{}", "{", token, "}"));
|
||||
println!("{}", data);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
use serde_json::Value;
|
||||
use chrono::{DateTime, Local};
|
||||
|
||||
use crate::aes_enc_dec::encrypt;
|
||||
use crate::tasks;
|
||||
|
||||
pub fn get_tasks_tcp(token: String) -> Vec<u8>{
|
||||
encrypt(&hex::decode(token).unwrap(), tasks::get_tasks().to_string().as_str())
|
||||
}
|
||||
|
||||
pub fn get_task_by_id_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
let tasks: Value = tasks::get_tasks();
|
||||
let res;
|
||||
if !json.get("id").is_none(){
|
||||
res = encrypt(&hex::decode(token).unwrap(), tasks[json["id"].as_str().unwrap_or("")].to_string().as_str());
|
||||
} else{
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn create_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
if json["task"].is_object(){
|
||||
if json["task"]["name"].is_string(){
|
||||
let current_datetime: DateTime<Local> = Local::now();
|
||||
let date: String = current_datetime.format("%Y.%m.%d").to_string();
|
||||
let time: String = current_datetime.format("%H:%M").to_string();
|
||||
|
||||
let name = json["task"]["name"].as_str().unwrap().to_string();
|
||||
let description = json["task"]["description"].as_str().unwrap_or("").to_string();
|
||||
let priority = json["task"]["priority"].as_str().unwrap_or("0").to_string();
|
||||
|
||||
encrypt(&hex::decode(token).unwrap(), tasks::add_task(date, time, name, description, priority).as_str())
|
||||
} else{
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
if json["task"].is_object(){
|
||||
if json["task"]["id"].is_number(){
|
||||
let id_task = json["task"]["id"].as_number().unwrap().to_string();
|
||||
let name = json["task"]["name"].as_str().unwrap().to_string();
|
||||
let description = json["task"]["description"].as_str().unwrap_or("").to_string();
|
||||
let priority = json["task"]["priority"].as_str().unwrap_or("0").to_string();
|
||||
|
||||
if tasks::edit_task(id_task, name, description, priority) {
|
||||
encrypt(&hex::decode(token).unwrap(), "200")
|
||||
} else{
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
} else{
|
||||
encrypt(&hex::decode(token).unwrap(), "400")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_task_tcp(token: String, json: Value) -> Vec<u8> {
|
||||
let res;
|
||||
if !json.get("id").is_none(){
|
||||
if tasks::delete_task(json["id"].to_string()){
|
||||
res = encrypt(&hex::decode(token).unwrap(), "200");
|
||||
} else{
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
} else{
|
||||
res = encrypt(&hex::decode(token).unwrap(), "400");
|
||||
}
|
||||
res
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use std::string::String;
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::config_tcp;
|
||||
use crate::{config_tcp, handler_commands};
|
||||
use crate::enc_dec_file::decrypt_file;
|
||||
use crate::aes_enc_dec::{encrypt, decrypt};
|
||||
|
||||
@@ -31,7 +31,7 @@ fn enc_hex_to_data(data: &str) -> String{
|
||||
} // Расшифровка обёрнутых в HEX данных до строки
|
||||
|
||||
|
||||
pub fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
|
||||
fn create_tcp_listener(app_handle: AppHandle) -> TcpListener{
|
||||
let path: PathBuf = [
|
||||
app_handle.path_resolver().app_local_data_dir().unwrap(),
|
||||
"ToDo".into(),
|
||||
@@ -88,19 +88,44 @@ fn handle_connection(mut stream: TcpStream) {
|
||||
let res = enc_hex_to_data(req.trim()); // Дешифрование
|
||||
let json: Value = serde_json::from_str(res.as_str()).unwrap_or(serde_json::json!("")); // Попытка преобразования запроса в JSON
|
||||
if json != serde_json::json!(""){
|
||||
println!("JSON: {:?}", json);
|
||||
println!("JSON command: {:?}", json["command"]);
|
||||
match json["command"].as_str().unwrap() { // Сравнивание команд
|
||||
"stop" => {
|
||||
unsafe {
|
||||
TCP_COMMAND = "stop";
|
||||
}
|
||||
let res = encrypt(&hex::decode(get_token()).unwrap(), "200");
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"status" => {
|
||||
let res = encrypt(&hex::decode(get_token()).unwrap(), "200");
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"get_tasks" => {
|
||||
let res = handler_commands::get_tasks_tcp(get_token());
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"get_task_by_id" => {
|
||||
let res = handler_commands::get_task_by_id_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"create_task" => {
|
||||
let res = handler_commands::create_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"edit_task" => {
|
||||
let res = handler_commands::edit_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
"delete_task" => {
|
||||
let res = handler_commands::delete_task_tcp(get_token(), json);
|
||||
stream.write(hex::encode(res).as_bytes()).unwrap();
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
} else{
|
||||
stream.write(hex::encode(encrypt(&hex::decode(get_token()).unwrap(), "400")).as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::thread;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[path="api/multithread_tcp_listener.rs"] mod multi_tcp_listener;
|
||||
#[path= "api/handler_commands.rs"] mod handler_commands;
|
||||
#[path= "api/config_tcp.rs"] mod config_tcp;
|
||||
#[path= "api/aes_enc_dec.rs"] mod aes_enc_dec;
|
||||
|
||||
@@ -24,22 +25,21 @@ fn open_api_window(app_handle: tauri::AppHandle){
|
||||
.decorations(false)
|
||||
.build() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
println!("{err}");
|
||||
Err(_) => {
|
||||
//println!("{err}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle();
|
||||
|
||||
if config_tcp::get_autostart(app_handle.clone()) {
|
||||
thread::spawn(move || {
|
||||
multi_tcp_listener::create_tcp_listener(app_handle);
|
||||
});
|
||||
multi_tcp_listener::start_tcp_server(app_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+130
-135
@@ -1,189 +1,184 @@
|
||||
use std::fs::{read_to_string};
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::File;
|
||||
use serde_json::{Value, json};
|
||||
use rust_fuzzy_search::fuzzy_search_best_n;
|
||||
|
||||
use crate::enc_dec_file::{encrypt_n_save_file, decrypt_file};
|
||||
|
||||
static mut PATH_CONFIG: Option<&'static str> = None;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn check_or_create_tasks_file(app_handle: tauri::AppHandle) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
let path: PathBuf = [
|
||||
app_handle.path_resolver().app_local_data_dir().unwrap(),
|
||||
"ToDo".into(),
|
||||
"tasks.enc".into(),
|
||||
].iter().collect(); // Путь к конфиг файлу
|
||||
|
||||
path.set_extension("enc");
|
||||
let path_str = Box::leak(Box::new(path.to_string_lossy().into_owned()));
|
||||
|
||||
let exist: bool = Path::new(&path).exists();
|
||||
if exist{
|
||||
let content = read_to_string(path);
|
||||
return match content {
|
||||
Ok(_) => {},
|
||||
Err(_) => {
|
||||
println!("Не удалось открыть конфигурационный файл!");
|
||||
unsafe {
|
||||
PATH_CONFIG = Some(path_str);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let exist: bool = Path::new(PATH_CONFIG.unwrap()).exists();
|
||||
if exist{
|
||||
let content = read_to_string(path);
|
||||
return match content {
|
||||
Ok(_) => {},
|
||||
Err(_) => {
|
||||
println!("Не удалось открыть конфигурационный файл!");
|
||||
}
|
||||
}
|
||||
} else{
|
||||
let path = Path::new(&path);
|
||||
let prefix = path.parent().unwrap();
|
||||
let file = std::fs::create_dir_all(prefix);
|
||||
match file{
|
||||
Ok(_) => {},
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err)
|
||||
}
|
||||
let file = File::create(path);
|
||||
match file{
|
||||
Ok(_) => {
|
||||
let data = String::from("{\
|
||||
\"id\": 0,\
|
||||
\"tasks\": {}\
|
||||
}");
|
||||
encrypt_n_save_file(path.into(), data);
|
||||
},
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
||||
}
|
||||
}
|
||||
} else{
|
||||
let path = Path::new(&path);
|
||||
let prefix = path.parent().unwrap();
|
||||
let file = std::fs::create_dir_all(prefix);
|
||||
match file{
|
||||
Ok(_) => {},
|
||||
Err(err) => println!("Не удалось создать директории! \n{}", err)
|
||||
}
|
||||
let file = File::create(path);
|
||||
match file{
|
||||
Ok(_) => {
|
||||
let data = String::from("{\
|
||||
\"id\": 0,\
|
||||
\"tasks\": {}\
|
||||
}");
|
||||
encrypt_n_save_file(path.into(), data);
|
||||
},
|
||||
Err(err) => println!("Не удалось создать файл! \n{}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_tasks(app_handle: tauri::AppHandle) -> Value {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
pub fn get_tasks() -> Value {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
let res = &data["tasks"];
|
||||
|
||||
let content = decrypt_file(path);
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
let res = &data["tasks"];
|
||||
|
||||
return res.clone();
|
||||
return res.clone();
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn search_tasks(app_handle: tauri::AppHandle, value: String) -> Value {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
pub fn search_tasks(value: String) -> Value {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
let mut tasks = vec![];
|
||||
|
||||
let content = decrypt_file(path);
|
||||
let data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
let binding = data.clone();
|
||||
for (_key, value) in binding["tasks"].as_object().unwrap() {
|
||||
tasks.push(value["name"].as_str().unwrap().to_lowercase());
|
||||
}
|
||||
|
||||
let mut tasks = vec![];
|
||||
let tasks_str: Vec<&str> = tasks.iter().map(|s| &**s).collect();
|
||||
|
||||
let binding = data.clone();
|
||||
for (_key, value) in binding["tasks"].as_object().unwrap() {
|
||||
tasks.push(value["name"].as_str().unwrap().to_lowercase());
|
||||
}
|
||||
let n: usize = 5;
|
||||
let binding = value.to_lowercase();
|
||||
let res: Vec<(&str, f32)> = fuzzy_search_best_n(&binding, &tasks_str, n);
|
||||
|
||||
let tasks_str: Vec<&str> = tasks.iter().map(|s| &**s).collect();
|
||||
let mut result = json!({});
|
||||
|
||||
let n : usize = 5;
|
||||
let binding = value.to_lowercase();
|
||||
let res : Vec<(&str, f32)> = fuzzy_search_best_n(&binding, &tasks_str, n);
|
||||
|
||||
let mut result = json!({});
|
||||
|
||||
let res1 = res.clone();
|
||||
let binding1 = data.clone();
|
||||
for (name, score) in res1 {
|
||||
if score > 0.6 {
|
||||
for (key, value) in binding1["tasks"].as_object().unwrap() {
|
||||
if value.get("name").unwrap().as_str().unwrap().to_lowercase().as_str() == name {
|
||||
result[key] = binding1["tasks"].get(key).unwrap().clone();
|
||||
break;
|
||||
let res1 = res.clone();
|
||||
let binding1 = data.clone();
|
||||
for (name, score) in res1 {
|
||||
if score > 0.6 {
|
||||
for (key, value) in binding1["tasks"].as_object().unwrap() {
|
||||
if value.get("name").unwrap().as_str().unwrap().to_lowercase().as_str() == name {
|
||||
result[key] = binding1["tasks"].get(key).unwrap().clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn add_task(app_handle: tauri::AppHandle, date: String, time: String, name: String, description: String, priority: String) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
pub fn add_task(date: String, time: String, name: String, description: String, priority: String) -> String {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
let pending = data.clone();
|
||||
let id = &pending["id"];
|
||||
|
||||
let content = decrypt_file(path.clone());
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
let task = json!({
|
||||
"id": id.to_string(),
|
||||
"date": date,
|
||||
"time": time,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"priority": priority,
|
||||
"completed": false
|
||||
});
|
||||
|
||||
let pending = data.clone();
|
||||
let id = &pending["id"];
|
||||
data["id"] = (id.as_i64().unwrap() + 1).into();
|
||||
|
||||
let task = json!({
|
||||
"id": id.to_string(),
|
||||
"date": date,
|
||||
"time": time,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"priority": priority,
|
||||
"completed": false
|
||||
});
|
||||
data["tasks"][id.to_string()] = task;
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
|
||||
data["id"] = (id.as_i64().unwrap() + 1).into();
|
||||
|
||||
data["tasks"][id.to_string()] = task;
|
||||
encrypt_n_save_file(path.into(), data.to_string());
|
||||
return id.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn edit_task(app_handle: tauri::AppHandle, id_task: String, name: String, description: String, priority: String) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
pub fn edit_task(id_task: String, name: String, description: String, priority: String) -> bool{
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
return if !data["tasks"][id_task.clone()].get("id").is_none(){
|
||||
data["tasks"][id_task.clone()]["name"] = json!(name);
|
||||
data["tasks"][id_task.clone()]["description"] = json!(description);
|
||||
data["tasks"][id_task]["priority"] = json!(priority);
|
||||
|
||||
let content = decrypt_file(path.clone());
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
data["tasks"][id_task.clone()]["name"] = json!(name);
|
||||
data["tasks"][id_task.clone()]["description"] = json!(description);
|
||||
data["tasks"][id_task]["priority"] = json!(priority);
|
||||
|
||||
encrypt_n_save_file(path.into(), data.to_string());
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn delete_task(app_handle: tauri::AppHandle, id_task: String) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
|
||||
path.set_extension("enc");
|
||||
|
||||
let content = decrypt_file(path.clone());
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
let tasks = data["tasks"].as_object_mut().unwrap();
|
||||
let binding = tasks.clone();
|
||||
for (key, value) in binding{
|
||||
if id_task == value["id"] {
|
||||
tasks.remove(&key);
|
||||
break;
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
true
|
||||
} else{
|
||||
false
|
||||
}
|
||||
}
|
||||
data["tasks"] = json!(tasks);
|
||||
encrypt_n_save_file(path.into(), data.to_string());
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn set_task_field(app_handle: tauri::AppHandle, id_task: String, field: String, value: String) {
|
||||
let mut path = app_handle.path_resolver().app_local_data_dir().unwrap();
|
||||
path.push("ToDo");
|
||||
path.push("tasks");
|
||||
pub fn delete_task(id_task: String) -> bool{
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
|
||||
path.set_extension("enc");
|
||||
let tasks = data["tasks"].as_object_mut().unwrap();
|
||||
let binding = tasks.clone();
|
||||
for (key, value) in binding {
|
||||
if id_task == value["id"] {
|
||||
tasks.remove(&key);
|
||||
data["tasks"] = json!(tasks);
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
let content = decrypt_file(path.clone());
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
data["tasks"][id_task][field] = json!(value);
|
||||
#[tauri::command]
|
||||
pub fn set_task_field(id_task: String, field: String, value: String) {
|
||||
unsafe {
|
||||
let content = decrypt_file(PathBuf::from(PATH_CONFIG.unwrap()));
|
||||
let mut data: Value = serde_json::from_str(content.as_str()).unwrap();
|
||||
data["tasks"][id_task][field] = json!(value);
|
||||
|
||||
encrypt_n_save_file(path.into(), data.to_string());
|
||||
encrypt_n_save_file(PathBuf::from(PATH_CONFIG.unwrap()), data.to_string());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user