feat: file repo

This commit is contained in:
Gabor Körber 2024-05-12 13:26:21 +02:00
parent 20fc177280
commit 8fa6886e77
5 changed files with 56 additions and 17 deletions

9
Cargo.lock generated
View File

@ -651,15 +651,17 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.31" version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys",
"num-traits", "num-traits",
"serde", "serde",
"windows-targets 0.48.5", "wasm-bindgen",
"windows-targets 0.52.0",
] ]
[[package]] [[package]]
@ -1756,6 +1758,7 @@ dependencies = [
"async-trait", "async-trait",
"axum 0.7.4", "axum 0.7.4",
"barrel", "barrel",
"chrono",
"dotenvy", "dotenvy",
"dunce", "dunce",
"entity", "entity",

View File

@ -53,3 +53,4 @@ slug = "0.1.5"
async-trait = "0.1.77" async-trait = "0.1.77"
tracing = "0.1.40" tracing = "0.1.40"
tower-http = { version = "0.5.1", features = ["trace"] } tower-http = { version = "0.5.1", features = ["trace"] }
chrono = "0.4.38"

View File

@ -1,15 +1,19 @@
use crate::admin::domain::*; use crate::admin::domain::*;
use async_trait::async_trait; use async_trait::async_trait;
use rear::admin::state::AdminRegistry;
use serde_json::Value; use serde_json::Value;
use std::path::PathBuf;
struct FileRepository { struct FileRepository {
directory_path: String, directory_path: PathBuf,
} }
impl FileRepository { impl FileRepository {
pub fn new(folder: String) -> Self { pub fn new(from_path: &str) -> Self {
let proper_path = file_operations::get_proper_path(from_path);
FileRepository { FileRepository {
directory_path: folder, directory_path: proper_path,
} }
} }
} }
@ -36,8 +40,15 @@ impl AdminRepository for FileRepository {
// GET on item collection. // GET on item collection.
async fn list(&self, model: &RepositoryContext) -> RepositoryList { async fn list(&self, model: &RepositoryContext) -> RepositoryList {
if let Some(file_data) = file_operations::directory_info(&self.directory_path) { if let Ok(file_data) = file_operations::directory_info(&self.directory_path) {
return RepositoryList::List(file_data); let items: Vec<RepositoryItem> = file_data.into_iter().map(|fields| {
RepositoryItem {
fields,
detail_url: None, // TODO: change the return type of directory_info into a struct, so we can access key.
change_url: None,
}
}).collect();
return RepositoryList::List { values: items };
} }
RepositoryList::Empty RepositoryList::Empty
} }
@ -82,12 +93,12 @@ impl AdminRepository for FileRepository {
} }
} }
pub fn register(registry: &mut AdminRegistry, db: DatabaseConnection) { pub fn register(registry: &mut AdminRegistry, path: &str) {
let app_key = registry.register_app("Static"); let app_key = registry.register_app("Files");
let repo = FileRepository::new(db); let repo = FileRepository::new(path);
let model_config = AdminModelConfig { let model_config = AdminModelConfig {
app_key: app_key, app_key: app_key,
name: "User".to_owned(), name: "Files".to_owned(),
}; };
let model_result = registry.register_model(model_config, repo); let model_result = registry.register_model(model_config, repo);
match model_result { match model_result {
@ -100,6 +111,15 @@ mod file_operations {
use serde_json::{json, Value}; use serde_json::{json, Value};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::env;
use std::path::PathBuf;
use std::time::SystemTime;
use chrono::{DateTime, Utc, TimeZone};
fn system_time_to_iso_string(system_time: SystemTime) -> String {
let datetime: DateTime<Utc> = system_time.into();
datetime.to_rfc3339()
}
fn get_metadata_as_json(path: &Path) -> Result<Value, Box<dyn std::error::Error>> { fn get_metadata_as_json(path: &Path) -> Result<Value, Box<dyn std::error::Error>> {
let metadata = fs::metadata(path)?; let metadata = fs::metadata(path)?;
@ -118,8 +138,8 @@ mod file_operations {
let is_symlink = file_type.is_symlink(); let is_symlink = file_type.is_symlink();
// Format times as strings if they exist // Format times as strings if they exist
let modified_time_str = modified_time.map(|t| t.to_string()); let modified_time_str = modified_time.map(|t| system_time_to_iso_string(t));
let created_time_str = created_time.map(|t| t.to_string()); let created_time_str = created_time.map(|t| system_time_to_iso_string(t));
Ok(json!({ Ok(json!({
"path": path.display().to_string(), "path": path.display().to_string(),
@ -132,12 +152,12 @@ mod file_operations {
})) }))
} }
fn file_info(file_path: &str) -> Result<Value, Box<dyn std::error::Error>> { pub fn file_info(file_path: &PathBuf) -> Result<Value, Box<dyn std::error::Error>> {
let path = Path::new(file_path); let path = file_path.as_path();
get_metadata_as_json(path) get_metadata_as_json(path)
} }
fn directory_info(directory_path: &str) -> Result<Vec<Value>, Box<dyn std::error::Error>> { pub fn directory_info(directory_path: &PathBuf) -> Result<Vec<Value>, Box<dyn std::error::Error>> {
let entries = fs::read_dir(directory_path)?; let entries = fs::read_dir(directory_path)?;
let mut results = Vec::new(); let mut results = Vec::new();
@ -151,4 +171,16 @@ mod file_operations {
Ok(results) Ok(results)
} }
pub fn get_proper_path(some_path: &str) -> PathBuf {
let path = Path::new(some_path);
if path.is_absolute() {
return path.to_path_buf();
} else {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
let template_path = PathBuf::from(manifest_dir).join(path);
return template_path;
}
}
} }

View File

@ -1,3 +1,4 @@
pub mod empty_repository; pub mod empty_repository;
pub mod static_repository; pub mod static_repository;
pub mod user_repository; pub mod user_repository;
pub mod file_repository;

View File

@ -7,6 +7,7 @@ mod state;
use crate::state::AppState; use crate::state::AppState;
use admin_examples::static_repository; use admin_examples::static_repository;
use admin_examples::user_repository; use admin_examples::user_repository;
use admin_examples::file_repository;
use axum::{ use axum::{
body::Bytes, body::Bytes,
extract::MatchedPath, extract::MatchedPath,
@ -53,6 +54,7 @@ async fn main() {
// Register Admin Apps // Register Admin Apps
static_repository::register(&mut admin); static_repository::register(&mut admin);
user_repository::register(&mut admin, db_connection); user_repository::register(&mut admin, db_connection);
file_repository::register(&mut admin, "static/admin");
// Create global Application State. // Create global Application State.
let state: AppState = AppState { let state: AppState = AppState {