feat: add image etag support

This commit is contained in:
2026-03-24 23:47:38 +01:00
parent 70f77f3501
commit 7a4876b0e8
4 changed files with 25 additions and 10 deletions

7
Cargo.lock generated
View File

@@ -459,8 +459,8 @@ dependencies = [
[[package]]
name = "const-macros"
version = "0.1.1"
source = "git+https://git.javalsai.tuxcord.net/rust/const-macros.git#6a37f26eaa6349c18ac7c74b1d3938d287013942"
version = "0.1.2"
source = "git+https://git.javalsai.tuxcord.net/rust/const-macros.git#6f91e1e1a6ce2e5cdb5866a1e1d7841ed5e3cd6f"
dependencies = [
"const-macros-proc",
]
@@ -468,7 +468,7 @@ dependencies = [
[[package]]
name = "const-macros-proc"
version = "0.1.0"
source = "git+https://git.javalsai.tuxcord.net/rust/const-macros.git#6a37f26eaa6349c18ac7c74b1d3938d287013942"
source = "git+https://git.javalsai.tuxcord.net/rust/const-macros.git#6f91e1e1a6ce2e5cdb5866a1e1d7841ed5e3cd6f"
dependencies = [
"magic",
"sha2",
@@ -1200,6 +1200,7 @@ dependencies = [
"moka",
"pamsock",
"serde",
"sha2",
"thiserror",
"tokio",
"toml",

View File

@@ -25,13 +25,14 @@ actix-web = "4.13"
anstyle = "1.0"
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"] }
const-macros = { git = "https://git.javalsai.tuxcord.net/rust/const-macros.git", version = "0.1.1" }
const-macros = { git = "https://git.javalsai.tuxcord.net/rust/const-macros.git", version = "0.1.2" }
const-str = { version = "1.1", features = ["proc"] }
futures-util = "0.3"
libc = "0.2"
magic = "0.16"
moka = { version = "0.12", features = ["async-lock", "future"] }
serde = { version = "1.0", features = ["derive"] }
sha2 = "0.10"
thiserror = "2.0"
tokio = { version = "1.49", features = ["full"] }
toml = "1.0"

View File

@@ -1,7 +1,8 @@
use std::{ffi::OsStr, io, os::unix::fs::MetadataExt, path::Path, sync::Arc};
use ::users::os::unix::UserExt;
use ::users::os::unix::UserExt as _;
use moka::future::{Cache, CacheBuilder};
use sha2::Digest as _;
use tokio::{fs::File, io::AsyncReadExt};
use crate::{
@@ -14,12 +15,13 @@ pub mod users;
const MIME_IMAGE_PREFIX: &str = "image/";
pub struct MimeWithBytes {
pub struct ImageInfo {
pub mime: Box<str>,
pub bytes: Box<[u8]>,
pub shasum: Box<str>,
}
pub type Image = Arc<MimeWithBytes>;
pub type Image = Arc<ImageInfo>;
// TODO: most of the cache methods here block the executor, if we wanna commit to async we'd have
// to consider that
@@ -115,15 +117,23 @@ impl<'a> AppCache<'a> {
for subpath in consts::USER_PFP_PATHS {
let path = home.join(subpath);
// I'm relying too much on this condition
if let Ok(img_buf) = read_limited_path::<{ consts::MAX_PFP_SIZE }>(&path).await
&& let Ok(Ok(mime)) = self.magic_mime_cookie.buffer(&img_buf) // TODO: first layer
// error is actually
// relevant
&& mime.starts_with(MIME_IMAGE_PREFIX)
{
return Some(Arc::new(MimeWithBytes {
let shasum = sha2::Sha256::digest(&img_buf);
let shasum = format!(
"\"{}\"",
crate::utils::shasum::sha256sum_to_hex_string(&shasum)
);
return Some(Arc::new(ImageInfo {
mime: mime.into_boxed_str(),
bytes: img_buf.into_boxed_slice(),
shasum: shasum.into_boxed_str(),
}));
}
}

View File

@@ -5,8 +5,6 @@
//!
//! Must be scoped at [`ws::IMAGES`]
// TODO: etags
use actix_web::{
HttpResponse, get,
http::header,
@@ -32,6 +30,10 @@ async fn get_default_image() -> HttpResponse {
header::CACHE_CONTROL,
consts::DEFAULT_USER_PFP_CACHES_HEADER,
))
.insert_header((
header::ETAG,
const_str::concat!('"', consts::DEFAULT_USER_PFP.shasum_str, '"'),
))
.content_type(consts::DEFAULT_USER_PFP.mime)
.body(web::Bytes::from_static(consts::DEFAULT_USER_PFP.bytes))
}
@@ -53,6 +55,7 @@ async fn get_image(
web::Either::Right(
HttpResponse::Ok()
.insert_header((header::CACHE_CONTROL, consts::USER_CACHES_HEADER))
.insert_header((header::ETAG, img.shasum.as_ref()))
.content_type(img.mime.as_ref())
.body(web::Bytes::copy_from_slice(img.bytes.as_ref())),
)