omegaupload/web/src/main.rs

328 lines
13 KiB
Rust
Raw Normal View History

2021-10-21 18:35:54 -07:00
#![warn(clippy::nursery, clippy::pedantic)]
2021-10-24 02:25:42 -07:00
use std::fmt::Debug;
2021-10-17 14:15:29 -07:00
use std::str::FromStr;
2021-10-23 10:10:55 -07:00
use std::sync::Arc;
2021-10-17 14:15:29 -07:00
2021-10-23 10:10:55 -07:00
use byte_unit::Byte;
2021-10-24 11:40:19 -07:00
use decrypt::DecryptedData;
2021-10-22 19:15:23 -07:00
use gloo_console::log;
2021-10-19 23:53:22 -07:00
use http::header::EXPIRES;
2021-10-19 18:48:32 -07:00
use http::uri::PathAndQuery;
use http::{StatusCode, Uri};
2021-10-24 02:25:42 -07:00
use js_sys::{Array, JsString, Object, Uint8Array};
2021-10-19 23:53:22 -07:00
use omegaupload_common::{Expiration, PartialParsedUrl};
2021-10-23 10:10:55 -07:00
use reqwasm::http::Request;
2021-10-24 02:25:42 -07:00
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, JsValue};
2021-10-22 19:15:23 -07:00
use wasm_bindgen_futures::JsFuture;
2021-10-24 02:25:42 -07:00
use web_sys::{Blob, Event, IdbDatabase, IdbObjectStore, IdbOpenDbRequest, IdbTransactionMode};
2021-10-17 14:15:29 -07:00
use yew::utils::window;
2021-10-24 02:25:42 -07:00
use yew::{html, Component, ComponentLink, Html, ShouldRender};
2021-10-17 14:15:29 -07:00
use yew_router::router::Router;
use yew_router::Switch;
2021-10-19 18:48:32 -07:00
use yewtil::future::LinkFuture;
2021-10-17 14:15:29 -07:00
2021-10-24 02:25:42 -07:00
use crate::decrypt::decrypt;
2021-10-23 10:10:55 -07:00
mod decrypt;
2021-10-17 14:15:29 -07:00
fn main() {
yew::start_app::<App>();
}
2021-10-24 02:25:42 -07:00
#[wasm_bindgen]
extern "C" {
fn loadFromDb();
fn createNotFoundUi();
}
2021-10-17 14:15:29 -07:00
struct App;
impl Component for App {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
Self
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
false
}
fn view(&self) -> Html {
html! {
<Router<Route> render={Router::render(render_route)} />
}
}
}
#[derive(Clone, Debug, Switch)]
enum Route {
#[to = "/!"]
Index,
#[rest]
Path(String),
}
2021-10-21 18:35:54 -07:00
#[allow(clippy::needless_pass_by_value)]
2021-10-17 14:15:29 -07:00
fn render_route(route: Route) -> Html {
match route {
Route::Index => html! {
<main>
<p>{ "Hello world" }</p>
</main>
},
Route::Path(_) => html! {
2021-10-24 02:25:42 -07:00
<>
<Paste/>
<section class="hljs fullscreen centered">
<p>{"Loading"}</p>
</section>
</>
2021-10-17 14:15:29 -07:00
},
}
}
2021-10-24 02:25:42 -07:00
pub struct Paste;
2021-10-17 14:15:29 -07:00
impl Component for Paste {
2021-10-24 02:25:42 -07:00
type Message = ();
2021-10-16 09:50:11 -07:00
type Properties = ();
2021-10-17 14:15:29 -07:00
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
let url = String::from(window().location().to_string());
let request_uri = {
let mut uri_parts = url.parse::<Uri>().unwrap().into_parts();
2021-10-21 18:35:54 -07:00
if let Some(parts) = uri_parts.path_and_query.as_mut() {
*parts = PathAndQuery::from_str(&format!("/api{}", parts.path())).unwrap();
}
2021-10-17 14:15:29 -07:00
Uri::from_parts(uri_parts).unwrap()
};
2021-10-19 18:48:32 -07:00
link.send_future(async move {
2021-10-23 10:10:55 -07:00
match Request::get(&request_uri.to_string()).send().await {
2021-10-19 18:48:32 -07:00
Ok(resp) if resp.status() == StatusCode::OK => {
2021-10-19 23:53:22 -07:00
let expires = resp
.headers()
2021-10-23 10:10:55 -07:00
.get(EXPIRES.as_str())
.ok()
.flatten()
.as_deref()
2021-10-24 02:25:42 -07:00
.and_then(|v| Expiration::try_from(v).ok())
.as_ref()
.map(Expiration::to_string)
.unwrap_or_else(|| "This item does not expire.".to_string());
2021-10-23 10:10:55 -07:00
let data = {
Uint8Array::new(
&JsFuture::from(resp.as_raw().array_buffer().unwrap())
.await
.unwrap(),
)
.to_vec()
2021-10-19 18:48:32 -07:00
};
2021-10-19 02:18:33 -07:00
2021-10-22 19:15:23 -07:00
let info = url
.split_once('#')
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
.unwrap_or_default();
let key = info.decryption_key.unwrap();
let nonce = info.nonce.unwrap();
2021-10-24 02:25:42 -07:00
let result = decrypt(data, key, nonce, None);
let decrypted = match result {
Ok(decrypted) => decrypted,
Err(err) => {
// log!("decryption error: {}", err);
// return Box::new(PasteError(err));
unimplemented!()
}
};
2021-10-23 10:10:55 -07:00
2021-10-24 02:25:42 -07:00
let db_open_req = window()
.indexed_db()
.unwrap()
.unwrap()
.open("omegaupload")
.unwrap();
// On success callback
let on_success = Closure::once(Box::new(move |event: Event| {
let target: IdbOpenDbRequest = event.target().unwrap().dyn_into().unwrap();
let db: IdbDatabase = target.result().unwrap().dyn_into().unwrap();
let transaction: IdbObjectStore = db
.transaction_with_str_and_mode(
"decrypted data",
IdbTransactionMode::Readwrite,
)
.unwrap()
.object_store("decrypted data")
.unwrap();
let decrypted_object = Array::new();
match &decrypted {
DecryptedData::String(s) => {
let entry = Array::new();
entry.push(&JsString::from("data"));
entry.push(&JsValue::from_str(&s));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("type"));
entry.push(&JsString::from("string"));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("expiration"));
entry.push(&JsString::from(expires.to_string()));
decrypted_object.push(&entry);
}
DecryptedData::Blob(blob) => {
let entry = Array::new();
entry.push(&JsString::from("data"));
entry.push(blob);
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("type"));
entry.push(&JsString::from("blob"));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("expiration"));
entry.push(&JsString::from(expires.to_string()));
decrypted_object.push(&entry);
}
DecryptedData::Image(blob, (width, height), size) => {
let entry = Array::new();
entry.push(&JsString::from("data"));
entry.push(blob);
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("type"));
entry.push(&JsString::from("image"));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("width"));
entry.push(&JsValue::from(*width));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("height"));
entry.push(&JsValue::from(*height));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("button"));
entry.push(&JsString::from(format!(
"Download {} \u{2014} {} by {}",
Byte::from_bytes(*size as u128).get_appropriate_unit(true),
width,
height,
)));
decrypted_object.push(&entry);
2021-10-24 11:40:19 -07:00
let entry = Array::new();
entry.push(&JsString::from("expiration"));
entry.push(&JsString::from(expires.to_string()));
decrypted_object.push(&entry);
}
DecryptedData::Audio(blob) => {
let entry = Array::new();
entry.push(&JsString::from("data"));
entry.push(blob);
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("type"));
entry.push(&JsString::from("audio"));
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("expiration"));
entry.push(&JsString::from(expires.to_string()));
decrypted_object.push(&entry);
}
DecryptedData::Video(blob) => {
let entry = Array::new();
entry.push(&JsString::from("data"));
entry.push(blob);
decrypted_object.push(&entry);
let entry = Array::new();
entry.push(&JsString::from("type"));
entry.push(&JsString::from("video"));
decrypted_object.push(&entry);
2021-10-24 02:25:42 -07:00
let entry = Array::new();
entry.push(&JsString::from("expiration"));
entry.push(&JsString::from(expires.to_string()));
decrypted_object.push(&entry);
}
}
let db_entry = Object::from_entries(&decrypted_object).unwrap();
transaction
.put_with_key(
&db_entry,
&JsString::from(window().location().pathname().unwrap()),
)
.unwrap()
.set_onsuccess(Some(
Closure::once(Box::new(|| {
log!("success");
loadFromDb();
})
as Box<dyn FnOnce()>)
.into_js_value()
.dyn_ref()
.unwrap(),
));
})
as Box<dyn FnOnce(Event)>);
db_open_req.set_onsuccess(Some(on_success.into_js_value().dyn_ref().unwrap()));
// On upgrade callback
let on_upgrade = Closure::wrap(Box::new(move |event: Event| {
let target: IdbOpenDbRequest = event.target().unwrap().dyn_into().unwrap();
let db: IdbDatabase = target.result().unwrap().dyn_into().unwrap();
let _obj_store = db.create_object_store("decrypted data").unwrap();
}) as Box<dyn FnMut(Event)>);
db_open_req
.set_onupgradeneeded(Some(on_upgrade.into_js_value().dyn_ref().unwrap()));
2021-10-19 02:18:33 -07:00
}
2021-10-21 18:35:54 -07:00
Ok(resp) if resp.status() == StatusCode::NOT_FOUND => {
2021-10-24 02:25:42 -07:00
createNotFoundUi();
2021-10-21 18:35:54 -07:00
}
2021-10-24 02:25:42 -07:00
Ok(resp) if resp.status() == StatusCode::BAD_REQUEST => {}
Ok(err) => {}
Err(err) => {}
};
2021-10-19 18:48:32 -07:00
});
2021-10-23 10:10:55 -07:00
2021-10-24 02:25:42 -07:00
Self
2021-10-16 09:50:11 -07:00
}
2021-10-17 14:15:29 -07:00
2021-10-24 02:25:42 -07:00
fn update(&mut self, _: Self::Message) -> ShouldRender {
false
2021-10-19 02:18:33 -07:00
}
2021-10-24 02:25:42 -07:00
fn change(&mut self, _: Self::Properties) -> ShouldRender {
2021-10-19 02:18:33 -07:00
false
}
fn view(&self) -> Html {
2021-10-24 02:25:42 -07:00
html! {}
2021-10-19 02:18:33 -07:00
}
}