Compare commits
16 commits
c73af50857
...
253fccaf78
Author | SHA1 | Date | |
---|---|---|---|
|
253fccaf78 | ||
|
b57298ffb2 | ||
|
4e7b3dfd3b | ||
|
a9e9a93493 | ||
|
37bdbae640 | ||
|
3ab01300b2 | ||
|
3c9c46da18 | ||
|
37727bfd3d | ||
|
774b13e46c | ||
|
b793139a99 | ||
|
57bcd6371c | ||
|
064fd749a4 | ||
|
4694683b9a | ||
|
c934b36b35 | ||
|
711f79a255 | ||
|
9a7f13f8b9 |
19 changed files with 905 additions and 657 deletions
637
Cargo.lock
generated
637
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -113,5 +113,4 @@ There are a few reasons to not use OmegaUpload:
|
|||
- Cannot download files larger than 512 MiB through the web frontend—this
|
||||
is a technical limitation of the current web frontend not using a web worker
|
||||
in addition to the fact that browsers are not optimized for XChaCha20.
|
||||
- Right now, you must upload via the CLI tool.
|
||||
- The frontend uses WASM, which is a novel attack surface.
|
||||
|
|
|
@ -22,6 +22,6 @@ CUR_DIR=$(pwd)
|
|||
PROJECT_TOP_LEVEL=$(git rev-parse --show-toplevel)
|
||||
|
||||
cd "$PROJECT_TOP_LEVEL" || exit 1
|
||||
git submodule foreach git pull
|
||||
git submodule update --remote
|
||||
|
||||
cd "$CUR_DIR"
|
||||
cd "$CUR_DIR"
|
||||
|
|
|
@ -9,10 +9,9 @@ license = "GPL-3.0-or-later"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
omegaupload-common = "0.2"
|
||||
|
||||
anyhow = "1"
|
||||
atty = "0.2"
|
||||
clap = { version = "3", features = ["derive"] }
|
||||
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] }
|
||||
rpassword = "5"
|
||||
omegaupload-common = { path = "../common" }
|
||||
anyhow = "1.0.58"
|
||||
atty = "0.2.14"
|
||||
clap = { version = "3.2.15", features = ["derive"] }
|
||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "blocking"] }
|
||||
rpassword = "7.0.0"
|
||||
|
|
|
@ -24,6 +24,7 @@ use anyhow::{anyhow, bail, Context, Result};
|
|||
use atty::Stream;
|
||||
use clap::Parser;
|
||||
use omegaupload_common::crypto::{open_in_place, seal_in_place};
|
||||
use omegaupload_common::fragment::Builder;
|
||||
use omegaupload_common::secrecy::{ExposeSecret, SecretString, SecretVec};
|
||||
use omegaupload_common::{
|
||||
base64, Expiration, ParsedUrl, Url, API_ENDPOINT, EXPIRATION_HEADER_NAME,
|
||||
|
@ -31,11 +32,7 @@ use omegaupload_common::{
|
|||
use reqwest::blocking::Client;
|
||||
use reqwest::header::EXPIRES;
|
||||
use reqwest::StatusCode;
|
||||
use rpassword::prompt_password_stderr;
|
||||
|
||||
use crate::fragment::Builder;
|
||||
|
||||
mod fragment;
|
||||
use rpassword::prompt_password;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Opts {
|
||||
|
@ -120,8 +117,7 @@ fn handle_upload(
|
|||
}
|
||||
|
||||
let password = if password {
|
||||
let maybe_password =
|
||||
prompt_password_stderr("Please set the password for this paste: ")?;
|
||||
let maybe_password = prompt_password("Please set the password for this paste: ")?;
|
||||
Some(SecretVec::new(maybe_password.into_bytes()))
|
||||
} else {
|
||||
None
|
||||
|
@ -200,8 +196,7 @@ fn handle_download(mut url: ParsedUrl) -> Result<()> {
|
|||
|
||||
let password = if url.needs_password {
|
||||
// Only print prompt on interactive, else it messes with output
|
||||
let maybe_password =
|
||||
prompt_password_stderr("Please enter the password to access this paste: ")?;
|
||||
let maybe_password = prompt_password("Please enter the password to access this paste: ")?;
|
||||
Some(SecretVec::new(maybe_password.into_bytes()))
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -9,24 +9,24 @@ license = "MIT"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
bytes = { version = "1", features = ["serde"] }
|
||||
chacha20poly1305 = { version = "0.9", features = ["stream", "std"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
headers = "0.3"
|
||||
lazy_static = "1"
|
||||
rand = "0.8"
|
||||
secrecy = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1"
|
||||
typenum = "1"
|
||||
url = "2"
|
||||
argon2 = "0.3.1"
|
||||
base64 = "0.13.0"
|
||||
bytes = { version = "1.2.0", features = ["serde"] }
|
||||
chacha20poly1305 = { version = "0.9.1", features = ["stream", "std"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
headers = "0.3.7"
|
||||
lazy_static = "1.4.0"
|
||||
rand = "0.8.5"
|
||||
secrecy = "0.8.0"
|
||||
serde = { version = "1.0.140", features = ["derive"] }
|
||||
thiserror = "1.0.31"
|
||||
typenum = "1.15.0"
|
||||
url = "2.2.2"
|
||||
argon2 = "0.4.1"
|
||||
|
||||
# Wasm features
|
||||
gloo-console = { version = "0.2", optional = true }
|
||||
http = { version = "0.2", optional = true }
|
||||
web-sys = { version = "0.3", features = ["Headers"], optional = true }
|
||||
gloo-console = { version = "0.2.1", optional = true }
|
||||
reqwasm = { version = "0.5.0", optional = true }
|
||||
http = { version = "0.2.8", optional = true }
|
||||
|
||||
[features]
|
||||
wasm = ["gloo-console", "http", "web-sys"]
|
||||
wasm = ["gloo-console", "reqwasm", "http"]
|
||||
|
|
|
@ -294,3 +294,43 @@ fn get_argon2() -> Argon2<'static> {
|
|||
pub fn get_csrng() -> impl CryptoRng + Rng {
|
||||
rand::thread_rng()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::open_in_place;
|
||||
use super::seal_in_place;
|
||||
use crate::crypto::SecretVec;
|
||||
|
||||
macro_rules! test_encryption {
|
||||
($($name:ident, $content:expr, $password:expr),*) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
let mut m = $content;
|
||||
let n: Vec<u8> = $content;
|
||||
let key = seal_in_place(&mut m, $password).unwrap();
|
||||
assert_ne!(m, n);
|
||||
assert!(open_in_place(&mut m, &key, $password).is_ok());
|
||||
assert_eq!(m, n);
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
test_encryption!(empty, vec![], None);
|
||||
test_encryption!(
|
||||
empty_password,
|
||||
vec![],
|
||||
Some(SecretVec::from(b"password".to_vec()))
|
||||
);
|
||||
test_encryption!(
|
||||
normal,
|
||||
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
None
|
||||
);
|
||||
test_encryption!(
|
||||
normal_password,
|
||||
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
Some(SecretVec::from(b"password".to_vec()))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use omegaupload_common::secrecy::{ExposeSecret, SecretString};
|
||||
use crate::secrecy::{ExposeSecret, SecretString};
|
||||
|
||||
pub struct Builder {
|
||||
decryption_key: SecretString,
|
||||
|
@ -8,6 +8,7 @@ pub struct Builder {
|
|||
}
|
||||
|
||||
impl Builder {
|
||||
#[must_use]
|
||||
pub fn new(decryption_key: SecretString) -> Self {
|
||||
Self {
|
||||
decryption_key,
|
||||
|
@ -17,6 +18,7 @@ impl Builder {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn needs_password(mut self) -> Self {
|
||||
self.needs_password = true;
|
||||
self
|
||||
|
@ -24,6 +26,7 @@ impl Builder {
|
|||
|
||||
// False positive
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
#[must_use]
|
||||
pub fn file_name(mut self, name: String) -> Self {
|
||||
self.file_name = Some(name);
|
||||
self
|
||||
|
@ -31,11 +34,13 @@ impl Builder {
|
|||
|
||||
// False positive
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
#[must_use]
|
||||
pub fn language(mut self, language: String) -> Self {
|
||||
self.language = Some(language);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn build(self) -> SecretString {
|
||||
if !self.needs_password && self.file_name.is_none() && self.language.is_none() {
|
||||
return self.decryption_key;
|
|
@ -1,4 +1,6 @@
|
|||
#![warn(clippy::nursery, clippy::pedantic)]
|
||||
// False positive: https://github.com/rust-lang/rust-clippy/issues/6902
|
||||
#![allow(clippy::use_self)]
|
||||
|
||||
//! Contains common functions and structures used by multiple projects
|
||||
|
||||
|
@ -39,6 +41,7 @@ use crate::crypto::Key;
|
|||
|
||||
pub mod base64;
|
||||
pub mod crypto;
|
||||
pub mod fragment;
|
||||
|
||||
pub const API_ENDPOINT: &str = "/api";
|
||||
|
||||
|
@ -230,10 +233,10 @@ expiration_from_str! {
|
|||
impl Display for Expiration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Expiration::BurnAfterReading | Expiration::BurnAfterReadingWithDeadline(_) => {
|
||||
Self::BurnAfterReading | Self::BurnAfterReadingWithDeadline(_) => {
|
||||
write!(f, "This item has been burned. You now have the only copy.")
|
||||
}
|
||||
Expiration::UnixTime(time) => write!(
|
||||
Self::UnixTime(time) => write!(
|
||||
f,
|
||||
"{}",
|
||||
time.format("This item will expire on %A, %B %-d, %Y at %T %Z.")
|
||||
|
@ -248,7 +251,7 @@ lazy_static! {
|
|||
|
||||
impl Header for Expiration {
|
||||
fn name() -> &'static HeaderName {
|
||||
&*EXPIRATION_HEADER_NAME
|
||||
&EXPIRATION_HEADER_NAME
|
||||
}
|
||||
|
||||
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
|
||||
|
@ -282,6 +285,8 @@ impl From<&Expiration> for HeaderValue {
|
|||
}
|
||||
|
||||
impl From<Expiration> for HeaderValue {
|
||||
// False positive: https://github.com/rust-lang/rust-clippy/issues/9095
|
||||
#[allow(clippy::needless_borrow)]
|
||||
fn from(expiration: Expiration) -> Self {
|
||||
(&expiration).into()
|
||||
}
|
||||
|
@ -290,14 +295,12 @@ impl From<Expiration> for HeaderValue {
|
|||
pub struct ParseHeaderValueError;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
impl TryFrom<web_sys::Headers> for Expiration {
|
||||
impl TryFrom<reqwasm::http::Headers> for Expiration {
|
||||
type Error = ParseHeaderValueError;
|
||||
|
||||
fn try_from(headers: web_sys::Headers) -> Result<Self, Self::Error> {
|
||||
fn try_from(headers: reqwasm::http::Headers) -> Result<Self, Self::Error> {
|
||||
headers
|
||||
.get(http::header::EXPIRES.as_str())
|
||||
.ok()
|
||||
.flatten()
|
||||
.as_deref()
|
||||
.and_then(|v| Self::try_from(v).ok())
|
||||
.ok_or(ParseHeaderValueError)
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
"highlight.js": "^11.4.0",
|
||||
"highlightjs-line-numbers.js": "^2.8.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"react-dom": "^17.0.2",
|
||||
"source-map-loader": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode production",
|
||||
|
|
|
@ -7,24 +7,24 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
omegaupload-common = { path = "../common" }
|
||||
anyhow = "1"
|
||||
axum = { version = "0.4", features = ["http2", "headers"] }
|
||||
bincode = "1"
|
||||
anyhow = "1.0.58"
|
||||
axum = { version = "0.5.14", features = ["http2", "headers"] }
|
||||
bincode = "1.3.3"
|
||||
# We don't care about which version (We want to match with axum), we just need
|
||||
# to enable the feature
|
||||
bytes = { version = "*", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.3"
|
||||
bytes = { version = "1.2.0", features = ["serde"] }
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
futures = "0.3.21"
|
||||
# We just need to pull in whatever axum is pulling in
|
||||
headers = "*"
|
||||
lazy_static = "1"
|
||||
headers = "0.3.7"
|
||||
lazy_static = "1.4.0"
|
||||
# Disable `random()` and `thread_rng()`
|
||||
rand = { version = "0.8", default_features = false }
|
||||
rocksdb = { version = "0.18", default_features = false, features = ["zstd"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
signal-hook = "0.3"
|
||||
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
tower-http = { version = "0.2", features = ["fs"] }
|
||||
tracing = { version = "0.1" }
|
||||
tracing-subscriber = "0.3"
|
||||
rand = { version = "0.8.5", default-features = false }
|
||||
rocksdb = { version = "0.18.0", default-features = false, features = ["zstd"] }
|
||||
serde = { version = "1.0.140", features = ["derive"] }
|
||||
signal-hook = "0.3.14"
|
||||
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
|
||||
tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] }
|
||||
tower-http = { version = "0.3.4", features = ["fs"] }
|
||||
tracing = "0.1.35"
|
||||
tracing-subscriber = "0.3.15"
|
||||
|
|
|
@ -27,7 +27,7 @@ use axum::extract::{Extension, Path, TypedHeader};
|
|||
use axum::http::header::EXPIRES;
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{get, get_service, post};
|
||||
use axum::{AddExtensionLayer, Router};
|
||||
use axum::Router;
|
||||
use chrono::Utc;
|
||||
use futures::stream::StreamExt;
|
||||
use headers::HeaderMap;
|
||||
|
@ -103,7 +103,7 @@ async fn main() -> Result<()> {
|
|||
&format!("{API_ENDPOINT}/:code"),
|
||||
get(paste::<SHORT_CODE_SIZE>).delete(delete::<SHORT_CODE_SIZE>),
|
||||
)
|
||||
.layer(AddExtensionLayer::new(db))
|
||||
.layer(axum::Extension(db))
|
||||
.into_make_service()
|
||||
})
|
||||
.await?;
|
||||
|
|
|
@ -9,27 +9,27 @@ crate-type = ["cdylib"]
|
|||
[dependencies]
|
||||
omegaupload-common = { path = "../common", features = ["wasm"] }
|
||||
# Enables wasm support
|
||||
getrandom = { version = "*", features = ["js"] }
|
||||
getrandom = { version = "0.2.7", features = ["js"] }
|
||||
|
||||
anyhow = "1"
|
||||
bytes = "1"
|
||||
byte-unit = "4"
|
||||
console_error_panic_hook = "0.1"
|
||||
gloo-console = "0.2"
|
||||
http = "0.2"
|
||||
js-sys = "0.3"
|
||||
mime_guess = "2"
|
||||
reqwasm = "0.4"
|
||||
tree_magic_mini = { version = "3", features = ["with-gpl-data"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"]}
|
||||
wasm-bindgen-futures = "0.4"
|
||||
zip = { version = "0.5", default-features = false, features = ["deflate"] }
|
||||
flate2 = "1.0.22"
|
||||
anyhow = "1.0.58"
|
||||
bytes = "1.2.0"
|
||||
byte-unit = "4.0.14"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
gloo-console = "0.2.1"
|
||||
http = "0.2.8"
|
||||
js-sys = "0.3.59"
|
||||
mime_guess = "2.0.4"
|
||||
reqwasm = "0.5.0"
|
||||
tree_magic_mini = { version = "3.0.3", features = ["with-gpl-data"] }
|
||||
serde = { version = "1.0.140", features = ["derive"] }
|
||||
wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = "0.4.32"
|
||||
zip = { version = "0.6.2", default-features = false, features = ["deflate"] }
|
||||
flate2 = "1.0.24"
|
||||
tar = "0.4.38"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
version = "0.3.59"
|
||||
features = [
|
||||
"BlobPropertyBag",
|
||||
"TextDecoder",
|
||||
|
|
|
@ -25,9 +25,12 @@ use gloo_console::{error, log};
|
|||
use http::uri::PathAndQuery;
|
||||
use http::{StatusCode, Uri};
|
||||
use js_sys::{Array, JsString, Object, Uint8Array};
|
||||
use omegaupload_common::base64;
|
||||
use omegaupload_common::crypto::seal_in_place;
|
||||
use omegaupload_common::crypto::{Error as CryptoError, Key};
|
||||
use omegaupload_common::secrecy::{Secret, SecretVec};
|
||||
use omegaupload_common::{Expiration, PartialParsedUrl};
|
||||
use omegaupload_common::fragment::Builder;
|
||||
use omegaupload_common::secrecy::{ExposeSecret, Secret, SecretString, SecretVec};
|
||||
use omegaupload_common::{Expiration, PartialParsedUrl, Url};
|
||||
use reqwasm::http::Request;
|
||||
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
|
@ -50,6 +53,8 @@ extern "C" {
|
|||
pub fn load_from_db(mime_type: JsString, name: Option<JsString>, language: Option<JsString>);
|
||||
#[wasm_bindgen(js_name = renderMessage)]
|
||||
pub fn render_message(message: JsString);
|
||||
#[wasm_bindgen(js_name = createUploadUi)]
|
||||
pub fn create_upload_ui();
|
||||
}
|
||||
|
||||
fn window() -> Window {
|
||||
|
@ -75,7 +80,7 @@ pub fn start() {
|
|||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
||||
if location().pathname().unwrap() == "/" {
|
||||
render_message("Go away".into());
|
||||
create_upload_ui();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -166,6 +171,55 @@ pub fn start() {
|
|||
});
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::future_not_send)]
|
||||
pub fn encrypt_string(data: String) {
|
||||
spawn_local(async move {
|
||||
if let Err(e) = do_encrypt(data.into_bytes()).await {
|
||||
log!(format!("[rs] Error encrypting string: {}", e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::future_not_send)]
|
||||
pub fn encrypt_array_buffer(data: Vec<u8>) {
|
||||
spawn_local(async move {
|
||||
if let Err(e) = do_encrypt(data).await {
|
||||
log!(format!("[rs] Error encrypting array buffer: {}", e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(clippy::future_not_send)]
|
||||
async fn do_encrypt(mut data: Vec<u8>) -> Result<()> {
|
||||
let (data, key) = {
|
||||
let enc_key = seal_in_place(&mut data, None)?;
|
||||
let key = SecretString::new(base64::encode(&enc_key.expose_secret().as_ref()));
|
||||
(data, key)
|
||||
};
|
||||
|
||||
let s: String = location().to_string().into();
|
||||
let mut url = Url::from_str(&s)?;
|
||||
let fragment = Builder::new(key);
|
||||
|
||||
let js_data = Uint8Array::new_with_length(u32::try_from(data.len()).expect("Data too large"));
|
||||
js_data.copy_from(&data);
|
||||
|
||||
let short_code = Request::post(url.as_ref())
|
||||
.body(js_data)
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
url.set_path(&short_code);
|
||||
url.set_fragment(Some(fragment.build().expose_secret()));
|
||||
location()
|
||||
.set_href(url.as_ref())
|
||||
.expect("Unable to navigate to encrypted upload");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::future_not_send)]
|
||||
async fn fetch_resources(
|
||||
request_uri: Uri,
|
||||
|
|
|
@ -95,6 +95,14 @@ img, audio, video {
|
|||
max-width: 75vw;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 75vw;
|
||||
min-height: 75vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.primary {
|
||||
@extend .hljs;
|
||||
}
|
||||
|
@ -108,4 +116,4 @@ img, audio, video {
|
|||
@extend .align-right;
|
||||
padding-left: $padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,64 @@
|
|||
|
||||
import './main.scss';
|
||||
import ReactDom from 'react-dom';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { encrypt_string, encrypt_array_buffer } from '../pkg';
|
||||
|
||||
const hljs = require('highlight.js');
|
||||
import hljs from 'highlight.js'
|
||||
(window as any).hljs = hljs;
|
||||
require('highlightjs-line-numbers.js');
|
||||
|
||||
const FileForm = () => {
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const fr = new FileReader();
|
||||
fr.onload = (e) => {
|
||||
const { result } = e.target;
|
||||
if (result instanceof ArrayBuffer) {
|
||||
let data = new Uint8Array(result);
|
||||
encrypt_array_buffer(data);
|
||||
}
|
||||
}
|
||||
fr.readAsArrayBuffer((event.target as HTMLInputElement).files[0]);
|
||||
}
|
||||
|
||||
return (
|
||||
<input type="file" onChange={handleChange} />
|
||||
)
|
||||
}
|
||||
|
||||
const PasteForm = () => {
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
encrypt_string(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<pre className='paste'>
|
||||
<form className='hljs centered' onSubmit={handleSubmit}>
|
||||
<textarea
|
||||
placeholder="Sample text"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<input type="submit" value="submit" />
|
||||
</form>
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
function createUploadUi() {
|
||||
const html = <main className='hljs centered fullscreen'>
|
||||
<FileForm />
|
||||
<PasteForm />
|
||||
</main>;
|
||||
|
||||
ReactDom.render(html, document.body);
|
||||
}
|
||||
|
||||
function loadFromDb(mimeType: string, name?: string, language?: string) {
|
||||
let resolvedName;
|
||||
let resolvedName: string;
|
||||
if (name) {
|
||||
resolvedName = name;
|
||||
} else {
|
||||
|
@ -287,4 +337,4 @@ function getObjectUrl(data, mimeType?: string) {
|
|||
|
||||
window.addEventListener("hashchange", () => location.reload());
|
||||
|
||||
export { renderMessage, loadFromDb };
|
||||
export { renderMessage, createUploadUi, loadFromDb };
|
||||
|
|
2
web/vendor/MPLUS_FONTS
vendored
2
web/vendor/MPLUS_FONTS
vendored
|
@ -1 +1 @@
|
|||
Subproject commit a1268635894c5ee23dfdece570418ca07b66c3fc
|
||||
Subproject commit 8690be3625964d9992e7be4bc3e1a61a80161cc6
|
|
@ -2,6 +2,7 @@ const path = require('path');
|
|||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||
const { SourceMapDevToolPlugin } = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: './web/src/index.js',
|
||||
|
@ -21,6 +22,8 @@ module.exports = {
|
|||
"css-loader",
|
||||
// Compiles Sass to CSS
|
||||
"sass-loader",
|
||||
// source map for debugging
|
||||
"source-map-loader"
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -41,6 +44,7 @@ module.exports = {
|
|||
crateDirectory: path.resolve(__dirname, "web"),
|
||||
outDir: path.resolve(__dirname, "web/pkg"),
|
||||
}),
|
||||
new SourceMapDevToolPlugin({}),
|
||||
],
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
|
|
Loading…
Reference in a new issue