image support
This commit is contained in:
parent
5d4adc91ed
commit
17dd44c8cc
6 changed files with 517 additions and 148 deletions
257
Cargo.lock
generated
257
Cargo.lock
generated
|
@ -2,6 +2,18 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler32"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aead"
|
name = "aead"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -176,6 +188,18 @@ version = "3.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
|
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -305,6 +329,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -324,6 +354,69 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -339,6 +432,12 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.28"
|
version = "0.8.28"
|
||||||
|
@ -458,6 +557,16 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -682,6 +791,25 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.23.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"gif",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
"scoped_threadpool",
|
||||||
|
"tiff",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -713,6 +841,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.1.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
|
||||||
|
dependencies = [
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.55"
|
version = "0.3.55"
|
||||||
|
@ -805,12 +942,40 @@ version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
@ -875,6 +1040,28 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -953,8 +1140,12 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"gloo-console",
|
"gloo-console",
|
||||||
"http",
|
"http",
|
||||||
|
"image",
|
||||||
|
"js-sys",
|
||||||
"omegaupload-common",
|
"omegaupload-common",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"yew",
|
"yew",
|
||||||
"yew-router",
|
"yew-router",
|
||||||
|
@ -1023,6 +1214,18 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.16.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"deflate",
|
||||||
|
"miniz_oxide 0.3.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "poly1305"
|
name = "poly1305"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
@ -1140,6 +1343,31 @@ dependencies = [
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.5.4"
|
||||||
|
@ -1252,6 +1480,18 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped_threadpool"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sct"
|
name = "sct"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
@ -1472,6 +1712,17 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
|
||||||
|
dependencies = [
|
||||||
|
"jpeg-decoder",
|
||||||
|
"miniz_oxide 0.4.4",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
|
@ -1869,6 +2120,12 @@ dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -266,6 +266,8 @@ impl Header for Expiration {
|
||||||
|
|
||||||
impl From<&Expiration> for HeaderValue {
|
impl From<&Expiration> for HeaderValue {
|
||||||
fn from(expiration: &Expiration) -> Self {
|
fn from(expiration: &Expiration) -> Self {
|
||||||
|
// SAFETY: All possible values of `Expiration` are valid header values,
|
||||||
|
// so we don't need the extra check.
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::from_maybe_shared_unchecked(match expiration {
|
Self::from_maybe_shared_unchecked(match expiration {
|
||||||
Expiration::BurnAfterReading => Bytes::from_static(b"0"),
|
Expiration::BurnAfterReading => Bytes::from_static(b"0"),
|
||||||
|
|
|
@ -15,8 +15,12 @@ bytes = "1"
|
||||||
downcast-rs = "1"
|
downcast-rs = "1"
|
||||||
gloo-console = "0.1"
|
gloo-console = "0.1"
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
|
image = "0.23"
|
||||||
|
js-sys = "0.3"
|
||||||
reqwest = { version = "0.11", default_features = false, features = ["tokio-rustls"] }
|
reqwest = { version = "0.11", default_features = false, features = ["tokio-rustls"] }
|
||||||
web-sys = { version = "0.3" }
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
web-sys = { version = "0.3", features = ["TextDecoder"] }
|
||||||
yew = { version = "0.18", features = ["wasm-bindgen-futures"] }
|
yew = { version = "0.18", features = ["wasm-bindgen-futures"] }
|
||||||
yew-router = "0.15"
|
yew-router = "0.15"
|
||||||
yewtil = "0.4"
|
yewtil = "0.4"
|
|
@ -10,60 +10,12 @@
|
||||||
<link data-trunk rel="copy-file" href="vendor/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js"
|
<link data-trunk rel="copy-file" href="vendor/highlightjs-line-numbers.js/dist/highlightjs-line-numbers.min.js"
|
||||||
dest="/" />
|
dest="/" />
|
||||||
<link data-trunk rel="copy-file" href="src/reload_on_hash_change.js" dest="/" />
|
<link data-trunk rel="copy-file" href="src/reload_on_hash_change.js" dest="/" />
|
||||||
|
|
||||||
<link data-trunk rel="css" href="vendor/highlight.js/src/styles/github-dark.css" />
|
<link data-trunk rel="css" href="vendor/highlight.js/src/styles/github-dark.css" />
|
||||||
|
<link data-trunk rel="scss" href="src/main.scss" />
|
||||||
|
|
||||||
<script src="reload_on_hash_change.js" async></script>
|
<script src="reload_on_hash_change.js" async></script>
|
||||||
<script src="highlight.min.js" defer></script>
|
<script src="highlight.min.js" defer></script>
|
||||||
<script src="highlightjs-line-numbers.min.js" defer></script>
|
<script src="highlightjs-line-numbers.min.js" defer></script>
|
||||||
|
|
||||||
<style>
|
|
||||||
@font-face {
|
|
||||||
font-family: "Mplus Code";
|
|
||||||
src: url("./MplusCodeLatin[wdth,wght].ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #404040;
|
|
||||||
font-family: 'Mplus Code', sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paste {
|
|
||||||
border-radius: 1em;
|
|
||||||
margin: 0;
|
|
||||||
padding: 1em;
|
|
||||||
background-color: #0d1117;
|
|
||||||
box-shadow: 0 0 1em black;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
font-family: 'Mplus Code', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-ln td.hljs-ln-numbers {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre header {
|
|
||||||
user-select: none;
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
</html>
|
</html>
|
277
web/src/main.rs
277
web/src/main.rs
|
@ -1,17 +1,22 @@
|
||||||
#![warn(clippy::nursery, clippy::pedantic)]
|
#![warn(clippy::nursery, clippy::pedantic)]
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail, Context};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use downcast_rs::{impl_downcast, Downcast};
|
use downcast_rs::{impl_downcast, Downcast};
|
||||||
|
use gloo_console::log;
|
||||||
use http::header::EXPIRES;
|
use http::header::EXPIRES;
|
||||||
use http::uri::PathAndQuery;
|
use http::uri::PathAndQuery;
|
||||||
use http::{StatusCode, Uri};
|
use http::{StatusCode, Uri};
|
||||||
|
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
||||||
use omegaupload_common::crypto::{open, Key, Nonce};
|
use omegaupload_common::crypto::{open, Key, Nonce};
|
||||||
use omegaupload_common::{Expiration, PartialParsedUrl};
|
use omegaupload_common::{Expiration, PartialParsedUrl};
|
||||||
use yew::format::Nothing;
|
use wasm_bindgen::JsCast;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
use web_sys::TextDecoder;
|
||||||
|
use web_sys::{Blob, Url};
|
||||||
use yew::utils::window;
|
use yew::utils::window;
|
||||||
use yew::Properties;
|
use yew::Properties;
|
||||||
use yew::{html, Component, ComponentLink, Html, ShouldRender};
|
use yew::{html, Component, ComponentLink, Html, ShouldRender};
|
||||||
|
@ -98,25 +103,27 @@ impl Component for Paste {
|
||||||
.headers()
|
.headers()
|
||||||
.get(EXPIRES)
|
.get(EXPIRES)
|
||||||
.and_then(|v| Expiration::try_from(v).ok());
|
.and_then(|v| Expiration::try_from(v).ok());
|
||||||
let partial = match resp.bytes().await {
|
let bytes = match resp.bytes().await {
|
||||||
Ok(bytes) => PastePartial::new(
|
Ok(bytes) => bytes,
|
||||||
bytes,
|
|
||||||
expires,
|
|
||||||
&url.split_once('#')
|
|
||||||
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
link_clone,
|
|
||||||
),
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Box::new(PasteError(anyhow!("Got {}.", e)))
|
return Box::new(PasteError(anyhow!("Got {}.", e)))
|
||||||
as Box<dyn PasteState>
|
as Box<dyn PasteState>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(completed) = PasteComplete::try_from(partial.clone()) {
|
let info = url
|
||||||
Box::new(completed) as Box<dyn PasteState>
|
.split_once('#')
|
||||||
|
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let key = info.decryption_key.unwrap();
|
||||||
|
let nonce = info.nonce.unwrap();
|
||||||
|
|
||||||
|
if let Ok(completed) = decrypt(bytes, key, nonce, None) {
|
||||||
|
Box::new(PasteComplete::new(link_clone, completed, expires))
|
||||||
|
as Box<dyn PasteState>
|
||||||
} else {
|
} else {
|
||||||
Box::new(partial) as Box<dyn PasteState>
|
todo!()
|
||||||
|
// Box::new(partial) as Box<dyn PasteState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(resp) if resp.status() == StatusCode::NOT_FOUND => {
|
Ok(resp) if resp.status() == StatusCode::NOT_FOUND => {
|
||||||
|
@ -154,7 +161,7 @@ impl Component for Paste {
|
||||||
|
|
||||||
if self.state.is::<PasteNotFound>() {
|
if self.state.is::<PasteNotFound>() {
|
||||||
return html! {
|
return html! {
|
||||||
<section class={"hljs error"}>
|
<section class={"hljs centered"}>
|
||||||
<p>{ "Either the paste has been burned or one never existed." }</p>
|
<p>{ "Either the paste has been burned or one never existed." }</p>
|
||||||
</section>
|
</section>
|
||||||
};
|
};
|
||||||
|
@ -162,7 +169,7 @@ impl Component for Paste {
|
||||||
|
|
||||||
if self.state.is::<PasteBadRequest>() {
|
if self.state.is::<PasteBadRequest>() {
|
||||||
return html! {
|
return html! {
|
||||||
<section class={"hljs error"}>
|
<section class={"hljs centered"}>
|
||||||
<p>{ "Bad Request. Is this a valid paste URL?" }</p>
|
<p>{ "Bad Request. Is this a valid paste URL?" }</p>
|
||||||
</section>
|
</section>
|
||||||
};
|
};
|
||||||
|
@ -170,7 +177,7 @@ impl Component for Paste {
|
||||||
|
|
||||||
if let Some(error) = self.state.downcast_ref::<PasteError>() {
|
if let Some(error) = self.state.downcast_ref::<PasteError>() {
|
||||||
return html! {
|
return html! {
|
||||||
<section class={"hljs error"}><p>{ error.0.to_string() }</p></section>
|
<section class={"hljs centered"}><p>{ error.0.to_string() }</p></section>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,11 +210,16 @@ struct PastePartial {
|
||||||
|
|
||||||
#[derive(Properties, Clone)]
|
#[derive(Properties, Clone)]
|
||||||
struct PasteComplete {
|
struct PasteComplete {
|
||||||
data: Bytes,
|
parent: ComponentLink<Paste>,
|
||||||
|
decrypted: DecryptedData,
|
||||||
expires: Option<Expiration>,
|
expires: Option<Expiration>,
|
||||||
key: Key,
|
}
|
||||||
nonce: Nonce,
|
|
||||||
password: Option<Key>,
|
#[derive(Clone)]
|
||||||
|
enum DecryptedData {
|
||||||
|
String(String),
|
||||||
|
Blob(Blob),
|
||||||
|
Image(Blob),
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PasteState: Downcast {}
|
trait PasteState: Downcast {}
|
||||||
|
@ -276,16 +288,18 @@ impl Component for PastePartial {
|
||||||
if (self.needs_pw && maybe_password.is_some())
|
if (self.needs_pw && maybe_password.is_some())
|
||||||
|| (!self.needs_pw && maybe_password.is_none()) =>
|
|| (!self.needs_pw && maybe_password.is_none()) =>
|
||||||
{
|
{
|
||||||
|
let parent = self.parent.clone();
|
||||||
let data = self.data.clone();
|
let data = self.data.clone();
|
||||||
let expires = self.expires;
|
let expires = self.expires;
|
||||||
self.parent.callback_once(move |Nothing| {
|
|
||||||
Box::new(PasteComplete::new(
|
self.parent.send_future(async move {
|
||||||
data,
|
match decrypt(data, key, nonce, maybe_password) {
|
||||||
expires,
|
Ok(decrypted) => Box::new(PasteComplete::new(parent, decrypted, expires))
|
||||||
key,
|
as Box<dyn PasteState>,
|
||||||
nonce,
|
Err(e) => {
|
||||||
maybe_password,
|
todo!()
|
||||||
)) as Box<dyn PasteState>
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -307,92 +321,159 @@ impl Component for PastePartial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<PastePartial> for PasteComplete {
|
fn decrypt(
|
||||||
type Error = anyhow::Error;
|
encrypted: Bytes,
|
||||||
|
key: Key,
|
||||||
|
nonce: Nonce,
|
||||||
|
maybe_password: Option<Key>,
|
||||||
|
) -> Result<DecryptedData, PasteCompleteConstructionError> {
|
||||||
|
let stage_one = maybe_password.map_or_else(
|
||||||
|
|| Ok(encrypted.to_vec()),
|
||||||
|
|password| open(&encrypted, &nonce.increment(), &password),
|
||||||
|
);
|
||||||
|
|
||||||
fn try_from(partial: PastePartial) -> Result<Self, Self::Error> {
|
let stage_one = stage_one.map_err(|_| PasteCompleteConstructionError::StageOneFailure)?;
|
||||||
match partial {
|
|
||||||
PastePartial {
|
let stage_two = open(&stage_one, &nonce, &key)
|
||||||
data,
|
.map_err(|_| PasteCompleteConstructionError::StageTwoFailure)?;
|
||||||
key: Some(key),
|
|
||||||
expires,
|
if let Ok(decrypted) = std::str::from_utf8(&stage_two) {
|
||||||
nonce: Some(nonce),
|
Ok(DecryptedData::String(decrypted.to_owned()))
|
||||||
password: Some(password),
|
} else {
|
||||||
needs_pw: true,
|
let blob_chunks = Array::new_with_length(stage_two.chunks(65536).len().try_into().unwrap());
|
||||||
..
|
for (i, chunk) in stage_two.chunks(65536).enumerate() {
|
||||||
} => Ok(Self {
|
let array = Uint8Array::new_with_length(chunk.len().try_into().unwrap());
|
||||||
data,
|
array.copy_from(&chunk);
|
||||||
expires,
|
blob_chunks.set(i.try_into().unwrap(), array.dyn_into().unwrap());
|
||||||
key,
|
}
|
||||||
nonce,
|
let blob = Blob::new_with_u8_array_sequence(blob_chunks.dyn_ref().unwrap()).unwrap();
|
||||||
password: Some(password),
|
|
||||||
}),
|
if image::guess_format(&stage_two).is_ok() {
|
||||||
PastePartial {
|
Ok(DecryptedData::Image(blob))
|
||||||
data,
|
} else {
|
||||||
key: Some(key),
|
Ok(DecryptedData::Blob(blob))
|
||||||
expires,
|
}
|
||||||
nonce: Some(nonce),
|
}
|
||||||
needs_pw: false,
|
}
|
||||||
..
|
|
||||||
} => Ok(Self {
|
#[derive(Debug)]
|
||||||
data,
|
enum PasteCompleteConstructionError {
|
||||||
key,
|
StageOneFailure,
|
||||||
expires,
|
StageTwoFailure,
|
||||||
nonce,
|
}
|
||||||
password: None,
|
|
||||||
}),
|
impl std::error::Error for PasteCompleteConstructionError {}
|
||||||
_ => bail!("missing field"),
|
|
||||||
|
impl Display for PasteCompleteConstructionError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PasteCompleteConstructionError::StageOneFailure => {
|
||||||
|
write!(f, "Failed to decrypt stage one.")
|
||||||
|
}
|
||||||
|
PasteCompleteConstructionError::StageTwoFailure => {
|
||||||
|
write!(f, "Failed to decrypt stage two.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PasteComplete {
|
impl PasteComplete {
|
||||||
fn new(
|
fn new(
|
||||||
data: Bytes,
|
parent: ComponentLink<Paste>,
|
||||||
|
decrypted: DecryptedData,
|
||||||
expires: Option<Expiration>,
|
expires: Option<Expiration>,
|
||||||
key: Key,
|
|
||||||
nonce: Nonce,
|
|
||||||
password: Option<Key>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data,
|
parent,
|
||||||
|
decrypted,
|
||||||
expires,
|
expires,
|
||||||
key,
|
|
||||||
nonce,
|
|
||||||
password,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Html {
|
fn view(&self) -> Html {
|
||||||
let stage_one = self.password.map_or_else(
|
match &self.decrypted {
|
||||||
|| self.data.to_vec(),
|
DecryptedData::String(decrypted) => html! {
|
||||||
|password| open(&self.data, &self.nonce.increment(), &password).unwrap(),
|
html! {
|
||||||
);
|
<>
|
||||||
let decrypted = open(&stage_one, &self.nonce, &self.key).unwrap();
|
<pre class={"paste"}>
|
||||||
|
<header class={"hljs"}>
|
||||||
|
{
|
||||||
|
self.expires.as_ref().map(ToString::to_string).unwrap_or_else(||
|
||||||
|
"This paste will not expire.".to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</header>
|
||||||
|
<hr class={"hljs"} />
|
||||||
|
<code>{decrypted}</code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
if let Ok(str) = String::from_utf8(decrypted) {
|
<script>{"
|
||||||
html! {
|
hljs.highlightAll();
|
||||||
<>
|
hljs.initLineNumbersOnLoad();
|
||||||
<pre class={"paste"}>
|
"}</script>
|
||||||
<header class={"hljs"}>
|
</>
|
||||||
{
|
|
||||||
self.expires.as_ref().map(ToString::to_string).unwrap_or_else(||
|
|
||||||
"This paste will not expire.".to_string()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
</header>
|
},
|
||||||
<hr class={"hljs"} />
|
DecryptedData::Blob(decrypted) => {
|
||||||
<code>{str}</code>
|
let object_url = Url::create_object_url_with_blob(decrypted);
|
||||||
</pre>
|
if let Ok(object_url) = object_url {
|
||||||
|
let file_name = window().location().pathname().unwrap_or("file".to_string());
|
||||||
<script>{"
|
let mut cloned = self.clone();
|
||||||
hljs.highlightAll();
|
let decrypted_cloned = decrypted.clone();
|
||||||
hljs.initLineNumbersOnLoad();
|
let display_anyways_callback =
|
||||||
"}</script>
|
self.parent.callback_future_once(|_| async move {
|
||||||
</>
|
let array_buffer: ArrayBuffer =
|
||||||
|
JsFuture::from(decrypted_cloned.array_buffer())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into()
|
||||||
|
.unwrap();
|
||||||
|
let decoder = TextDecoder::new().unwrap();
|
||||||
|
cloned.decrypted = decoder
|
||||||
|
.decode_with_buffer_source(&array_buffer)
|
||||||
|
.map(DecryptedData::String)
|
||||||
|
.unwrap();
|
||||||
|
Box::new(cloned) as Box<dyn PasteState>
|
||||||
|
});
|
||||||
|
html! {
|
||||||
|
<section class="hljs centered">
|
||||||
|
<div class="centered">
|
||||||
|
<p>{ "Found a binary file." }</p>
|
||||||
|
<a href={object_url} download=file_name class="hljs-meta">{"Download"}</a>
|
||||||
|
</div>
|
||||||
|
<p onclick=display_anyways_callback class="display-anyways hljs-meta">{ "Display anyways?" }</p>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This branch really shouldn't happen, but might as well
|
||||||
|
// try and give a user-friendly error message.
|
||||||
|
html! {
|
||||||
|
<section class="hljs centered">
|
||||||
|
<p>{ "Failed to create an object URL for the decrypted file. Try reloading the page?" }</p>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DecryptedData::Image(decrypted) => {
|
||||||
|
let object_url = Url::create_object_url_with_blob(decrypted);
|
||||||
|
if let Ok(object_url) = object_url {
|
||||||
|
let file_name = window().location().pathname().unwrap_or("file".to_string());
|
||||||
|
html! {
|
||||||
|
<section class="centered">
|
||||||
|
<img src={object_url.clone()} />
|
||||||
|
<a href={object_url} download=file_name class="hljs-meta">{"Download"}</a>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This branch really shouldn't happen, but might as well
|
||||||
|
// try and give a user-friendly error message.
|
||||||
|
html! {
|
||||||
|
<section class="hljs centered">
|
||||||
|
<p>{ "Failed to create an object URL for the decrypted file. Try reloading the page?" }</p>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
html! { "binary" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
73
web/src/main.scss
Normal file
73
web/src/main.scss
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: "Mplus Code";
|
||||||
|
src: url("./MplusCodeLatin[wdth,wght].ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #404040;
|
||||||
|
font-family: 'Mplus Code', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre header {
|
||||||
|
user-select: none;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paste {
|
||||||
|
border-radius: 1em;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: #0d1117;
|
||||||
|
box-shadow: 0 0 1em black;
|
||||||
|
min-width: 120ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
font-family: 'Mplus Code', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-ln td.hljs-ln-numbers {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-anyways {
|
||||||
|
margin-bottom: 4em;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-bottom: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
background-color: #0d1117;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
a {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue