Add upload image

This commit is contained in:
Adrian Wozniak 2020-05-05 08:33:40 +02:00
parent c20cc9ebc5
commit 5b871a3332
19 changed files with 884 additions and 52 deletions

562
Cargo.lock generated
View File

@ -71,6 +71,26 @@ dependencies = [
"futures 0.3.4",
]
[[package]]
name = "actix-files"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301482841d3d74483a446ead63cb7d362e187d2c8b603f13d91995621ea53c46"
dependencies = [
"actix-http",
"actix-service",
"actix-web",
"bitflags",
"bytes",
"derive_more",
"futures 0.3.4",
"log 0.4.8",
"mime",
"mime_guess",
"percent-encoding 2.1.0",
"v_htmlescape",
]
[[package]]
name = "actix-http"
version = "1.0.1"
@ -115,7 +135,7 @@ dependencies = [
"serde_urlencoded",
"sha1",
"slab",
"time",
"time 0.1.42",
]
[[package]]
@ -143,7 +163,7 @@ dependencies = [
"httparse",
"log 0.4.8",
"mime",
"time",
"time 0.1.42",
"twoway",
]
@ -298,7 +318,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"time",
"time 0.1.42",
"url 2.1.1",
]
@ -378,6 +398,18 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "ascii_utils"
version = "0.9.3"
@ -463,6 +495,12 @@ dependencies = [
"libc",
]
[[package]]
name = "base-x"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
[[package]]
name = "base64"
version = "0.9.3"
@ -488,6 +526,12 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "base64"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"
[[package]]
name = "bigdecimal"
version = "0.1.0"
@ -515,6 +559,17 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
@ -616,7 +671,7 @@ dependencies = [
"num-integer",
"num-traits",
"serde",
"time",
"time 0.1.42",
]
[[package]]
@ -670,13 +725,19 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
dependencies = [
"time",
"time 0.1.42",
"url 1.7.2",
]
@ -732,6 +793,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "dbg"
version = "1.0.4"
@ -771,7 +842,7 @@ dependencies = [
"pq-sys",
"r2d2",
"serde_json",
"time",
"time 0.1.42",
"uuid 0.6.5",
"uuid 0.8.1",
]
@ -796,6 +867,34 @@ dependencies = [
"generic-array",
]
[[package]]
name = "dirs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
"cfg-if",
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
dependencies = [
"cfg-if",
"libc",
"redox_users",
"winapi 0.3.8",
]
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "dotenv"
version = "0.15.0"
@ -825,7 +924,7 @@ dependencies = [
"encoding",
"lazy_static",
"rand 0.4.6",
"time",
"time 0.1.42",
"version_check 0.1.5",
]
@ -1261,6 +1360,22 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
dependencies = [
"crypto-mac",
"digest",
]
[[package]]
name = "hostname"
version = "0.1.5"
@ -1293,6 +1408,16 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-body"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "httparse"
version = "1.3.4"
@ -1308,6 +1433,43 @@ dependencies = [
"quick-error",
]
[[package]]
name = "hyper"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"itoa",
"log 0.4.8",
"net2",
"pin-project",
"time 0.1.42",
"tokio",
"tower-service",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-tls",
]
[[package]]
name = "idna"
version = "0.1.5"
@ -1425,6 +1587,7 @@ version = "0.1.0"
dependencies = [
"actix",
"actix-cors",
"actix-files",
"actix-multipart",
"actix-rt",
"actix-service",
@ -1453,9 +1616,11 @@ dependencies = [
"pretty_env_logger",
"quickcheck",
"r2d2",
"rusoto_core",
"rusoto_s3",
"serde",
"serde_json",
"time",
"time 0.1.42",
"toml",
"url 2.1.1",
"uuid 0.8.1",
@ -1545,7 +1710,7 @@ dependencies = [
"email",
"lettre",
"mime",
"time",
"time 0.1.42",
"uuid 0.7.4",
]
@ -1621,6 +1786,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "memchr"
version = "0.1.11"
@ -1642,6 +1813,16 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.3.6"
@ -1664,7 +1845,7 @@ dependencies = [
"kernel32-sys",
"libc",
"log 0.4.8",
"miow",
"miow 0.2.1",
"net2",
"slab",
"winapi 0.2.8",
@ -1682,6 +1863,18 @@ dependencies = [
"slab",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
dependencies = [
"log 0.4.8",
"mio",
"miow 0.3.3",
"winapi 0.3.8",
]
[[package]]
name = "mio-uds"
version = "0.6.7"
@ -1705,6 +1898,16 @@ dependencies = [
"ws2_32-sys",
]
[[package]]
name = "miow"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
dependencies = [
"socket2",
"winapi 0.3.8",
]
[[package]]
name = "native-tls"
version = "0.2.4"
@ -2238,6 +2441,17 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "redox_users"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
dependencies = [
"getrandom",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "regex"
version = "0.1.80"
@ -2294,12 +2508,120 @@ dependencies = [
"quick-error",
]
[[package]]
name = "rusoto_core"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8d624cb48fcaca612329e4dd544380aa329ef338e83d3a90f5b7897e631971"
dependencies = [
"async-trait",
"base64 0.12.0",
"bytes",
"futures 0.3.4",
"hmac",
"http",
"hyper",
"hyper-tls",
"lazy_static",
"log 0.4.8",
"md5",
"percent-encoding 2.1.0",
"pin-project",
"rusoto_credential",
"rusoto_signature",
"rustc_version",
"serde",
"serde_json",
"sha2",
"tokio",
"xml-rs",
]
[[package]]
name = "rusoto_credential"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3e7cdf483d7198d9bca7414746d3ba656239e89e467b715d0571912f0b492f"
dependencies = [
"async-trait",
"chrono",
"dirs",
"futures 0.3.4",
"hyper",
"pin-project",
"regex 1.3.6",
"serde",
"serde_json",
"shlex",
"tokio",
"zeroize",
]
[[package]]
name = "rusoto_s3"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b6bc3221ae5a2c036d5757eee68a2ffb6b7f87b8a83adbf4271c8133fdee01c"
dependencies = [
"async-trait",
"bytes",
"futures 0.3.4",
"rusoto_core",
"xml-rs",
]
[[package]]
name = "rusoto_signature"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62940a2bd479900a1bf8935b8f254d3e19368ac3ac4570eb4bd48eb46551a1b7"
dependencies = [
"base64 0.12.0",
"bytes",
"futures 0.3.4",
"hex",
"hmac",
"http",
"hyper",
"log 0.4.8",
"md5",
"percent-encoding 2.1.0",
"pin-project",
"rusoto_credential",
"rustc_version",
"serde",
"sha2",
"time 0.2.15",
"tokio",
]
[[package]]
name = "rust-argon2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
dependencies = [
"base64 0.11.0",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.3"
@ -2398,6 +2720,21 @@ dependencies = [
"web-sys",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.105"
@ -2459,6 +2796,24 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
[[package]]
name = "sha2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "shlex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]]
name = "signal-hook-registry"
version = "1.2.0"
@ -2493,12 +2848,73 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "standback"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e4b8c631c998468961a9ea159f064c5c8499b95b5e4a34b77849d45949d540"
[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
dependencies = [
"discard",
"rustc_version",
"stdweb-derive",
"stdweb-internal-macros",
"stdweb-internal-runtime",
"wasm-bindgen",
]
[[package]]
name = "stdweb-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2",
"quote",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "stdweb-internal-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2",
"quote",
"serde",
"serde_derive",
"serde_json",
"sha1",
"syn",
]
[[package]]
name = "stdweb-internal-runtime"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "syn"
version = "1.0.17"
@ -2602,6 +3018,44 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "time"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1330829d2e6c06771eeae476be12ff1aa9eb5e29ca718a431ab27168efde6c1"
dependencies = [
"cfg-if",
"libc",
"standback",
"stdweb",
"time-macros",
"version_check 0.9.1",
"winapi 0.3.8",
]
[[package]]
name = "time-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d"
dependencies = [
"proc-macro-hack",
"time-macros-impl",
]
[[package]]
name = "time-macros-impl"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"standback",
"syn",
]
[[package]]
name = "tokio"
version = "0.2.13"
@ -2616,13 +3070,36 @@ dependencies = [
"libc",
"memchr 2.3.3",
"mio",
"mio-named-pipes",
"mio-uds",
"pin-project-lite",
"signal-hook-registry",
"slab",
"tokio-macros",
"winapi 0.3.8",
]
[[package]]
name = "tokio-macros"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.2.0"
@ -2646,6 +3123,12 @@ dependencies = [
"serde",
]
[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
[[package]]
name = "trust-dns-proto"
version = "0.18.0-alpha.2"
@ -2685,6 +3168,12 @@ dependencies = [
"trust-dns-proto",
]
[[package]]
name = "try-lock"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
[[package]]
name = "twoway"
version = "0.2.1"
@ -2827,6 +3316,37 @@ dependencies = [
"sha1",
]
[[package]]
name = "v_escape"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "660b101c07b5d0863deb9e7fb3138777e858d6d2a79f9e6049a27d1cc77c6da6"
dependencies = [
"v_escape_derive",
]
[[package]]
name = "v_escape_derive"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae"
dependencies = [
"nom",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "v_htmlescape"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41"
dependencies = [
"cfg-if",
"v_escape",
]
[[package]]
name = "vcpkg"
version = "0.2.8"
@ -2862,6 +3382,16 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log 0.4.8",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
@ -3046,3 +3576,15 @@ dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "xml-rs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
[[package]]
name = "zeroize"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8"

View File

@ -50,6 +50,11 @@ features = [
"BinaryType",
"Blob",
"AddEventListenerOptions",
"File",
"FileList",
"FormData",
"FileReader",
"FileReaderSync",
# events
"EventTarget",
"ErrorEvent",

View File

@ -0,0 +1,14 @@
.styledImageInput {
}
.styledImageInput > .label > .mask {
display: block;
width: 120px;
height: 120px;
margin: 0 auto;
border-radius: 60px;
}
.styledImageInput > .input {
display: none;
}

View File

@ -13,6 +13,7 @@
@import "./css/styledSelectChild.css";
@import "./css/styledButton.css";
@import "./css/styledInput.css";
@import "./css/styledImageInput.css";
@import "./css/styledModal.css";
@import "./css/styledTextArea.css";
@import "./css/styledForm.css";

View File

@ -1,6 +1,7 @@
use std::sync::RwLock;
use seed::{prelude::*, *};
use web_sys::File;
use jirs_data::*;
@ -128,11 +129,13 @@ impl std::fmt::Display for FieldId {
UsersFieldId::Username => f.write_str("users-username"),
UsersFieldId::Email => f.write_str("users-email"),
UsersFieldId::UserRole => f.write_str("users-userRole"),
UsersFieldId::Avatar => f.write_str("users-avatar"),
},
FieldId::Profile(sub) => match sub {
UsersFieldId::Username => f.write_str("profile-username"),
UsersFieldId::Email => f.write_str("profile-email"),
UsersFieldId::UserRole => f.write_str("profile-userRole"),
UsersFieldId::Avatar => f.write_str("profile-avatar"),
},
}
}
@ -169,7 +172,7 @@ pub enum PageChanged {
Profile(ProfilePageChange),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub enum Msg {
NoOp,
GlobalKeyDown {
@ -179,6 +182,11 @@ pub enum Msg {
alt: bool,
},
PageChanged(PageChanged),
ChangePage(model::Page),
StyledSelectChanged(FieldId, StyledSelectChange),
InternalFailure(String),
ToggleAboutTooltip,
// Auth Token
AuthTokenStored,
@ -195,12 +203,6 @@ pub enum Msg {
// sign up
SignUpRequest,
StyledSelectChanged(FieldId, StyledSelectChange),
ChangePage(model::Page),
InternalFailure(String),
ToggleAboutTooltip,
// project
ProjectAvatarFilterChanged(UserId, AvatarFilterActive),
ProjectToggleOnlyMy,
@ -219,6 +221,7 @@ pub enum Msg {
// inputs
StrInputChanged(FieldId, String),
U32InputChanged(FieldId, u32),
FileInputChanged(FieldId, Vec<File>),
// issues
AddIssue,
@ -228,6 +231,9 @@ pub enum Msg {
SaveComment,
DeleteComment(CommentId),
// profile
AvatarUpdateFetched(seed::fetch::FetchObject<String>),
// modals
ModalOpened(Box<ModalType>),
ModalDropped,
@ -237,9 +243,10 @@ pub enum Msg {
}
fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
if msg == Msg::NoOp {
return;
}
match msg {
Msg::NoOp => return,
_ => (),
};
if cfg!(debug_assertions) {
log!(msg);
}

View File

@ -8,10 +8,10 @@ use crate::shared::styled_field::StyledField;
use crate::shared::styled_input::{StyledInput, StyledInputState};
use crate::shared::styled_modal::StyledModal;
use crate::shared::styled_select::{StyledSelect, StyledSelectState};
use crate::shared::styled_select_child::*;
use crate::shared::tracking_widget::{fibonacci_values, tracking_widget};
use crate::shared::{find_issue, ToChild, ToNode};
use crate::{EditIssueModalSection, FieldId, Msg};
// use crate::shared::styled_select_child::*;
use crate::shared::tracking_widget::{fibonacci_values, tracking_widget};
pub fn value_for_time_tracking(v: &Option<i32>, time_tracking_type: &TimeTracking) -> String {
match (time_tracking_type, v.as_ref()) {

View File

@ -8,6 +8,7 @@ use jirs_data::*;
use crate::modal::time_tracking::value_for_time_tracking;
use crate::shared::styled_checkbox::StyledCheckboxState;
use crate::shared::styled_editor::Mode;
use crate::shared::styled_image_input::StyledImageInputState;
use crate::shared::styled_input::StyledInputState;
use crate::shared::styled_select::StyledSelectState;
use crate::{EditIssueModalSection, FieldId, ProjectFieldId, HOST_URL};
@ -364,6 +365,7 @@ impl Default for UsersPage {
pub struct ProfilePage {
pub name: StyledInputState,
pub email: StyledInputState,
pub avatar: StyledImageInputState,
}
impl ProfilePage {
@ -377,6 +379,10 @@ impl ProfilePage {
FieldId::Profile(UsersFieldId::Email),
user.email.as_str(),
),
avatar: StyledImageInputState::new(
FieldId::Profile(UsersFieldId::Avatar),
user.avatar_url.as_ref().cloned(),
),
}
}
}

View File

@ -1,4 +1,5 @@
use seed::{prelude::*, *};
use web_sys::FormData;
use jirs_data::*;
@ -6,11 +7,12 @@ use crate::api::send_ws_msg;
use crate::model::{Model, Page, PageContent, ProfilePage};
use crate::shared::styled_field::StyledField;
use crate::shared::styled_form::StyledForm;
use crate::shared::styled_image_input::StyledImageInput;
use crate::shared::styled_input::StyledInput;
use crate::shared::{inner_layout, ToNode};
use crate::{FieldId, Msg};
use crate::{FieldId, Msg, HOST_URL};
pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orders<Msg>) {
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
let user = match model.user {
Some(ref user) => user,
_ => return,
@ -31,6 +33,27 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, _orders: &mut impl Orde
project_page.name.update(&msg);
project_page.email.update(&msg);
project_page.avatar.update(&msg);
match msg {
Msg::FileInputChanged(FieldId::Profile(UsersFieldId::Avatar), ..) => {
let file = match project_page.avatar.file.as_ref() {
Some(f) => f,
_ => return,
};
let token = match crate::shared::read_auth_token() {
Ok(uuid) => uuid,
_ => return,
};
let fd = FormData::new().unwrap();
fd.set_with_str("token", format!("{}", token).as_str())
.unwrap();
fd.set_with_blob("avatar", file).unwrap();
orders.perform_cmd(update_avatar(fd));
orders.skip();
}
_ => (),
}
}
pub fn view(model: &Model) -> Node<Msg> {
@ -39,6 +62,12 @@ pub fn view(model: &Model) -> Node<Msg> {
_ => return empty![],
};
let avatar = StyledImageInput::build(FieldId::Profile(UsersFieldId::Avatar))
.add_class("avatar")
.state(&page.avatar)
.build()
.into_node();
let username = StyledInput::build(FieldId::Profile(UsersFieldId::Username))
.state(&page.name)
.valid(true)
@ -65,9 +94,20 @@ pub fn view(model: &Model) -> Node<Msg> {
let content = StyledForm::build()
.heading("Profile")
.add_field(avatar)
.add_field(username_field)
.add_field(email_field)
.build()
.into_node();
inner_layout(model, "profile", vec![content], empty![])
}
async fn update_avatar(data: FormData) -> Result<Msg, Msg> {
let host_url = unsafe { HOST_URL.clone() };
let path = format!("{}/avatar/", host_url);
Request::new(path)
.method(Method::Post)
.body(data.into())
.fetch_string(Msg::AvatarUpdateFetched)
.await
}

View File

@ -14,9 +14,9 @@ pub mod styled_checkbox;
pub mod styled_confirm_modal;
pub mod styled_editor;
pub mod styled_field;
pub mod styled_file_input;
pub mod styled_form;
pub mod styled_icon;
pub mod styled_image_input;
pub mod styled_input;
pub mod styled_link;
pub mod styled_modal;

View File

@ -0,0 +1,127 @@
use seed::{prelude::*, *};
use web_sys::File;
use crate::shared::ToNode;
use crate::{FieldId, Msg};
#[derive(Debug, Clone)]
pub struct StyledImageInputState {
field_id: FieldId,
pub file: Option<File>,
pub url: Option<String>,
}
impl StyledImageInputState {
pub fn new(field_id: FieldId, url: Option<String>) -> Self {
Self {
field_id,
file: None,
url,
}
}
pub fn update(&mut self, msg: &Msg) {
match msg {
Msg::FileInputChanged(field_id, files) if field_id == &self.field_id => {
self.file = files.get(0).cloned();
}
_ => return,
}
}
}
pub struct StyledImageInput {
id: FieldId,
class_list: Vec<String>,
url: Option<String>,
}
impl StyledImageInput {
pub fn build(field_id: FieldId) -> StyledInputInputBuilder {
StyledInputInputBuilder {
id: field_id,
class_list: vec![],
url: None,
}
}
}
impl ToNode for StyledImageInput {
fn into_node(self) -> Node<Msg> {
render(self)
}
}
pub struct StyledInputInputBuilder {
id: FieldId,
class_list: Vec<String>,
url: Option<String>,
}
impl StyledInputInputBuilder {
pub fn add_class<S>(mut self, name: S) -> Self
where
S: Into<String>,
{
self.class_list.push(name.into());
self
}
pub fn state(mut self, state: &StyledImageInputState) -> Self {
self.url = state.url.as_ref().cloned();
self
}
pub fn build(self) -> StyledImageInput {
StyledImageInput {
id: self.id,
class_list: self.class_list,
url: self.url,
}
}
}
fn render(values: StyledImageInput) -> Node<Msg> {
let StyledImageInput {
id,
class_list,
url,
} = values;
let field_id = id.clone();
let on_change = ev(Ev::Change, move |ev| {
let target = ev.target().unwrap();
let input = seed::to_input(&target);
let v = input
.files()
.map(|list| {
let mut v = vec![];
for i in 0..list.length() {
v.push(list.get(i).unwrap());
}
v
})
.unwrap_or_default();
Msg::FileInputChanged(field_id, v)
});
let input_id = id.to_string();
div![
class!["styledImageInput"],
attrs![At::Class => class_list.join(" ")],
label![
class!["label"],
attrs![At::For => input_id],
img![
class!["mask"],
attrs![At::Src => url.unwrap_or_default()],
" "
]
],
input![
class!["input"],
attrs![At::Type => "file", At::Id => input_id],
on_change
]
]
}

View File

@ -22,10 +22,13 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
return;
}
if msg == Msg::ChangePage(Page::SignIn) {
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
return;
}
match msg {
Msg::ChangePage(Page::SignIn) => {
model.page_content = PageContent::SignIn(Box::new(SignInPage::default()));
return;
}
_ => (),
};
let page = match &mut model.page_content {
PageContent::SignIn(page) => page,

View File

@ -19,10 +19,13 @@ pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>
return;
}
if msg == Msg::ChangePage(Page::SignUp) {
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
return;
}
match msg {
Msg::ChangePage(Page::SignUp) => {
model.page_content = PageContent::SignUp(Box::new(SignUpPage::default()));
return;
}
_ => (),
};
let page = match &mut model.page_content {
PageContent::SignUp(page) => page,

View File

@ -673,6 +673,7 @@ pub enum UsersFieldId {
Username,
Email,
UserRole,
Avatar,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialOrd, PartialEq, Hash)]

View File

@ -21,6 +21,7 @@ actix-service = { version = "*" }
actix-rt = "1"
actix-web-actors = "*"
actix-multipart = { version = "*" }
actix-files = { version = "0.2.1" }
dotenv = { version = "*" }
byteorder = "1.0"
@ -29,7 +30,7 @@ libc = { version = "0.2.0" }
pq-sys = { version = ">=0.3.0, <0.5.0" }
quickcheck = { version = "0.4" }
serde_json = { version = ">=0.8.0, <2.0" }
toml = "0.5"
toml = "0.5.6"
bincode = "1.2.1"
time = { version = "0.1" }
url = { version = "2.1.0" }
@ -49,6 +50,10 @@ futures = { version = "*" }
lettre = { version = "*" }
lettre_email = { version = "*" }
# Amazon S3
rusoto_s3 = "0.43.0"
rusoto_core = "0.43.0"
[dependencies.diesel]
version = "1.4.4"
features = [ "unstable", "postgres", "numeric", "extras", "uuidv07" ]

View File

@ -7,6 +7,7 @@ extern crate log;
use actix::Actor;
use actix_cors::Cors;
use actix_files as fs;
use actix_web::{App, HttpServer};
use crate::ws::WsServer;
@ -40,7 +41,8 @@ async fn main() -> Result<(), String> {
let ws_server = WsServer::default().start();
HttpServer::new(move || {
App::new()
let web_config = web::Configuration::read();
let mut app = App::new()
.wrap(actix_web::middleware::Logger::default())
.wrap(Cors::default())
.data(ws_server.clone())
@ -48,6 +50,14 @@ async fn main() -> Result<(), String> {
.data(mail_addr.clone())
.data(crate::db::build_pool())
.service(crate::ws::index)
.service(actix_web::web::scope("/avatar").service(crate::web::avatar::upload));
if let Some(file_system) = web_config.filesystem.as_ref() {
app = app.service(fs::Files::new(
file_system.client_path.as_str(),
file_system.store_path.as_str(),
));
}
app
})
.workers(web_config.concurrency)
.bind(web_config.addr())

View File

@ -1,28 +1,67 @@
use actix_multipart::Multipart;
use actix_web::{get, post, web, Error, HttpResponse, Responder};
use futures::{StreamExt, TryStreamExt};
use std::io::Write;
use actix::Addr;
use actix_multipart::{Field, Multipart};
use actix_web::http::header::ContentDisposition;
use actix_web::web::Data;
use actix_web::{get, post, web, Error, HttpResponse, Responder};
use futures::{StreamExt, TryStreamExt};
use crate::db::DbExecutor;
#[post("/")]
async fn upload(mut payload: Multipart) -> Result<HttpResponse, Error> {
while let Ok(Some(mut field)) = payload.try_next().await {
let content_type = field.content_disposition().unwrap();
let filename = content_type.get_filename().unwrap();
let filepath = format!("./tmp/{}", filename);
// File::create is blocking operation, use threadpool
let mut f = web::block(|| std::fs::File::create(filepath))
.await
.unwrap();
// Field in turn is stream of *Bytes* object
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
// filesystem operations are blocking, we have to use threadpool
f = web::block(move || f.write_all(&data).map(|_| f)).await?;
pub async fn upload(
mut payload: Multipart,
db: Data<Addr<DbExecutor>>,
) -> Result<HttpResponse, Error> {
while let Ok(Some(field)) = payload.try_next().await {
let disposition: ContentDisposition = match field.content_disposition() {
Some(d) => d,
_ => continue,
};
if !disposition.is_form_data() {
return Ok(HttpResponse::BadRequest().finish());
}
let _name = disposition.get_name().as_ref().cloned().unwrap_or_default();
match disposition.get_name() {
Some("token") => handle_token(field, disposition, db.clone()).await?,
Some("avatar") => handle_image(field, disposition, db.clone()).await?,
_ => continue,
};
}
Ok(HttpResponse::Ok().json(""))
}
async fn handle_token(
mut field: Field,
_disposition: ContentDisposition,
_db: Data<Addr<DbExecutor>>,
) -> Result<(), Error> {
Ok(())
}
async fn handle_image(
mut field: Field,
disposition: ContentDisposition,
_db: Data<Addr<DbExecutor>>,
) -> Result<(), Error> {
let web_config = crate::web::Configuration::read();
let filename = disposition.get_filename().unwrap();
let filepath = format!("./tmp/{}", filename);
// File::create is blocking operation, use threadpool
let mut f = web::block(|| std::fs::File::create(filepath))
.await
.unwrap();
// Field in turn is stream of *Bytes* object
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
// filesystem operations are blocking, we have to use thread pool
f = web::block(move || f.write_all(&data).map(|_| f)).await?;
}
Ok(())
}
#[get("/{id}")]
async fn download(_id: web::Path<i32>) -> impl Responder {
HttpResponse::Ok().json("")

View File

@ -40,12 +40,35 @@ pub enum Protocol {
Https,
}
#[derive(Serialize, Deserialize)]
pub struct FileSystemStorage {
pub store_path: String,
pub client_path: String,
}
#[derive(Serialize, Deserialize)]
pub struct AmazonS3Storage {
pub access_key_id: String,
pub secret_access_key: String,
pub bucket: String,
pub region: String,
}
#[derive(Serialize, Deserialize)]
pub enum Storage {
FileSystem,
AmazonS3,
}
#[derive(Serialize, Deserialize)]
pub struct Configuration {
pub concurrency: usize,
pub port: String,
pub bind: String,
pub ssl: bool,
pub storage: Storage,
pub filesystem: Option<FileSystemStorage>,
pub s3: Option<AmazonS3Storage>,
}
impl Default for Configuration {
@ -55,6 +78,12 @@ impl Default for Configuration {
port: "5000".to_string(),
bind: "0.0.0.0".to_string(),
ssl: false,
storage: Storage::FileSystem,
filesystem: Some(FileSystemStorage {
store_path: "./tmp".to_string(),
client_path: "/img".to_string(),
}),
s3: None,
}
}
}

0
tmp/.gitkeep Normal file
View File