mirror of
https://github.com/shiroyashik/sculptor.git
synced 2025-12-06 13:01:12 +03:00
Configuration auto-update fixed and hex replaced with faster_hex
This commit is contained in:
parent
4c0871e26c
commit
2f8ab4cfa8
14 changed files with 589 additions and 210 deletions
|
|
@ -71,7 +71,7 @@ async fn index_assets(version: &str) -> anyhow::Result<IndexMap<String, Value>>
|
|||
entry.path().strip_prefix(version_path.clone())?.to_string_lossy().to_string()
|
||||
};
|
||||
|
||||
map.insert(path, Value::from(hex::encode(digest(&SHA256, &data).as_ref())));
|
||||
map.insert(path, Value::from(faster_hex::hex_string(digest(&SHA256, &data).as_ref())));
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ async fn id(
|
|||
State(state): State<AppState>,
|
||||
) -> String {
|
||||
let server_id =
|
||||
hex::encode(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
|
||||
faster_hex::hex_string(&digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, &rand()).as_ref()[0..20]);
|
||||
let state = state.user_manager;
|
||||
state.pending_insert(server_id.clone(), query.username);
|
||||
server_id
|
||||
|
|
@ -32,11 +32,11 @@ async fn verify(
|
|||
State(state): State<AppState>,
|
||||
) -> Response {
|
||||
let server_id = query.id.clone();
|
||||
let username = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
|
||||
let nickname = state.user_manager.pending_remove(&server_id).unwrap().1; // TODO: Add error check
|
||||
let userinfo = match has_joined(
|
||||
state.config.read().await.auth_providers.clone(),
|
||||
&server_id,
|
||||
&username
|
||||
&nickname
|
||||
).await {
|
||||
Ok(d) => d,
|
||||
Err(_e) => {
|
||||
|
|
@ -47,12 +47,12 @@ async fn verify(
|
|||
if let Some((uuid, auth_provider)) = userinfo {
|
||||
let umanager = state.user_manager;
|
||||
if umanager.is_banned(&uuid) {
|
||||
info!("[Authentication] {username} tried to log in, but was banned");
|
||||
info!("[Authentication] {nickname} tried to log in, but was banned");
|
||||
return (StatusCode::BAD_REQUEST, "You're banned!".to_string()).into_response();
|
||||
}
|
||||
info!("[Authentication] {username} logged in using {}", auth_provider.name);
|
||||
info!("[Authentication] {nickname} logged in using {}", auth_provider.name);
|
||||
let userinfo = Userinfo {
|
||||
username,
|
||||
nickname,
|
||||
uuid,
|
||||
token: Some(server_id.clone()),
|
||||
auth_provider,
|
||||
|
|
@ -70,7 +70,7 @@ async fn verify(
|
|||
}
|
||||
(StatusCode::OK, server_id.to_string()).into_response()
|
||||
} else {
|
||||
info!("[Authentication] failed to verify {username}");
|
||||
info!("[Authentication] failed to verify {nickname}");
|
||||
(StatusCode::BAD_REQUEST, "failed to verify".to_string()).into_response()
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ pub async fn upload_avatar(
|
|||
tracing::info!(
|
||||
"{} ({}) trying to upload an avatar",
|
||||
user_info.uuid,
|
||||
user_info.username
|
||||
user_info.nickname
|
||||
);
|
||||
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
|
||||
let mut file = BufWriter::new(fs::File::create(&avatar_file).await.map_err(internal_and_log)?);
|
||||
|
|
@ -121,7 +121,7 @@ pub async fn delete_avatar(Token(token): Token, State(state): State<AppState>) -
|
|||
tracing::info!(
|
||||
"{} ({}) is trying to delete the avatar",
|
||||
user_info.uuid,
|
||||
user_info.username
|
||||
user_info.nickname
|
||||
);
|
||||
let avatar_file = format!("{}/{}.moon", *AVATARS_VAR, user_info.uuid);
|
||||
fs::remove_file(avatar_file).await.map_err(internal_and_log)?;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ async fn handle_socket(mut ws: WebSocket, state: AppState) {
|
|||
}
|
||||
|
||||
async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppState) -> anyhow::Result<()> {
|
||||
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.username);
|
||||
tracing::debug!("WebSocket control for {} is transferred to the main worker", session.user.nickname);
|
||||
loop {
|
||||
tokio::select! {
|
||||
external_msg = ws.recv_and_decode() => {
|
||||
|
|
@ -96,7 +96,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
let _ = session.subs_tx.send(s2c_ping);
|
||||
},
|
||||
C2SMessage::Sub(uuid) => {
|
||||
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.username, uuid);
|
||||
tracing::debug!("[WebSocket] {} subscribes to {}", session.user.nickname, uuid);
|
||||
|
||||
// Doesn't allow to subscribe to yourself
|
||||
if session.user.uuid != uuid {
|
||||
|
|
@ -114,11 +114,11 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
}
|
||||
},
|
||||
C2SMessage::Unsub(uuid) => {
|
||||
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.username, uuid);
|
||||
tracing::debug!("[WebSocket] {} unsubscribes from {}", session.user.nickname, uuid);
|
||||
|
||||
match session.sub_workers_aborthandles.get(&uuid) {
|
||||
Some(handle) => handle.abort(),
|
||||
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.username),
|
||||
None => tracing::warn!("[WebSocket] {} was not subscribed.", session.user.nickname),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ async fn main_worker(session: &mut WSSession, ws: &mut WebSocket, state: &AppSta
|
|||
.inspect_err(
|
||||
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
|
||||
);
|
||||
bail!("{} banned!", session.user.username)
|
||||
bail!("{} banned!", session.user.nickname)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ async fn authenticate(socket: &mut WebSocket, state: &AppState) -> Result<Userin
|
|||
.inspect_err(
|
||||
|kind| tracing::warn!("[WebSocket] Didn't get the ban message due to {}", kind)
|
||||
);
|
||||
Err(AuthModeError::Banned(user.username.clone()))
|
||||
Err(AuthModeError::Banned(user.nickname.clone()))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl RecvAndDecode for WebSocket {
|
|||
match C2SMessage::try_from(msg.clone().into_data().as_slice()) {
|
||||
Ok(decoded) => Ok(decoded),
|
||||
Err(e) => {
|
||||
Err(RADError::DecodeError(e, hex::encode(msg.into_data())))
|
||||
Err(RADError::DecodeError(e, faster_hex::hex_string(&msg.into_data())))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ pub(super) async fn raw(
|
|||
) -> ApiResult<&'static str> {
|
||||
trace!(body = body);
|
||||
state.config.read().await.clone().verify_token(&token)?;
|
||||
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
let mut payload = vec![0; body.len() / 2];
|
||||
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
debug!("{:?}", payload);
|
||||
|
||||
match query.uuid {
|
||||
|
|
@ -47,7 +48,8 @@ pub(super) async fn sub_raw(
|
|||
) -> ApiResult<&'static str> {
|
||||
trace!(body = body);
|
||||
state.config.read().await.clone().verify_token(&token)?;
|
||||
let payload = hex::decode(body).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
let mut payload = vec![0; body.len() / 2];
|
||||
faster_hex::hex_decode(body.as_bytes(), &mut payload).map_err(|err| { warn!("not raw data"); error_and_log(err, crate::ApiError::NotAcceptable) })?;
|
||||
debug!("{:?}", payload);
|
||||
|
||||
match query.uuid {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,12 @@ pub struct Token(pub String);
|
|||
|
||||
impl Token {
|
||||
pub async fn check_auth(self, state: &AppState) -> ApiResult<()> {
|
||||
if state.user_manager.is_authenticated(&self.0) {
|
||||
Ok(())
|
||||
if let Some(user) = state.user_manager.get(&self.0) {
|
||||
if !user.banned {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ApiError::Unauthorized)
|
||||
}
|
||||
} else {
|
||||
Err(ApiError::Unauthorized)
|
||||
}
|
||||
|
|
@ -191,7 +195,7 @@ impl UManager {
|
|||
warn!("Rejected attempt to create a second session for the same user!");
|
||||
return Err(())
|
||||
}
|
||||
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.username);
|
||||
debug!("`{}` already have token in registered profile (old token already removed from 'authenticated')", userinfo.nickname);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +209,7 @@ impl UManager {
|
|||
let usercopy = userinfo.clone();
|
||||
self.registered.entry(uuid)
|
||||
.and_modify(|exist| {
|
||||
if !userinfo.username.is_empty() { exist.username = userinfo.username };
|
||||
if !userinfo.nickname.is_empty() { exist.nickname = userinfo.nickname };
|
||||
if !userinfo.auth_provider.is_empty() { exist.auth_provider = userinfo.auth_provider };
|
||||
if userinfo.rank != Userinfo::default().rank { exist.rank = userinfo.rank };
|
||||
if userinfo.token.is_some() { exist.token = userinfo.token };
|
||||
|
|
@ -236,7 +240,7 @@ impl UManager {
|
|||
user.banned = false;
|
||||
};
|
||||
}
|
||||
pub fn is_authenticated(&self, token: &String) -> bool {
|
||||
pub fn _is_authenticated(&self, token: &String) -> bool {
|
||||
self.authenticated.contains_key(token)
|
||||
}
|
||||
pub fn _is_registered(&self, uuid: &Uuid) -> bool {
|
||||
|
|
|
|||
|
|
@ -6,21 +6,20 @@ use uuid::Uuid;
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Userinfo {
|
||||
pub uuid: Uuid,
|
||||
pub username: String,
|
||||
pub nickname: String,
|
||||
pub rank: String,
|
||||
pub last_used: String,
|
||||
pub auth_provider: AuthProvider,
|
||||
pub token: Option<String>,
|
||||
pub version: String,
|
||||
pub banned: bool
|
||||
|
||||
}
|
||||
|
||||
impl Default for Userinfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
uuid: Default::default(),
|
||||
username: Default::default(),
|
||||
nickname: Default::default(),
|
||||
rank: "default".to_string(),
|
||||
last_used: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||
auth_provider: Default::default(),
|
||||
|
|
|
|||
29
src/main.rs
29
src/main.rs
|
|
@ -93,7 +93,6 @@ async fn main() -> Result<()> {
|
|||
|
||||
// 3. Display info about current instance and check updates
|
||||
tracing::info!("The Sculptor v{SCULPTOR_VERSION} ({REPOSITORY})");
|
||||
// let _ = check_updates(REPOSITORY, SCULPTOR_VERSION).await; // Currently, there is no need to do anything with the result of the function
|
||||
|
||||
match get_latest_version(REPOSITORY).await {
|
||||
Ok(latest_version) => {
|
||||
|
|
@ -168,25 +167,13 @@ async fn app() -> Result<bool> {
|
|||
config,
|
||||
};
|
||||
|
||||
// FIXME: FIXME: FIXME: ПЕРЕДЕЛАЙ ЭТО! НЕМЕДЛЕННО! ЕБУЧИЙ ПОЗОР :<
|
||||
// Automatic update of configuration while the server is running
|
||||
let config_update = Arc::clone(&state.config);
|
||||
let umanager = Arc::clone(&state.user_manager);
|
||||
let session = Arc::clone(&state.session);
|
||||
update_advanced_users(&config_update.read().await.advanced_users.clone(), &umanager, &session).await;
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
let new_config = Config::parse(CONFIG_VAR.clone().into());
|
||||
let mut config = config_update.write().await;
|
||||
|
||||
if new_config != *config {
|
||||
tracing::info!("Server configuration modification detected!");
|
||||
*config = new_config;
|
||||
update_advanced_users(&config.advanced_users.clone(), &umanager, &session).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Automatic update of configuration/ban list while the server is running
|
||||
tokio::spawn(update_advanced_users(
|
||||
CONFIG_VAR.clone().into(),
|
||||
Arc::clone(&state.user_manager),
|
||||
Arc::clone(&state.session),
|
||||
Arc::clone(&state.config)
|
||||
));
|
||||
if state.config.read().await.mc_folder.exists() {
|
||||
tokio::spawn(update_bans_from_minecraft(
|
||||
state.config.read().await.mc_folder.clone(),
|
||||
|
|
@ -242,11 +229,9 @@ async fn shutdown_signal() {
|
|||
let terminate = std::future::pending::<()>();
|
||||
tokio::select! {
|
||||
() = ctrl_c => {
|
||||
println!();
|
||||
tracing::info!("Ctrl+C signal received");
|
||||
},
|
||||
() = terminate => {
|
||||
println!();
|
||||
tracing::info!("Terminate signal received");
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl From<BannedPlayer> for Userinfo {
|
|||
fn from(val: BannedPlayer) -> Self {
|
||||
Userinfo {
|
||||
uuid: val.uuid,
|
||||
username: val.name,
|
||||
nickname: val.name,
|
||||
banned: true,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
use std::{fs::File, io::Read, path::{Path, PathBuf}};
|
||||
use std::{fs::File, io::Read, path::{Path, PathBuf}, sync::Arc};
|
||||
|
||||
use notify::{Event, Watcher};
|
||||
use tokio::{io::AsyncReadExt, sync::RwLock};
|
||||
use base64::prelude::*;
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use rand::{thread_rng, Rng};
|
||||
use ring::digest::{self, digest};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
use chrono::prelude::*;
|
||||
|
||||
use crate::{auth::{UManager, Userinfo}, state::{AdvancedUsers, BannedPlayer}};
|
||||
use crate::{auth::Userinfo, state::{BannedPlayer, Config}, UManager};
|
||||
|
||||
// Core functions
|
||||
pub fn rand() -> [u8; 50] {
|
||||
let mut rng = thread_rng();
|
||||
let distr = rand::distributions::Uniform::new_inclusive(0, 255);
|
||||
|
|
@ -20,48 +19,63 @@ pub fn rand() -> [u8; 50] {
|
|||
}
|
||||
nums
|
||||
}
|
||||
// End of Core functions
|
||||
|
||||
pub fn _generate_hex_string(length: usize) -> String {
|
||||
// FIXME: Variable doesn't using!
|
||||
let rng = thread_rng();
|
||||
let random_bytes: Vec<u8> = rng.sample_iter(&Alphanumeric).take(length / 2).collect();
|
||||
|
||||
hex::encode(random_bytes)
|
||||
}
|
||||
|
||||
pub async fn update_advanced_users(
|
||||
value: &std::collections::HashMap<Uuid, AdvancedUsers>,
|
||||
umanager: &UManager,
|
||||
sessions: &dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>
|
||||
path: PathBuf,
|
||||
umanager: Arc<UManager>,
|
||||
sessions: Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>,
|
||||
config: Arc<RwLock<Config>>,
|
||||
) {
|
||||
let users: Vec<(Uuid, Userinfo)> = value
|
||||
.iter()
|
||||
.map( |(uuid, userdata)| {
|
||||
(
|
||||
*uuid,
|
||||
Userinfo {
|
||||
uuid: *uuid,
|
||||
username: userdata.username.clone(),
|
||||
banned: userdata.banned,
|
||||
..Default::default()
|
||||
}
|
||||
)})
|
||||
.collect();
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
|
||||
tx.send(Ok(notify::Event::default())).await.unwrap();
|
||||
let mut watcher = notify::RecommendedWatcher::new(
|
||||
move |res| {
|
||||
tx.blocking_send(res).unwrap();
|
||||
},
|
||||
notify::Config::default(),
|
||||
).unwrap();
|
||||
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
|
||||
|
||||
for (uuid, userinfo) in users {
|
||||
umanager.insert_user(uuid, userinfo.clone());
|
||||
if userinfo.banned {
|
||||
umanager.ban(&userinfo);
|
||||
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
let mut first_time = true;
|
||||
while rx.recv().await.is_some() {
|
||||
let new_config = Config::parse(path.clone());
|
||||
let mut config = config.write().await;
|
||||
|
||||
if new_config != *config || first_time {
|
||||
if !first_time { tracing::info!("Server configuration modification detected!") }
|
||||
first_time = false;
|
||||
*config = new_config;
|
||||
let users: Vec<(Uuid, Userinfo)> = config.advanced_users
|
||||
.iter()
|
||||
.map( |(uuid, userdata)| {
|
||||
(
|
||||
*uuid,
|
||||
Userinfo {
|
||||
uuid: *uuid,
|
||||
nickname: userdata.username.clone(),
|
||||
banned: userdata.banned,
|
||||
..Default::default()
|
||||
}
|
||||
)})
|
||||
.collect();
|
||||
|
||||
for (uuid, userinfo) in users {
|
||||
umanager.insert_user(uuid, userinfo.clone());
|
||||
if userinfo.banned {
|
||||
umanager.ban(&userinfo);
|
||||
if let Some(tx) = sessions.get(&uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
} else {
|
||||
umanager.unban(&uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_bans_from_minecraft(
|
||||
folder: PathBuf,
|
||||
umanager: std::sync::Arc<UManager>,
|
||||
sessions: std::sync::Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>
|
||||
umanager: Arc<UManager>,
|
||||
sessions: Arc<dashmap::DashMap<Uuid, tokio::sync::mpsc::Sender<crate::api::figura::SessionMessage>>>
|
||||
) {
|
||||
let path = folder.join("banned-players.json");
|
||||
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or banned-players.json doesn't exists!");
|
||||
|
|
@ -71,10 +85,10 @@ pub async fn update_bans_from_minecraft(
|
|||
// initialize
|
||||
file.read_to_string(&mut data).await.expect("cant read banned-players.json");
|
||||
let mut old_bans: Vec<BannedPlayer> = serde_json::from_str(&data).expect("cant parse banned-players.json");
|
||||
|
||||
|
||||
if !old_bans.is_empty() {
|
||||
let names: Vec<String> = old_bans.iter().map(|user| user.name.clone()).collect();
|
||||
info!("Banned players: {}", names.join(", "));
|
||||
tracing::info!("Banned players: {}", names.join(", "));
|
||||
}
|
||||
|
||||
for player in &old_bans {
|
||||
|
|
@ -82,19 +96,27 @@ pub async fn update_bans_from_minecraft(
|
|||
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
}
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<notify::Result<Event>>(1);
|
||||
let mut watcher = notify::RecommendedWatcher::new(
|
||||
move |res| {
|
||||
tx.blocking_send(res).unwrap();
|
||||
},
|
||||
notify::Config::default(),
|
||||
).unwrap();
|
||||
watcher.watch(&path, notify::RecursiveMode::NonRecursive).unwrap();
|
||||
|
||||
// old_bans
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
while rx.recv().await.is_some() {
|
||||
let mut file = tokio::fs::File::open(path.clone()).await.expect("Access denied or file doesn't exists!");
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).await.expect("cant read banned-players.json");
|
||||
let new_bans: Vec<BannedPlayer> = if let Ok(res) = serde_json::from_str(&data) { res } else {
|
||||
error!("Error occured while parsing a banned-players.json");
|
||||
tracing::error!("Error occured while parsing a banned-players.json");
|
||||
continue;
|
||||
};
|
||||
|
||||
if new_bans != old_bans {
|
||||
info!("Minecraft ban list modification detected!");
|
||||
tracing::info!("Minecraft ban list modification detected!");
|
||||
let unban: Vec<&BannedPlayer> = old_bans.iter().filter(|user| !new_bans.contains(user)).collect();
|
||||
let mut unban_names = unban.iter().map(|user| user.name.clone()).collect::<Vec<String>>().join(", ");
|
||||
if !unban.is_empty() {
|
||||
|
|
@ -110,14 +132,13 @@ pub async fn update_bans_from_minecraft(
|
|||
if let Some(tx) = sessions.get(&player.uuid) {let _ = tx.send(crate::api::figura::SessionMessage::Banned).await;}
|
||||
}
|
||||
} else { ban_names = String::from("-")};
|
||||
info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");
|
||||
tracing::info!("List of changes:\n Banned: {ban_names}\n Unbanned: {unban_names}");
|
||||
// Write new to old for next iteration
|
||||
old_bans = new_bans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn format_uuid(uuid: &Uuid) -> String {
|
||||
// let uuid = Uuid::parse_str(&uuid)?; TODO: Вероятно format_uuid стоит убрать
|
||||
// .map_err(|_| tide::Error::from_str(StatusCode::InternalServerError, "Failed to parse UUID"))?;
|
||||
|
|
@ -138,7 +159,7 @@ pub fn calculate_file_sha256(file_path: &str) -> Result<String, std::io::Error>
|
|||
let hash = binding.as_ref();
|
||||
|
||||
// Convert the hash to a hexadecimal string
|
||||
let hex_hash = hex::encode(hash);
|
||||
let hex_hash = faster_hex::hex_string(hash);
|
||||
|
||||
Ok(hex_hash)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue