Get icons working

This commit is contained in:
Jackzie 2025-04-15 00:08:07 -05:00
parent fdebc535a0
commit c6006dd592
9 changed files with 170 additions and 22 deletions

View file

@ -1,5 +1,5 @@
use std::sync::Arc; use std::sync::Arc;
use log::debug; use log::{debug, error, info, trace, warn};
use rocket::{catch, launch, routes, Request, State}; use rocket::{catch, launch, routes, Request, State};
use rocket::data::ByteUnit; use rocket::data::ByteUnit;
use rocket::fs::{relative, FileServer}; use rocket::fs::{relative, FileServer};
@ -34,7 +34,11 @@ async fn rocket() -> _ {
setup_logger(); setup_logger();
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
let handlebars = Handlebars::new(); trace!("trace");
debug!("debug");
info!("info");
warn!("warn");
error!("error");
let pool = PgPoolOptions::new() let pool = PgPoolOptions::new()
.max_connections(5) .max_connections(5)
@ -61,13 +65,16 @@ async fn rocket() -> _ {
.manage(repo_manager) .manage(repo_manager)
.manage(libraries_manager) .manage(libraries_manager)
.mount("/static", FileServer::from(relative!("static"))) .mount("/static", FileServer::from(relative!("static")))
.mount("/api/library", routes![
api::library::move_file, api::library::upload_file, api::library::download_file, api::library::list_files, api::library::get_file, api::library::delete_file,
])
.mount("/", routes![ .mount("/", routes![
ui::user::index, ui::user::list_library_files ui::user::index, ui::user::list_library_files
]) ])
.mount("/api", routes![ .attach(Template::custom(|engines| {
api::library::move_file, api::library::upload_file, api::library::download_file, api::library::list_files, api::library::get_file, api::library::delete_file, let _ = engines
]) .handlebars;
.attach(Template::fairing()) }))
} }

View file

