Use naturalWidth and naturalHeight instead
This commit is contained in:
parent
f5e9b3abd2
commit
6a17a84d3b
10 changed files with 67 additions and 81 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -648,12 +648,6 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "imagesize"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b3b62f4e783e38afa07b51eaaa789be2fba03dbe29a05a1a906eb64feb987d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -920,7 +914,6 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"gloo-console",
|
"gloo-console",
|
||||||
"http",
|
"http",
|
||||||
"imagesize",
|
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"omegaupload-common",
|
"omegaupload-common",
|
||||||
"reqwasm",
|
"reqwasm",
|
||||||
|
|
|
@ -113,8 +113,10 @@ fn handle_download(url: ParsedUrl) -> Result<()> {
|
||||||
.get(EXPIRES)
|
.get(EXPIRES)
|
||||||
.and_then(|v| Expiration::try_from(v).ok())
|
.and_then(|v| Expiration::try_from(v).ok())
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToString::to_string)
|
.map_or_else(
|
||||||
.unwrap_or_else(|| "This paste will not expire.".to_string());
|
|| "This paste will not expire.".to_string(),
|
||||||
|
ToString::to_string,
|
||||||
|
);
|
||||||
|
|
||||||
let mut data = res.bytes()?.as_ref().to_vec();
|
let mut data = res.bytes()?.as_ref().to_vec();
|
||||||
|
|
||||||
|
|
|
@ -303,7 +303,7 @@ impl TryFrom<web_sys::Headers> for Expiration {
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(|v| Expiration::try_from(v).ok())
|
.and_then(|v| Self::try_from(v).ok())
|
||||||
.ok_or(ParseHeaderValueError)
|
.ok_or(ParseHeaderValueError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ async fn main() -> Result<()> {
|
||||||
],
|
],
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
set_up_expirations(Arc::clone(&db));
|
set_up_expirations(&db);
|
||||||
|
|
||||||
axum::Server::bind(&"0.0.0.0:8081".parse()?)
|
axum::Server::bind(&"0.0.0.0:8081".parse()?)
|
||||||
.serve(
|
.serve(
|
||||||
|
@ -68,7 +68,7 @@ async fn main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_up_expirations(db: Arc<DB>) {
|
fn set_up_expirations(db: &Arc<DB>) {
|
||||||
let mut corrupted = 0;
|
let mut corrupted = 0;
|
||||||
let mut expired = 0;
|
let mut expired = 0;
|
||||||
let mut pending = 0;
|
let mut pending = 0;
|
||||||
|
@ -77,7 +77,7 @@ fn set_up_expirations(db: Arc<DB>) {
|
||||||
|
|
||||||
let meta_cf = db.cf_handle(META_CF_NAME).unwrap();
|
let meta_cf = db.cf_handle(META_CF_NAME).unwrap();
|
||||||
|
|
||||||
let db_ref = Arc::clone(&db);
|
let db_ref = Arc::clone(db);
|
||||||
|
|
||||||
let delete_entry = move |key: &[u8]| {
|
let delete_entry = move |key: &[u8]| {
|
||||||
let blob_cf = db_ref.cf_handle(BLOB_CF_NAME).unwrap();
|
let blob_cf = db_ref.cf_handle(BLOB_CF_NAME).unwrap();
|
||||||
|
@ -91,7 +91,7 @@ fn set_up_expirations(db: Arc<DB>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (key, value) in db.iterator_cf(meta_cf, IteratorMode::Start) {
|
for (key, value) in db.iterator_cf(meta_cf, IteratorMode::Start) {
|
||||||
let expires = if let Ok(value) = bincode::deserialize::<Expiration>(&value) {
|
let expiration = if let Ok(value) = bincode::deserialize::<Expiration>(&value) {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
corrupted += 1;
|
corrupted += 1;
|
||||||
|
@ -99,7 +99,7 @@ fn set_up_expirations(db: Arc<DB>) {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let expiration_time = match expires {
|
let expiration_time = match expiration {
|
||||||
Expiration::BurnAfterReading => {
|
Expiration::BurnAfterReading => {
|
||||||
panic!("Got burn after reading expiration time? Invariant violated");
|
panic!("Got burn after reading expiration time? Invariant violated");
|
||||||
}
|
}
|
||||||
|
@ -107,16 +107,16 @@ fn set_up_expirations(db: Arc<DB>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let sleep_duration = (expiration_time - Utc::now()).to_std().unwrap_or_default();
|
let sleep_duration = (expiration_time - Utc::now()).to_std().unwrap_or_default();
|
||||||
if sleep_duration != Duration::default() {
|
if sleep_duration == Duration::default() {
|
||||||
|
expired += 1;
|
||||||
|
delete_entry(&key);
|
||||||
|
} else {
|
||||||
pending += 1;
|
pending += 1;
|
||||||
let delete_entry_ref = delete_entry.clone();
|
let delete_entry_ref = delete_entry.clone();
|
||||||
task::spawn_blocking(move || async move {
|
task::spawn_blocking(move || async move {
|
||||||
tokio::time::sleep(sleep_duration).await;
|
tokio::time::sleep(sleep_duration).await;
|
||||||
delete_entry_ref(&key);
|
delete_entry_ref(&key);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
expired += 1;
|
|
||||||
delete_entry(&key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ fn set_up_expirations(db: Arc<DB>) {
|
||||||
} else {
|
} else {
|
||||||
warn!("Found {} corrupted pastes.", corrupted);
|
warn!("Found {} corrupted pastes.", corrupted);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Found {} expired pastes.", expired);
|
info!("Found {} expired pastes.", expired);
|
||||||
info!("Found {} active pastes.", pending);
|
info!("Found {} active pastes.", pending);
|
||||||
info!("Cleanup timers have been initialized.");
|
info!("Cleanup timers have been initialized.");
|
||||||
|
|
|
@ -17,7 +17,6 @@ console_error_panic_hook = "0.1"
|
||||||
gloo-console = "0.1"
|
gloo-console = "0.1"
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
imagesize = "0.9"
|
|
||||||
reqwasm = "0.2"
|
reqwasm = "0.2"
|
||||||
tree_magic_mini = { version = "3", features = ["with-gpl-data"] }
|
tree_magic_mini = { version = "3", features = ["with-gpl-data"] }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
|
|
@ -11,7 +11,7 @@ use web_sys::Blob;
|
||||||
pub enum DecryptedData {
|
pub enum DecryptedData {
|
||||||
String(Arc<String>),
|
String(Arc<String>),
|
||||||
Blob(Arc<Blob>),
|
Blob(Arc<Blob>),
|
||||||
Image(Arc<Blob>, (usize, usize), usize),
|
Image(Arc<Blob>, usize),
|
||||||
Audio(Arc<Blob>),
|
Audio(Arc<Blob>),
|
||||||
Video(Arc<Blob>),
|
Video(Arc<Blob>),
|
||||||
}
|
}
|
||||||
|
@ -69,30 +69,16 @@ pub fn decrypt(
|
||||||
Arc::new(Blob::new_with_u8_array_sequence(blob_chunks.dyn_ref().unwrap()).unwrap());
|
Arc::new(Blob::new_with_u8_array_sequence(blob_chunks.dyn_ref().unwrap()).unwrap());
|
||||||
log!(format!("Blob conversion completed in {}ms", now() - start));
|
log!(format!("Blob conversion completed in {}ms", now() - start));
|
||||||
|
|
||||||
log!("Image introspection started");
|
let mime_type = tree_magic_mini::from_u8(container);
|
||||||
let start = now();
|
|
||||||
let dimensions = imagesize::blob_size(&container).ok();
|
|
||||||
log!(format!(
|
|
||||||
"Image introspection completed in {}ms",
|
|
||||||
now() - start
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Some(dimensions) = dimensions {
|
if mime_type.starts_with("image/") || mime_type == "application/x-riff" {
|
||||||
Ok(DecryptedData::Image(
|
Ok(DecryptedData::Image(blob, container.len()))
|
||||||
blob,
|
} else if mime_type.starts_with("audio/") {
|
||||||
(dimensions.width, dimensions.height),
|
Ok(DecryptedData::Audio(blob))
|
||||||
container.len(),
|
} else if mime_type.starts_with("video/") || mime_type == "application/x-matroska" {
|
||||||
))
|
Ok(DecryptedData::Video(blob))
|
||||||
} else {
|
} else {
|
||||||
let mime_type = tree_magic_mini::from_u8(container);
|
Ok(DecryptedData::Blob(blob))
|
||||||
log!(mime_type);
|
|
||||||
if mime_type.starts_with("audio/") {
|
|
||||||
Ok(DecryptedData::Audio(blob))
|
|
||||||
} else if mime_type.starts_with("video/") || mime_type.ends_with("x-matroska") {
|
|
||||||
Ok(DecryptedData::Video(blob))
|
|
||||||
} else {
|
|
||||||
Ok(DecryptedData::Blob(blob))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl<State: IdbObjectState> IdbObject<State> {
|
||||||
|
|
||||||
impl From<IdbObject<Ready>> for Object {
|
impl From<IdbObject<Ready>> for Object {
|
||||||
fn from(db_object: IdbObject<Ready>) -> Self {
|
fn from(db_object: IdbObject<Ready>) -> Self {
|
||||||
match Object::from_entries(db_object.as_ref()) {
|
match Self::from_entries(db_object.as_ref()) {
|
||||||
Ok(o) => o,
|
Ok(o) => o,
|
||||||
// SAFETY: IdbObject maintains the invariant that it can eventually
|
// SAFETY: IdbObject maintains the invariant that it can eventually
|
||||||
// be constructed into a JS object.
|
// be constructed into a JS object.
|
||||||
|
@ -52,6 +52,12 @@ impl IdbObject<NeedsType> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for IdbObject<NeedsType> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IdbObject<NeedsExpiration> {
|
impl IdbObject<NeedsExpiration> {
|
||||||
pub fn expiration_text(self, expires: &str) -> IdbObject<NeedsData> {
|
pub fn expiration_text(self, expires: &str) -> IdbObject<NeedsData> {
|
||||||
self.add_tuple("expiration", &JsString::from(expires))
|
self.add_tuple("expiration", &JsString::from(expires))
|
||||||
|
|
|
@ -74,21 +74,19 @@ fn main() {
|
||||||
.split_once('#')
|
.split_once('#')
|
||||||
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
|
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let key = match partial_parsed_url.decryption_key {
|
let key = if let Some(key) = partial_parsed_url.decryption_key {
|
||||||
Some(key) => key,
|
key
|
||||||
None => {
|
} else {
|
||||||
error!("Key is missing in url; bailing.");
|
error!("Key is missing in url; bailing.");
|
||||||
render_message("Invalid paste link: Missing decryption key.".into());
|
render_message("Invalid paste link: Missing decryption key.".into());
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let nonce = match partial_parsed_url.nonce {
|
let nonce = if let Some(nonce) = partial_parsed_url.nonce {
|
||||||
Some(nonce) => nonce,
|
nonce
|
||||||
None => {
|
} else {
|
||||||
error!("Nonce is missing in url; bailing.");
|
error!("Nonce is missing in url; bailing.");
|
||||||
render_message("Invalid paste link: Missing nonce.".into());
|
render_message("Invalid paste link: Missing nonce.".into());
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
(key, nonce, partial_parsed_url.needs_password)
|
(key, nonce, partial_parsed_url.needs_password)
|
||||||
};
|
};
|
||||||
|
@ -175,20 +173,15 @@ async fn fetch_resources(
|
||||||
DecryptedData::Blob(blob) => {
|
DecryptedData::Blob(blob) => {
|
||||||
IdbObject::new().blob().expiration_text(&expires).data(blob)
|
IdbObject::new().blob().expiration_text(&expires).data(blob)
|
||||||
}
|
}
|
||||||
DecryptedData::Image(blob, (width, height), size) => IdbObject::new()
|
DecryptedData::Image(blob, size) => IdbObject::new()
|
||||||
.image()
|
.image()
|
||||||
.expiration_text(&expires)
|
.expiration_text(&expires)
|
||||||
.data(blob)
|
.data(blob)
|
||||||
.extra("width", *width)
|
|
||||||
.extra("height", *height)
|
|
||||||
.extra(
|
.extra(
|
||||||
"button",
|
"file_size",
|
||||||
&format!(
|
Byte::from_bytes(*size as u128)
|
||||||
"Download {} \u{2014} {} by {}",
|
.get_appropriate_unit(true)
|
||||||
Byte::from_bytes(*size as u128).get_appropriate_unit(true),
|
.to_string(),
|
||||||
width,
|
|
||||||
height,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
DecryptedData::Audio(blob) => IdbObject::new()
|
DecryptedData::Audio(blob) => IdbObject::new()
|
||||||
.audio()
|
.audio()
|
||||||
|
@ -233,7 +226,7 @@ async fn fetch_resources(
|
||||||
));
|
));
|
||||||
let on_upgrade = Closure::wrap(Box::new(move |event: Event| {
|
let on_upgrade = Closure::wrap(Box::new(move |event: Event| {
|
||||||
let db = as_idb_db(&event);
|
let db = as_idb_db(&event);
|
||||||
let _ = db.create_object_store("decrypted data").unwrap();
|
let _obj_store = db.create_object_store("decrypted data").unwrap();
|
||||||
}) as Box<dyn FnMut(Event)>);
|
}) as Box<dyn FnMut(Event)>);
|
||||||
db_open_req.set_onupgradeneeded(Some(on_upgrade.into_js_value().unchecked_ref()));
|
db_open_req.set_onupgradeneeded(Some(on_upgrade.into_js_value().unchecked_ref()));
|
||||||
}
|
}
|
||||||
|
@ -244,7 +237,7 @@ async fn fetch_resources(
|
||||||
render_message("Invalid paste URL.".into());
|
render_message("Invalid paste URL.".into());
|
||||||
}
|
}
|
||||||
Ok(err) => {
|
Ok(err) => {
|
||||||
render_message(format!("{}", err.status_text()).into());
|
render_message(err.status_text().into());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
render_message(format!("{}", err).into());
|
render_message(format!("{}", err).into());
|
||||||
|
|
|
@ -117,8 +117,12 @@ function createBlobPasteUi(data) {
|
||||||
bodyEle.appendChild(mainEle);
|
bodyEle.appendChild(mainEle);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createImagePasteUi({ expiration, data, button }) {
|
function createImagePasteUi({ expiration, data, file_size }) {
|
||||||
createMultiMediaPasteUi("img", expiration, data, button);
|
createMultiMediaPasteUi("img", expiration, data, (downloadEle, imgEle) => {
|
||||||
|
imgEle.onload = () => {
|
||||||
|
downloadEle.textContent = "Download " + file_size + " \u2014 " + imgEle.naturalWidth + " by " + imgEle.naturalHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAudioPasteUi({ expiration, data }) {
|
function createAudioPasteUi({ expiration, data }) {
|
||||||
|
@ -129,7 +133,7 @@ function createVideoPasteUi({ expiration, data }) {
|
||||||
createMultiMediaPasteUi("video", expiration, data, "Download");
|
createMultiMediaPasteUi("video", expiration, data, "Download");
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMultiMediaPasteUi(tag, expiration, data, downloadMessage) {
|
function createMultiMediaPasteUi(tag, expiration, data, on_create?) {
|
||||||
let bodyEle = document.getElementsByTagName("body")[0];
|
let bodyEle = document.getElementsByTagName("body")[0];
|
||||||
bodyEle.textContent = '';
|
bodyEle.textContent = '';
|
||||||
|
|
||||||
|
@ -144,19 +148,24 @@ function createMultiMediaPasteUi(tag, expiration, data, downloadMessage) {
|
||||||
expirationEle.textContent = expiration;
|
expirationEle.textContent = expiration;
|
||||||
mainEle.appendChild(expirationEle);
|
mainEle.appendChild(expirationEle);
|
||||||
|
|
||||||
let videoEle = document.createElement(tag);
|
let mediaEle = document.createElement(tag);
|
||||||
videoEle.src = downloadLink;
|
mediaEle.src = downloadLink;
|
||||||
videoEle.controls = true;
|
mediaEle.controls = true;
|
||||||
mainEle.appendChild(videoEle);
|
mainEle.appendChild(mediaEle);
|
||||||
|
|
||||||
let downloadEle = document.createElement("a");
|
let downloadEle = document.createElement("a");
|
||||||
downloadEle.href = downloadLink;
|
downloadEle.href = downloadLink;
|
||||||
downloadEle.download = window.location.pathname;
|
downloadEle.download = window.location.pathname;
|
||||||
downloadEle.classList.add("hljs-meta");
|
downloadEle.classList.add("hljs-meta");
|
||||||
downloadEle.textContent = downloadMessage;
|
|
||||||
mainEle.appendChild(downloadEle);
|
mainEle.appendChild(downloadEle);
|
||||||
|
|
||||||
bodyEle.appendChild(mainEle);
|
bodyEle.appendChild(mainEle);
|
||||||
|
|
||||||
|
if (on_create instanceof Function) {
|
||||||
|
on_create(downloadEle, mediaEle);
|
||||||
|
} else {
|
||||||
|
downloadEle.textContent = on_create;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMessage(message) {
|
function renderMessage(message) {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
use js_sys::Function;
|
|
||||||
use wasm_bindgen::closure::WasmClosure;
|
|
||||||
use wasm_bindgen::prelude::Closure;
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::{Event, IdbDatabase, IdbOpenDbRequest};
|
use web_sys::{Event, IdbDatabase, IdbOpenDbRequest};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue