diff --git a/Cargo.lock b/Cargo.lock index 64d7c7ea..eeefe65b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2505,6 +2505,7 @@ dependencies = [ "security-framework", "serde", "serde_json", + "sha2", "strum", "strum_macros", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 20b1c77b..60cf1f1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ url = "2.5.2" urlencoding = "2.1.3" uuid = { version = "1.10.0", features = ["v4"] } security-framework = "2.11.0" +sha2 = "0.10.8" [profile.small] inherits = 'release' diff --git a/crates/notedeck/Cargo.toml b/crates/notedeck/Cargo.toml index 31bdfba2..b8138641 100644 --- a/crates/notedeck/Cargo.toml +++ b/crates/notedeck/Cargo.toml @@ -22,6 +22,7 @@ serde = { workspace = true } hex = { workspace = true } thiserror = { workspace = true } puffin = { workspace = true, optional = true } +sha2 = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/notedeck/src/imgcache.rs b/crates/notedeck/src/imgcache.rs index 4a78fc00..9cbe31a1 100644 --- a/crates/notedeck/src/imgcache.rs +++ b/crates/notedeck/src/imgcache.rs @@ -5,9 +5,13 @@ use poll_promise::Promise; use egui::ColorImage; use std::collections::HashMap; -use std::fs::File; +use std::fs::{create_dir_all, File}; +use hex::ToHex; +use sha2::Digest; use std::path; +use std::path::PathBuf; +use tracing::warn; pub type ImageCacheValue = Promise>; pub type ImageCacheMap = HashMap; @@ -46,6 +50,9 @@ impl ImageCache { pub fn write(cache_dir: &path::Path, url: &str, data: ColorImage) -> Result<()> { let file_path = cache_dir.join(Self::key(url)); + if let Some(p) = file_path.parent() { + create_dir_all(p)?; + } let file = File::options() .write(true) .create(true) @@ -64,7 +71,51 @@ impl ImageCache { } pub fn key(url: &str) -> String { - base32::encode(base32::Alphabet::Crockford, url.as_bytes()) + let k: String = sha2::Sha256::digest(url.as_bytes()).encode_hex(); + PathBuf::from(&k[0..2]) + .join(&k[2..4]) + .join(k) + .to_string_lossy() + .to_string() + } + + /// Migrate from base32 encoded url to sha256 url + sub-dir structure + pub fn migrate_v0(&self) -> Result<()> { + for file in std::fs::read_dir(&self.cache_dir)? { + let file = if let Ok(f) = file { + f + } else { + // not sure how this could fail, skip entry + continue; + }; + if !file.path().is_file() { + continue; + } + let old_filename = file.file_name().to_string_lossy().to_string(); + let old_url = if let Some(u) = + base32::decode(base32::Alphabet::Crockford, &old_filename) + .and_then(|s| String::from_utf8(s).ok()) + { + u + } else { + warn!("Invalid base32 filename: {}", &old_filename); + continue; + }; + let new_path = self.cache_dir.join(Self::key(&old_url)); + if let Some(p) = new_path.parent() { + create_dir_all(p)?; + } + + if let Err(e) = std::fs::rename(file.path(), &new_path) { + warn!( + "Failed to migrate file from {} to {}: {:?}", + file.path().display(), + new_path.display(), + e + ); + } + } + Ok(()) } pub fn map(&self) -> &ImageCacheMap { diff --git a/crates/notedeck_chrome/src/app.rs b/crates/notedeck_chrome/src/app.rs index 6cc6435b..c420d0d3 100644 --- a/crates/notedeck_chrome/src/app.rs +++ b/crates/notedeck_chrome/src/app.rs @@ -207,6 +207,11 @@ impl Notedeck { let tabs = Tabs::new(None); let app_rect_handler = AppSizeHandler::new(&path); + // migrate + if let Err(e) = img_cache.migrate_v0() { + error!("error migrating image cache: {e}"); + } + Self { ndb, img_cache,