@ -17,12 +17,39 @@ pub enum StorageBackendMap {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct FileEntry { #[serde(rename_all = "lowercase")]
pub file_name: String, pub enum FileType {
// last_modified: File,
pub file_size: u64, Folder,
Symlink,
Other
} }
impl From<std::fs::FileType> for FileType {
fn from(value: std::fs::FileType) -> Self {
if value.is_file() {
FileType::File
} else if value.is_dir() {
FileType::Folder
} else if value.is_symlink() {
FileType::Symlink
} else {
FileType::Other
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FileEntry {
pub path: String,
// last_modified:
pub size: u64,
#[serde(rename="type")]
pub _type: FileType,
}
pub fn get_backend(storage_type: &str, settings: &JsonValue) -> Result<Option<Box<dyn StorageBackend + Send + Sync>>, anyhow::Error> { pub fn get_backend(storage_type: &str, settings: &JsonValue) -> Result<Option<Box<dyn StorageBackend + Send + Sync>>, anyhow::Error> {
Ok(match storage_type { Ok(match storage_type {
"local" => Some(Box::new(LocalStorage::new(settings)?)), "local" => Some(Box::new(LocalStorage::new(settings)?)),

View file

@ -19,7 +19,10 @@ impl LocalStorage {
} }
} }
fn get_path(folder_root: &PathBuf, library_id: &str, path: &PathBuf) -> Result<PathBuf, anyhow::Error> { fn get_path(folder_root: &PathBuf, library_id: &str, mut path: &Path) -> Result<PathBuf, anyhow::Error> {
if path.starts_with("/") {
path = path.strip_prefix("/")?
}
let path = folder_root.join(library_id).join(path); let path = folder_root.join(library_id).join(path);
// Prevent path traversal // Prevent path traversal
debug!("root={:?}", folder_root); debug!("root={:?}", folder_root);
@ -51,9 +54,12 @@ impl StorageBackend for LocalStorage {
.map(|entry| entry.unwrap()) .map(|entry| entry.unwrap())
.map(|entry| { .map(|entry| {
let meta = entry.metadata().unwrap(); let meta = entry.metadata().unwrap();
let file_type = meta.file_type().into();
// TODO: filter out 'other'
FileEntry { FileEntry {
file_name: entry.file_name().into_string().unwrap(), _type: file_type,
file_size: meta.size() path: entry.file_name().into_string().unwrap(),
size: meta.size()
} }
}) })
.collect()) .collect())

View file

@ -1,8 +1,11 @@
use std::fs;
use std::io::Cursor; use std::io::Cursor;
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::{response, Request, Response}; use rocket::{response, Request, Response};
use rocket::fs::relative;
use rocket::response::Responder; use rocket::response::Responder;
use rocket::serde::Serialize; use rocket::serde::Serialize;
use rocket_dyn_templates::handlebars::Handlebars;
use sqlx::Error; use sqlx::Error;
use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::util::SubscriberInitExt;
@ -12,12 +15,32 @@ pub(crate) fn setup_logger() {
tracing_subscriber::registry() tracing_subscriber::registry()
.with( .with(
tracing_subscriber::filter::EnvFilter::try_from_default_env() tracing_subscriber::filter::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| format!("warn,rocket=trace,storage-server=trace").into()), .unwrap_or_else(|_| format!("warn,rocket=trace,storage_server=trace").into()),
) )
.with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::fmt::layer())
.init(); .init();
} }
// pub(crate) fn setup_template_engine() -> Handlebars<'static> {
// let mut hb = Handlebars::new();
// #[cfg(debug_assertions)]
// hb.set_dev_mode(true);
//
// let templates = fs::read_dir(relative!("templates")).unwrap();
// let mut ok = true;
// for file in templates {
// let file = file.unwrap();
// if let Err(e) = hb.register_template_file(file.path().to_str().unwrap(), ) {
// error!(template, path = %path.display(),
// "failed to register Handlebars template: {e}");
//
// ok = false;
// }
// }
//
// hb
// }
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct JsonErrorResponse { pub struct JsonErrorResponse {
pub(crate) code: String, pub(crate) code: String,

View file

@ -26,4 +26,33 @@
} }
.sidebar-list li button:hover, .sidebar-list li button.is-active { .sidebar-list li button:hover, .sidebar-list li button.is-active {
background-color: lightgray; background-color: lightgray;
}
.sidebar-column {
height: 100%;
}
html,body {
height: 100%;
}
.sidebar-column {
border-right: 1px solid lightgray;
}
tr.file-list td {
vertical-align: middle;
padding: 0;
}
tr.file-list td input[type="checkbox"] {
margin: 20px; /** this weirdly makes it look good TODO: fix */
-ms-transform: scale(1.5);
/* IE */
-moz-transform: scale(1.5);
/* FF */
-webkit-transform: scale(1.5);
/* Safari and Chrome */
-o-transform: scale(1.5);
/* Opera */
transform: scale(1.5);
padding: 10px;
} }

View file

@ -10,12 +10,12 @@
<link href="/static/css/main.css" rel="stylesheet" /> <link href="/static/css/main.css" rel="stylesheet" />
<link rel="stylesheet" href="/static/css/bulma.min.css"> <link rel="stylesheet" href="/static/css/bulma.min.css">
<link href="/static/icons/css/fontawesome.css" rel="stylesheet" /> <link href="/static/icons/css/fontawesome.css" rel="stylesheet" />
<link href="/static/icons/css/regular.css" rel="stylesheet" /> <link href="/static/icons/css/solid.css" rel="stylesheet" />
<link href="/static/icons/css/brands.css" rel="stylesheet" /> <link href="/static/icons/css/brands.css" rel="stylesheet" />
</head> </head>
<body> <body>
{{> partials/nav }} {{> partials/nav }}
<div class="mx-5 columns"> <div class="mx-5 columns" style="height: 100%">
<div class="column pl-0 sidebar-column"> <div class="column pl-0 sidebar-column">
{{> partials/sidebar }} {{> partials/sidebar }}
</div> </div>

View file

@ -1,6 +1,14 @@
{{#> layouts/main }} {{#> layouts/main }}
<div class=""> <div class="">
<h4 class="title is-4 is-inline">{{ library.name }} > Files</h4> <nav class="breadcrumb is-inline-block is-size-5 mb-0" aria-label="breadcrumbs">
<ul>
<li><a class="has-text-black" href="#">{{ library.name }}</a></li>
<li class="is-active"><a class="has-text-black" href="#" aria-current="page">test</a></li>
<div class="button is-small">
+
</div>
</ul>
</nav>
<div class="is-pulled-right is-inline-block"> <div class="is-pulled-right is-inline-block">
<div class="buttons"> <div class="buttons">
<div class="button is-small"> <div class="button is-small">
@ -9,12 +17,21 @@
<div class="button is-small"> <div class="button is-small">
Sort Sort
</div> </div>
<div class="button is-small">
Info
</div>
<div class="button is-small">
...
</div>
</div> </div>
</div> </div>
<hr class="my-2"> <hr class="my-2">
<table class="table is-fullwidth"> <table class="table is-fullwidth">
<thead> <thead>
<tr> <tr class="file-list">
<td style="width:0"><input type="checkbox" /></td>
<td style="width:0"></td>
<td style="width:0"></td>
<td>Name </td> <td>Name </td>
<td>Size </td> <td>Size </td>
<td>Last Updated </td> <td>Last Updated </td>
@ -23,9 +40,30 @@
</thead> </thead>
<tbody> <tbody>
{{#each files }} {{#each files }}
<tr> <tr class="file-list">
<td class="px-4 py-4"> <td><input type="checkbox" /></td>
<a href="{{ file_name }}">{{ file_name }}</a> <td>
<a class="has-text-black">
<span class="icon is-large">
<i class="fas fa-star fa-xl"></i>
</span>
</a>
</td>
<td class="">
<span class="icon is-large">
{{#if (eq type "folder") }}
<i class="fas fa-folder fa-xl"></i>
{{/if}}
{{#if (eq type "file") }}
<i class="fas fa-file fa-xl"></i>
{{/if}}
</span>
</td>
<td class="pl-4">
<a href="{{ path }}">{{ path }}</a>
{{#if (eq type "folder") }}
/
{{/if}}
</td> </td>
<td></td> <td></td>
<td></td> <td></td>

View file

@ -0,0 +1,8 @@
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li><a href="#">Bulma</a></li>
<li><a href="#">Documentation</a></li>
<li><a href="#">Components</a></li>
<li class="is-active"><a href="#" aria-current="page">Breadcrumb</a></li>
</ul>
</nav>

View file

@ -14,6 +14,16 @@
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<div class="navbar-item" style="width:300px">
<div class="field" style="width:100%" >
<p class="control has-icons-left">
<input class="input is-small" type="search" placeholder="Search files (CTRL + K)"></input>
<span class="icon is-small is-left">
<i class="fa-solid fa-magnifying-glass"></i>
</span>
</p>
</div>
</div>
<div class="navbar-item"> <div class="navbar-item">
Bell Bell
</div> </div>