feat: add image etag support
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -459,8 +459,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-macros"
|
name = "const-macros"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
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 = [
|
dependencies = [
|
||||||
"const-macros-proc",
|
"const-macros-proc",
|
||||||
]
|
]
|
||||||
@@ -468,7 +468,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "const-macros-proc"
|
name = "const-macros-proc"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"magic",
|
"magic",
|
||||||
"sha2",
|
"sha2",
|
||||||
@@ -1200,6 +1200,7 @@ dependencies = [
|
|||||||
"moka",
|
"moka",
|
||||||
"pamsock",
|
"pamsock",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ actix-web = "4.13"
|
|||||||
anstyle = "1.0"
|
anstyle = "1.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
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"] }
|
const-str = { version = "1.1", features = ["proc"] }
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
magic = "0.16"
|
magic = "0.16"
|
||||||
moka = { version = "0.12", features = ["async-lock", "future"] }
|
moka = { version = "0.12", features = ["async-lock", "future"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
sha2 = "0.10"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
tokio = { version = "1.49", features = ["full"] }
|
tokio = { version = "1.49", features = ["full"] }
|
||||||
toml = "1.0"
|
toml = "1.0"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::{ffi::OsStr, io, os::unix::fs::MetadataExt, path::Path, sync::Arc};
|
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 moka::future::{Cache, CacheBuilder};
|
||||||
|
use sha2::Digest as _;
|
||||||
use tokio::{fs::File, io::AsyncReadExt};
|
use tokio::{fs::File, io::AsyncReadExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -14,12 +15,13 @@ pub mod users;
|
|||||||
|
|
||||||
const MIME_IMAGE_PREFIX: &str = "image/";
|
const MIME_IMAGE_PREFIX: &str = "image/";
|
||||||
|
|
||||||
pub struct MimeWithBytes {
|
pub struct ImageInfo {
|
||||||
pub mime: Box<str>,
|
pub mime: Box<str>,
|
||||||
pub bytes: Box<[u8]>,
|
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
|
// TODO: most of the cache methods here block the executor, if we wanna commit to async we'd have
|
||||||
// to consider that
|
// to consider that
|
||||||
@@ -115,15 +117,23 @@ impl<'a> AppCache<'a> {
|
|||||||
for subpath in consts::USER_PFP_PATHS {
|
for subpath in consts::USER_PFP_PATHS {
|
||||||
let path = home.join(subpath);
|
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
|
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
|
&& let Ok(Ok(mime)) = self.magic_mime_cookie.buffer(&img_buf) // TODO: first layer
|
||||||
// error is actually
|
// error is actually
|
||||||
// relevant
|
// relevant
|
||||||
&& mime.starts_with(MIME_IMAGE_PREFIX)
|
&& 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(),
|
mime: mime.into_boxed_str(),
|
||||||
bytes: img_buf.into_boxed_slice(),
|
bytes: img_buf.into_boxed_slice(),
|
||||||
|
shasum: shasum.into_boxed_str(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
//!
|
//!
|
||||||
//! Must be scoped at [`ws::IMAGES`]
|
//! Must be scoped at [`ws::IMAGES`]
|
||||||
|
|
||||||
// TODO: etags
|
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
HttpResponse, get,
|
HttpResponse, get,
|
||||||
http::header,
|
http::header,
|
||||||
@@ -32,6 +30,10 @@ async fn get_default_image() -> HttpResponse {
|
|||||||
header::CACHE_CONTROL,
|
header::CACHE_CONTROL,
|
||||||
consts::DEFAULT_USER_PFP_CACHES_HEADER,
|
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)
|
.content_type(consts::DEFAULT_USER_PFP.mime)
|
||||||
.body(web::Bytes::from_static(consts::DEFAULT_USER_PFP.bytes))
|
.body(web::Bytes::from_static(consts::DEFAULT_USER_PFP.bytes))
|
||||||
}
|
}
|
||||||
@@ -53,6 +55,7 @@ async fn get_image(
|
|||||||
web::Either::Right(
|
web::Either::Right(
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header((header::CACHE_CONTROL, consts::USER_CACHES_HEADER))
|
.insert_header((header::CACHE_CONTROL, consts::USER_CACHES_HEADER))
|
||||||
|
.insert_header((header::ETAG, img.shasum.as_ref()))
|
||||||
.content_type(img.mime.as_ref())
|
.content_type(img.mime.as_ref())
|
||||||
.body(web::Bytes::copy_from_slice(img.bytes.as_ref())),
|
.body(web::Bytes::copy_from_slice(img.bytes.as_ref())),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user