From b49ab1044fa804ed5be942b252ac66c8a053d496 Mon Sep 17 00:00:00 2001 From: Stridsvagn69420 Date: Sun, 4 Dec 2022 03:57:00 +0100 Subject: [PATCH] Structural tuning and server binary --- Cargo.lock | 191 +++++++++++++++++++------------------------ Cargo.toml | 9 +- src/account.rs | 26 +++++- src/album.rs | 23 +++--- src/bin/cyrkensia.rs | 96 +++++++++++++++++++--- src/config.rs | 42 ++++++---- src/hostinfo.rs | 20 +---- src/lib.rs | 13 +-- src/metadata.rs | 26 ++---- src/server/routes.rs | 55 ++++++------- 10 files changed, 278 insertions(+), 223 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bd5b32..47187f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,6 @@ dependencies = [ "actix-codec", "actix-rt", "actix-service", - "actix-tls", "actix-utils", "ahash", "base64", @@ -134,24 +133,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "actix-tls" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "log", - "pin-project-lite", - "tokio-rustls", - "tokio-util", - "webpki-roots", -] - [[package]] name = "actix-utils" version = "3.0.1" @@ -175,7 +156,6 @@ dependencies = [ "actix-rt", "actix-server", "actix-service", - "actix-tls", "actix-utils", "actix-web-codegen", "ahash", @@ -537,6 +517,8 @@ dependencies = [ "argon2", "blake3", "chrono", + "dirs", + "kagero", "rand", "serde", "serde_json", @@ -567,6 +549,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "encoding_rs" version = "0.8.31" @@ -777,6 +779,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kagero" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff686c11ce916d3ffd62f7ed470638d3d0021bb503a2d818a220f3e795687e" +dependencies = [ + "url", + "winres", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1024,6 +1036,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.7.0" @@ -1041,21 +1064,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rustc_version" version = "0.4.0" @@ -1065,18 +1073,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustls" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "ryu" version = "1.0.11" @@ -1095,16 +1091,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "semver" version = "1.0.14" @@ -1199,12 +1185,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "subtle" version = "2.4.1" @@ -1231,6 +1211,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" @@ -1302,17 +1302,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - [[package]] name = "tokio-util" version = "0.7.4" @@ -1327,6 +1316,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tracing" version = "0.1.37" @@ -1381,12 +1379,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "url" version = "2.3.1" @@ -1396,6 +1388,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -1480,35 +1473,6 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" -dependencies = [ - "webpki", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1597,6 +1561,15 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/Cargo.toml b/Cargo.toml index 4882d11..2008932 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,15 +13,18 @@ documentation = "https://docs.rs/cyrkensia" authors = ["Stridsvagn69420"] categories = [] keywords = [] +default-run = "cyrkensia" [dependencies] # Main crate serde = { version = "1", features = ["derive"] } serde_json = "1" uuid = { version = "1.2", features = ["v4", "serde"] } +dirs = "4" +kagero = { version = "0.4", default-features = false, features = ["printer"] } # Server only -actix-web = { version = "4", optional = true, features = ["rustls"] } +actix-web = { version = "4", optional = true } actix-cors = { version = "0.6", optional = true } blake3 = { version = "1.3", optional = true } @@ -30,9 +33,9 @@ argon2 = { version = "0.4", optional = true, features = ["std", "password-hash"] rand = { version = "0.8", optional = true, features = ["std"] } [features] -default = ["server"] +default = ["server", "accounts"] accounts = ["argon2", "rand"] -server = ["actix-web", "actix-cors", "blake3", "accounts"] +server = ["actix-web", "actix-cors", "blake3"] [build-dependencies] chrono = "0.4" diff --git a/src/account.rs b/src/account.rs index c8fc45e..76f6c23 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,11 +1,13 @@ use std::fmt::Display; use std::path::Path; -use std::{io, fs}; +use std::{io, fs, env}; +use dirs::home_dir; use serde::{Deserialize, Serialize}; use serde_json::from_str; use argon2::password_hash::rand_core::OsRng; use argon2::password_hash::{SaltString, Result}; use argon2::{Argon2, PasswordHasher, PasswordVerifier, PasswordHash}; +use super::meta; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Account { @@ -55,6 +57,26 @@ impl Account { let data = fs::read_to_string(file)?; Ok(from_str(data.as_str())?) } + + /// Cascade Loading + /// + /// Attempts to load a user file by: + /// 1. First command-line argument + /// 2. `CYRKENSIA_USERS` environment variable + /// 3. `~/.config/cyrkensia/users.json` file + pub fn load_cascade(cmdarg: Option<&String>, pathcfg: Option<&String>) -> io::Result> { + // Select extra path + let envvar = env::var(meta::USERS_ENVVAR); + + // Read users from extra location + if let Some(path) = cmdarg.or_else(|| envvar.as_ref().ok()).or(pathcfg) { + return Account::load(path); + } + + // Read with default path + let localpath = home_dir().unwrap_or_default().join(meta::USERS_PATH); + Account::load(localpath) + } } impl Display for Account { @@ -89,4 +111,4 @@ macro_rules! get_argon2 { Argon2::default() }; } -pub use get_argon2; +pub use get_argon2; \ No newline at end of file diff --git a/src/album.rs b/src/album.rs index 1883393..79bba8e 100644 --- a/src/album.rs +++ b/src/album.rs @@ -3,7 +3,6 @@ use uuid::Uuid; use std::fs; use std::io; use std::cmp::PartialEq; -use std::collections::HashMap; use std::convert::From; use std::path::PathBuf; use super::Metadata; @@ -28,10 +27,15 @@ pub struct Album { /// The relative path of the album. pub path: String, + /// Artists + /// + /// The UUID of the artists responsible for this album. + pub artists: Vec, + /// Files /// /// All files present in the album as a relative path. - pub files: HashMap>, + pub files: Vec, /// Size /// @@ -43,16 +47,14 @@ impl Album { /// New Album /// /// Creates a new album. If `artists` or `files` is [None], an empty array will be created for them. - pub fn new(name: String, cover: String, path: String, files: Option>>, size: u128) -> Album { + pub fn new(name: String, cover: String, path: String, artists: Vec, files: Vec, size: u128) -> Album { Album { name, cover, path, - files: match files { - Some(x) => x, - None => HashMap::new() - }, - size + files, + size, + artists } } @@ -91,8 +93,9 @@ impl From for Album { name: x.name, cover: x.cover, path: "".to_string(), - files: x.artists, - size: 0 + files: Vec::new(), + size: 0, + artists: x.artists } } } diff --git a/src/bin/cyrkensia.rs b/src/bin/cyrkensia.rs index 29058aa..e5d2b7c 100644 --- a/src/bin/cyrkensia.rs +++ b/src/bin/cyrkensia.rs @@ -1,15 +1,89 @@ -//use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; +use std::{env, io}; +use std::process::exit; +use cyrkensia::Config; +use cyrkensia::meta::WIKI_HELP_URL; +use cyrkensia::server::CyrkensiaState; +use cyrkensia::server::routes::{index, hostinfo}; +use cyrkensia::server::middleware::{cors_everywhere, source_headers, license_headers}; +use actix_web::{web, App, HttpServer}; +use kagero::printer::{Printer, Colors}; -/*#[actix_web::main] -async fn main() -> std::io::Result<()> { - HttpServer::new(|| { - App::new() +/// Init +/// +/// Server preparations. +fn init() -> io::Result { + // Safe Args Collect to String + let args: Vec = env::args_os() + .filter_map(|x| { + let Some(arg) = x.to_str() else { + return None; + }; + Some(arg.to_string()) }) - .bind(("127.0.0.1", 8080))? - .run() - .await -}*/ + .collect(); + + // Config + Config::load_cascade(args.get(1)) +} + +/// Server +/// +/// Server startup. +async fn server(cfg: Config) -> io::Result<()> { + // ---- Server Init ---- + let bindaddr = cfg.bindaddr.clone(); + let unbound_server = HttpServer::new(move || { + // Initialize state + let Ok(state) = CyrkensiaState::new(cfg.clone()) else { + Printer::default().errorln("Cyrkensia failed trying to initialize!", Colors::YellowBright); + exit(1); + }; + + // ---- App ---- + App::new() + // State + .app_data(web::Data::new(state)) + // Middleware + .wrap(cors_everywhere()) + .wrap(source_headers()) + .wrap(license_headers()) + //Routes + .route("/", web::get().to(hostinfo)) + .route("/{album}", web::get().to(index)) + }); + + // ---- Server Bind ---- + #[cfg(target_family = "unix")] + let server = if bindaddr.starts_with('/') { + unbound_server.bind_uds(bindaddr)? + } else { + unbound_server.bind(bindaddr)? + }; + + #[cfg(not(target_family = "unix"))] + let server = unbound_server.bind(bindaddr)?; + + // ---- Ignite ---- + server.run().await +} + +#[actix_web::main] +async fn main() { + // Init + let mut console = Printer::default(); + let Ok(config) = init() else { + console.errorln("Failed to read the config file for Cyrkensia!", Colors::RedBright); + console.errorln(&("See ".to_owned() + WIKI_HELP_URL + " for more."), Colors::Yellow); + exit(1); + }; + + // Start + if let Err(serv) = server(config).await { + console.errorln("An error occured while running the server:", Colors::Red); + eprintln!("{serv}"); + } -fn main() { - println!("This is still WIP!!!"); + // Exit + console.println("Successfully stopped the Cyrkensia server!", Colors::Cyan); + exit(0) } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 843dd57..62474dc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,12 +1,14 @@ use serde::{Serialize, Deserialize}; use serde_json::from_str; +use dirs::home_dir; +use uuid::Uuid; use std::cmp::PartialEq; use std::convert::From; +use std::path::Path; use std::io; use std::fs; -use std::path::Path; -use uuid::Uuid; -use super::{Owner, Hostinfo}; +use std::env; +use super::{Owner, Hostinfo, meta}; /// Configuration /// @@ -44,16 +46,6 @@ pub struct Config { /// The IP address to bind to, e.g. `127.0.0.1`, `0.0.0.0:80` or a Unix socket (Unix only). pub bindaddr: String, - /// TLS Certificate (Optional) - /// - /// The Path to the TLS certificate. TLS will only be activated if both certificate and key are provided. - pub tlscert: Option, - - /// TLS Key (Optional) - /// - /// The path to the TLS key. TLS will only be activated if both certificate and key are provided. - pub tlskey: Option, - /// Owners /// /// List of repository [maintainers](Owner) @@ -81,6 +73,26 @@ impl Config { pub fn load_json(data: &str) -> io::Result { Ok(from_str(data)?) } + + /// Cascade Loading + /// + /// Attempts to load a config file by: + /// 1. First command-line argument + /// 2. `CYRKENSIA_CONFIG` environment variable + /// 3. `~/.config/cyrkensia/config.json` file + pub fn load_cascade(cmdarg: Option<&String>) -> io::Result { + // Select extra path + let envvar = env::var(meta::CONFIG_ENVVAR); + + // Read config from extra location + if let Some(path) = cmdarg.or_else(|| envvar.as_ref().ok()) { + return Config::load_file(path); + } + + // Read with default path + let localpath = home_dir().unwrap_or_default().join(meta::CONFIG_PATH); + Config::load_file(localpath) + } } impl From for Config { @@ -92,8 +104,6 @@ impl From for Config { icon: x.icon, htpasswd: None, bindaddr: "".to_string(), - tlscert: None, - tlskey: None, owners: x.owners, max_age: None } @@ -108,8 +118,6 @@ impl PartialEq for Config { self.icon == other.icon && self.htpasswd == other.htpasswd && self.bindaddr == other.bindaddr && - self.tlscert == other.tlscert && - self.tlskey == other.tlskey && self.owners == other.owners } } \ No newline at end of file diff --git a/src/hostinfo.rs b/src/hostinfo.rs index d8ede21..10a0888 100644 --- a/src/hostinfo.rs +++ b/src/hostinfo.rs @@ -142,31 +142,17 @@ impl Hostinfo { }; // Read data - let mut m = Metadata::load(path.join(".metadata.json"))?; + let m = Metadata::load(path.join(".metadata.json"))?; let c = Hostinfo::list_files(path)?; - // List files with default Artist - if let Some(defart) = m.default { - for file in c.0 { - if let Some(other_arts) = m.artists.get_mut(&file) { - // Add default Artist to listed file if it's not included yet - if other_arts.contains(&defart) { - other_arts.push(defart); - } - } else { - // Append all yet unlisted files - m.artists.insert(file, vec![defart]); - } - } - } - // Create Album Ok(Album { name: m.name, cover: m.cover, path: fname.to_string(), - files: m.artists, + files: c.0, size: c.1, + artists: m.artists, }) } diff --git a/src/lib.rs b/src/lib.rs index 92392b4..2accb82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,7 @@ pub mod meta { pub const NAME_RICH: &str = "Cyrkensia"; pub const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY"); + pub const WIKI_HELP_URL: &str = concat!(env!("CARGO_PKG_REPOSITORY"), "/wiki/Troubleshooting"); pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); pub const LICENSE: &str = "EUPL-1.2"; pub const LICENSE_RICH: &str = "European Union Public License v1.2"; @@ -75,11 +76,11 @@ pub mod meta { pub const RUSTC_VERSION: &str = env!("RUSTC_VERSION"); pub const COMPILE_DATE: &str = env!("COMPILE_DATE"); pub const TARGET: &str = env!("TARGET"); - pub const USERAGENT: &str = concat!( - env!("CARGO_PKG_NAME"), - "/", - env!("CARGO_PKG_VERSION") - ); + pub const USERAGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); + pub const CONFIG_ENVVAR: &str = "CYRKENSIA_CONFIG"; + pub const CONFIG_PATH: &str = concat!(".config/", env!("CARGO_PKG_NAME"), "/config.json"); + pub const USERS_ENVVAR: &str = "CYRKENSIA_USERS"; + pub const USERS_PATH: &str = concat!(".config/", env!("CARGO_PKG_NAME"), "/users.json"); } #[cfg(feature = "server")] @@ -92,4 +93,4 @@ pub mod server; /// Account database /// /// Submodule containing the [Account](accounts::Account) struct and related cryptographic and wrapper functions. -pub mod account; \ No newline at end of file +pub mod account; diff --git a/src/metadata.rs b/src/metadata.rs index f18a92d..c57429c 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,7 +1,6 @@ use serde::{Serialize, Deserialize}; use serde_json::from_str; use uuid::Uuid; -use std::collections::HashMap; use std::fmt::{Display, Result}; use std::cmp::PartialEq; use std::convert::From; @@ -25,30 +24,21 @@ pub struct Metadata { /// The asset key representing the album cover art. pub cover: String, - /// Default Artist + /// Artists /// - /// Represents the UUIDv4 of the main [Artist] of this album - pub default: Option, - - /// Additional Artists - /// - /// Map of which additional artists are associated with what music track - pub artists: HashMap> + /// Represents the UUIDv4 of the [Artist] of this album + pub artists: Vec } impl Metadata { /// New Metadata /// /// Creates new [Metadata]. Authors can be appended later on. - pub fn new(name: String, cover: String, default_artist: Option, artists: Option>>) -> Metadata { + pub fn new(name: String, cover: String, artists: Vec) -> Metadata { Metadata { name, cover, - default: default_artist, - artists: match artists { - Some(x) => x, - None => HashMap::new() - } + artists } } @@ -66,8 +56,7 @@ impl From for Metadata { Metadata { name: x.name, cover: x.cover, - artists: x.files, - default: None + artists: x.artists } } } @@ -75,8 +64,7 @@ impl From for Metadata { impl PartialEq for Metadata { fn eq(&self, other: &Self) -> bool { self.name == other.name && - self.cover == other.cover && - self.artists == other.artists + self.cover == other.cover } } diff --git a/src/server/routes.rs b/src/server/routes.rs index 0f01e16..e9d3918 100644 --- a/src/server/routes.rs +++ b/src/server/routes.rs @@ -1,6 +1,7 @@ use std::time::Instant; use actix_web::{web, Responder, HttpRequest, HttpResponse}; use actix_web::http::header::ContentType; +use serde::Deserialize; use super::{CyrkensiaState, responses, uri_noquery}; use crate::{Hostinfo, Artist, Metadata, Album}; @@ -60,6 +61,7 @@ pub async fn hostinfo(req: HttpRequest, data: web::Data) -> impl finalres } +#[derive(Deserialize)] /// Index Route Params /// /// Simple struct containing the param-name and param-type needed for the [index] route. @@ -74,38 +76,33 @@ pub async fn index(p: web::Path, data: web::Data) - // Extract album name let path = p.into_inner(); // Read files and album name depending on if cache is enabled or not - let meta: (String, usize, Vec) = match data.config.max_age { - Some(_) => { - // Cached files and metadata - let Ok(hostinfo) = data.hostinfo.lock() else { - return responses::server_500(None::); - }; - let Some(album) = hostinfo.albums.clone().into_iter().find(|x| x.path == path.album) else { - return responses::client_404(Some("Album not found")); - }; - - // Return tri-tuple - let files: Vec = album.files.keys().cloned().collect(); - (album.name, files.len(), files) + let meta: (String, usize, Vec) = if data.config.max_age.is_some() { + // Cached files and metadata + let Ok(hostinfo) = data.hostinfo.lock() else { + return responses::server_500(None::); + }; + let Some(album) = hostinfo.albums.clone().into_iter().find(|x| x.path == path.album) else { + return responses::client_404(Some("Album not found")); + }; - }, - None => { - // Attempt to find album in filesystem - let Ok(path) = Album::find(&data.config.root, &path.album) else { - return responses::client_404(Some("Album not found")); - }; + // Return tri-tuple + (album.name, album.files.len(), album.files) + } else { + // Attempt to find album in filesystem + let Ok(path) = Album::find(&data.config.root, &path.album) else { + return responses::client_404(Some("Album not found")); + }; - // Ad-hoc metadata and files - let Ok(album) = Metadata::load(path.join(".metadata.json")) else { - return responses::client_404(Some("Album not found")); - }; - let Ok(files) = Hostinfo::list_files(path) else { - return responses::client_404(Some("Album not accessable")); - }; + // Ad-hoc metadata and files + let Ok(album) = Metadata::load(path.join(".metadata.json")) else { + return responses::client_404(Some("Album not found")); + }; + let Ok(files) = Hostinfo::list_files(path) else { + return responses::client_404(Some("Album not accessable")); + }; - // Return tri-tuple - (album.name, files.0.len(), files.0) - } + // Return tri-tuple + (album.name, files.0.len(), files.0) }; // Codegen