This commit is contained in:
parent
c5038dd610
commit
4a1f985cce
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "discoursio-quoter"
|
name = "discoursio-quoter"
|
||||||
version = "0.0.4"
|
version = "0.0.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -4,7 +4,7 @@ use aws_sdk_s3::{config::Credentials, Client as S3Client};
|
||||||
use redis::{aio::MultiplexedConnection, AsyncCommands, Client as RedisClient};
|
use redis::{aio::MultiplexedConnection, AsyncCommands, Client as RedisClient};
|
||||||
use std::{env, time::Duration};
|
use std::{env, time::Duration};
|
||||||
use tokio::time::interval;
|
use tokio::time::interval;
|
||||||
|
use std::collections::HashMap;
|
||||||
use crate::s3_utils::check_file_exists;
|
use crate::s3_utils::check_file_exists;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -110,12 +110,13 @@ impl AppState {
|
||||||
for object in objects.iter() {
|
for object in objects.iter() {
|
||||||
if let Some(key) = &object.key {
|
if let Some(key) = &object.key {
|
||||||
let parts: Vec<&str> = key.split('.').collect();
|
let parts: Vec<&str> = key.split('.').collect();
|
||||||
if parts.len() > 1 && !parts.last().unwrap().contains('/') {
|
let filename = parts.first().unwrap_or(&"");
|
||||||
let filename = parts[0..parts.len()-1].join(".");
|
let ext = parts.get(1).unwrap_or(&"");
|
||||||
file_list.entry(filename).or_insert(key.clone());
|
if ext.contains('/') {
|
||||||
} else {
|
continue;
|
||||||
file_list.entry(key.to_string()).or_insert(key.clone());
|
|
||||||
}
|
}
|
||||||
|
let filename_with_extension = format!("{}.{}", filename, ext);
|
||||||
|
file_list.insert(filename_with_extension, key.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,25 +151,25 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Сохраняет маппинг старого пути из AWS S3 на новый путь в Storj S3.
|
/// Сохраняет маппинг старого пути из AWS S3 на новый путь в Storj S3.
|
||||||
async fn save_path_by_filekey(
|
async fn save_path_by_filename_with_extension(
|
||||||
&self,
|
&self,
|
||||||
filekey: &str,
|
filename_with_extension: &str,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> Result<(), actix_web::Error> {
|
) -> Result<(), actix_web::Error> {
|
||||||
let mut redis = self.redis.clone();
|
let mut redis = self.redis.clone();
|
||||||
// Храним маппинг в формате Hash: old_path -> new_path
|
// Храним маппинг в формате Hash: old_path -> new_path
|
||||||
redis
|
redis
|
||||||
.hset::<_, &str, &str, ()>(PATH_MAPPING_KEY, filekey, path)
|
.hset::<_, &str, &str, ()>(PATH_MAPPING_KEY, filename_with_extension, path)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to save path mapping in Redis"))?;
|
.map_err(|_| ErrorInternalServerError("Failed to save path mapping in Redis"))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Получает путь в хранилище из ключа (имени файла) в Redis.
|
/// Получает путь в хранилище из ключа (имени файла) в Redis.
|
||||||
pub async fn get_path(&self, filekey: &str) -> Result<Option<String>, actix_web::Error> {
|
pub async fn get_path(&self, filename_with_extension: &str) -> Result<Option<String>, actix_web::Error> {
|
||||||
let mut redis = self.redis.clone();
|
let mut redis = self.redis.clone();
|
||||||
let new_path: Option<String> = redis
|
let new_path: Option<String> = redis
|
||||||
.hget(PATH_MAPPING_KEY, filekey)
|
.hget(PATH_MAPPING_KEY, filename_with_extension)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to get path mapping from Redis"))?;
|
.map_err(|_| ErrorInternalServerError("Failed to get path mapping from Redis"))?;
|
||||||
Ok(new_path)
|
Ok(new_path)
|
||||||
|
@ -186,35 +187,38 @@ impl AppState {
|
||||||
for object in objects {
|
for object in objects {
|
||||||
if let Some(key) = object.key {
|
if let Some(key) = object.key {
|
||||||
// Получаем имя файла с расширением
|
// Получаем имя файла с расширением
|
||||||
let [filename, ext] = key.split('.').collect::<Vec<&str>>();
|
let parts: Vec<&str> = key.split('.').collect();
|
||||||
|
let filename = parts.first().unwrap_or(&"");
|
||||||
|
let ext = parts.get(1).unwrap_or(&"");
|
||||||
|
if ext.contains('/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let filename_with_extension = format!("{}.{}", filename, ext);
|
||||||
|
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
eprint!("Пустое имя файла {}", key);
|
eprint!("[ERROR] empty filename: {}", key);
|
||||||
} else {
|
} else {
|
||||||
// Проверяем, существует ли файл на Storj S3
|
// Проверяем, существует ли файл на Storj S3
|
||||||
match check_file_exists(&self.s3_client, &self.s3_bucket, filename)
|
match check_file_exists(&self.s3_client, &self.s3_bucket, &filename_with_extension)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
// Сохраняем маппинг пути
|
// Сохраняем маппинг пути
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
self.save_path_by_filekey(filename, &key).await
|
self.save_path_by_filename_with_extension(&filename_with_extension, &key).await
|
||||||
{
|
{
|
||||||
eprintln!(
|
eprintln!("[ERROR] save {}: {:?}", key, e);
|
||||||
"[ОШИБКА СОХРАНЕНИЯ] {}: {:?}",
|
|
||||||
filename_with_extension, e
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
println!("{}", filename_with_extension);
|
println!("{}", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
println!("Файл {} уже существует в Storj.", filename);
|
println!("Already exists in Storj: {}", filename_with_extension);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Ошибка при проверке файла {} на Storj: {:?}",
|
"[ERROR] check {}: {:?}",
|
||||||
filename, e
|
filename_with_extension, e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,11 +226,11 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Список файлов в AWS S3 пуст.");
|
println!("AWS S3 file list is empty.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Не удалось получить список файлов из AWS S3: {:?}", e);
|
eprintln!("[ERROR] get AWS S3 file list: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,14 @@ use crate::thumbnail::{
|
||||||
pub const MAX_WEEK_BYTES: u64 = 2 * 1024 * 1024 * 1024;
|
pub const MAX_WEEK_BYTES: u64 = 2 * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
/// Функция для обслуживания файла по заданному пути.
|
/// Функция для обслуживания файла по заданному пути.
|
||||||
async fn serve_file(file_key: &str, state: &AppState) -> Result<HttpResponse, actix_web::Error> {
|
async fn serve_file(filename_with_extension: &str, state: &AppState) -> Result<HttpResponse, actix_web::Error> {
|
||||||
// Проверяем наличие файла в Storj S3
|
// Проверяем наличие файла в Storj S3
|
||||||
if !check_file_exists(&state.s3_client, &state.s3_bucket, file_key).await? {
|
if !check_file_exists(&state.s3_client, &state.s3_bucket, filename_with_extension).await? {
|
||||||
warn!("{}", file_key);
|
warn!("{}", filename_with_extension);
|
||||||
return Err(ErrorInternalServerError("File not found in S3"));
|
return Err(ErrorInternalServerError("File not found in S3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let checked_filekey = state.get_path(file_key).await.unwrap().unwrap();
|
let checked_filekey = state.get_path(filename_with_extension).await.unwrap().unwrap();
|
||||||
|
|
||||||
// Получаем объект из Storj S3
|
// Получаем объект из Storj S3
|
||||||
let get_object_output = state
|
let get_object_output = state
|
||||||
|
@ -43,7 +43,7 @@ async fn serve_file(file_key: &str, state: &AppState) -> Result<HttpResponse, ac
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to read object body"))?;
|
.map_err(|_| ErrorInternalServerError("Failed to read object body"))?;
|
||||||
|
|
||||||
let data_bytes = data.into_bytes();
|
let data_bytes = data.into_bytes();
|
||||||
let mime_type = MimeGuess::from_path(file_key).first_or_octet_stream(); // Определяем MIME-тип файла
|
let mime_type = MimeGuess::from_path(filename_with_extension).first_or_octet_stream(); // Определяем MIME-тип файла
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type(mime_type.as_ref())
|
.content_type(mime_type.as_ref())
|
||||||
|
@ -126,7 +126,7 @@ pub async fn proxy_handler(
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
) -> Result<HttpResponse, actix_web::Error> {
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
info!("proxy_handler вызван с путем: {}", path);
|
info!("proxy_handler: {}", path);
|
||||||
|
|
||||||
let requested_path = match state.get_path(&path).await {
|
let requested_path = match state.get_path(&path).await {
|
||||||
Ok(Some(path)) => path,
|
Ok(Some(path)) => path,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user