diff --git a/Cargo.lock b/Cargo.lock index 61881398ee44d31b94580aef26bf877a8b22e407..14f438db6a9a22bccba7aca373539dca447603e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "actix-codec" version = "0.3.0" @@ -31,8 +41,8 @@ dependencies = [ "futures-util", "http", "log", - "rustls", - "tokio-rustls", + "rustls 0.18.1", + "tokio-rustls 0.14.1", "trust-dns-proto", "trust-dns-resolver", "webpki", @@ -96,14 +106,14 @@ dependencies = [ "mime", "percent-encoding", "pin-project 1.0.8", - "rand", + "rand 0.7.3", "regex", "serde", "serde_json", "serde_urlencoded", "sha-1", "slab", - "time", + "time 0.2.27", ] [[package]] @@ -213,10 +223,10 @@ dependencies = [ "actix-service", "actix-utils", "futures-util", - "rustls", - "tokio-rustls", + "rustls 0.18.1", + "tokio-rustls 0.14.1", "webpki", - "webpki-roots", + "webpki-roots 0.20.0", ] [[package]] @@ -269,12 +279,12 @@ dependencies = [ "mime", "pin-project 1.0.8", "regex", - "rustls", + "rustls 0.18.1", "serde", "serde_json", "serde_urlencoded", "socket2", - "time", + "time 0.2.27", "tinyvec", "url", ] @@ -301,12 +311,33 @@ dependencies = [ "futures", ] +[[package]] +name = "addr2line" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "opaque-debug", +] + [[package]] name = "aho-corasick" version = "0.7.15" @@ -362,19 +393,40 @@ dependencies = [ "log", "mime", "percent-encoding", - "rand", - "rustls", + "rand 0.7.3", + "rustls 0.18.1", "serde", "serde_json", "serde_urlencoded", ] +[[package]] +name = "backtrace" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base-x" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.12.3" @@ -399,9 +451,26 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ + "block-padding", "generic-array", ] +[[package]] +name = "block-modes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" +dependencies = [ + "block-padding", + "cipher", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "brotli-sys" version = "0.3.2" @@ -422,6 +491,23 @@ dependencies = [ "libc", ] +[[package]] +name = "bson" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0aa578035b938855a710ba58d43cfb4d435f3619f99236fb35922a574d6cb1" +dependencies = [ + "base64 0.13.0", + "chrono", + "hex", + "lazy_static", + "linked-hash-map", + "rand 0.7.3", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "buf-min" version = "0.4.0" @@ -482,6 +568,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.43", + "winapi 0.3.9", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "const_fn" version = "0.4.8" @@ -501,7 +609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "percent-encoding", - "time", + "time 0.2.27", "version_check 0.9.3", ] @@ -520,6 +628,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc-any" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "073375684a58dece169afbdc9879a027f3698118ad3814938316c6002b7aa921" +dependencies = [ + "debug-helper", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -529,6 +646,103 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +dependencies = [ + "darling_core 0.13.0", + "darling_macro 0.13.0", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core 0.10.2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +dependencies = [ + "darling_core 0.13.0", + "quote", + "syn", +] + +[[package]] +name = "debug-helper" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76fbd10dce159c002b9c688ae8ab7cd531151e185e0ad360f4bfea3b0eede3a8" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" version = "0.99.16" @@ -542,6 +756,17 @@ dependencies = [ "syn", ] +[[package]] +name = "des" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac41dd49fb554432020d52c875fc290e110113f864c6b1b525cd62c7e7747a5d" +dependencies = [ + "byteorder", + "cipher", + "opaque-debug", +] + [[package]] name = "digest" version = "0.9.0" @@ -603,6 +828,20 @@ dependencies = [ "termcolor", ] +[[package]] +name = "err-derive" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22deed3a8124cff5fa835713fa105621e43bbdc46690c3a6b68328a012d350d4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "rustversion", + "syn", + "synstructure", +] + [[package]] name = "flate2" version = "1.0.22" @@ -768,9 +1007,26 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" + [[package]] name = "h2" version = "0.2.7" @@ -815,6 +1071,22 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest", +] + [[package]] name = "hostname" version = "0.3.1" @@ -837,18 +1109,80 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http", +] + [[package]] name = "httparse" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project 1.0.8", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" +dependencies = [ + "bytes 0.5.6", + "futures-util", + "hyper", + "log", + "rustls 0.18.1", + "tokio", + "tokio-rustls 0.14.1", + "webpki", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -897,9 +1231,15 @@ dependencies = [ "socket2", "widestring", "winapi 0.3.9", - "winreg", + "winreg 0.6.2", ] +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + [[package]] name = "itoa" version = "0.4.8" @@ -932,9 +1272,17 @@ dependencies = [ "actix-files", "actix-web", "actix-web-middleware-redirect-https", + "chrono", "dotenv", "env_logger", - "rustls", + "futures", + "magic-crypt", + "rand 0.8.4", + "rustls 0.18.1", + "serde", + "serde_json", + "tokio", + "wither", ] [[package]] @@ -988,6 +1336,23 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "magic-crypt" +version = "3.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f89f3c9a23ba052e4fc602770944c7ef16096ade1ca110a5c722efb16da7395" +dependencies = [ + "aes", + "base64 0.13.0", + "block-modes", + "crc-any", + "des", + "digest", + "md-5", + "sha2", + "tiger", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1000,6 +1365,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + [[package]] name = "memchr" version = "2.3.4" @@ -1045,12 +1421,24 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.2", "net2", "slab", "winapi 0.2.8", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.7", + "winapi 0.3.9", +] + [[package]] name = "mio-uds" version = "0.6.8" @@ -1075,12 +1463,66 @@ dependencies = [ ] [[package]] -name = "net2" -version = "0.2.37" +name = "miow" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "cfg-if 0.1.10", + "winapi 0.3.9", +] + +[[package]] +name = "mongodb" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2a1cb6fd58c27e51ee650dca3b6924c4ce533dc0384f05b3219709ea1f1eb6" +dependencies = [ + "async-trait", + "base64 0.11.0", + "bitflags", + "bson", + "chrono", + "derivative", + "err-derive", + "futures", + "hex", + "hmac", + "lazy_static", + "md-5", + "os_info", + "pbkdf2", + "percent-encoding", + "rand 0.7.3", + "reqwest", + "rustls 0.17.0", + "serde", + "serde_bytes", + "serde_with", + "sha-1", + "sha2", + "socket2", + "stringprep", + "strsim 0.10.0", + "take_mut", + "time 0.1.43", + "tokio", + "tokio-rustls 0.13.1", + "trust-dns-proto", + "trust-dns-resolver", + "typed-builder", + "uuid", + "version_check 0.9.3", + "webpki", + "webpki-roots 0.21.1", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] @@ -1095,6 +1537,25 @@ dependencies = [ "version_check 0.1.5", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -1105,6 +1566,12 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" + [[package]] name = "once_cell" version = "1.8.0" @@ -1117,6 +1584,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "os_info" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac91020bfed8cc3f8aa450d4c3b5fa1d3373fc091c8a92009f3b27749d5a227" +dependencies = [ + "log", + "winapi 0.3.9", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -1142,6 +1619,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1221,6 +1707,30 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check 0.9.3", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check 0.9.3", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1263,11 +1773,24 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", ] [[package]] @@ -1277,7 +1800,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", ] [[package]] @@ -1286,7 +1819,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", ] [[package]] @@ -1295,7 +1837,25 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -1324,6 +1884,43 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "reqwest" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" +dependencies = [ + "base64 0.13.0", + "bytes 0.5.6", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite 0.2.7", + "rustls 0.18.1", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls 0.14.1", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.20.0", + "winreg 0.7.0", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -1349,6 +1946,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.2.3" @@ -1367,6 +1970,19 @@ dependencies = [ "semver 0.11.0", ] +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +dependencies = [ + "base64 0.11.0", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.18.1" @@ -1380,6 +1996,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + [[package]] name = "ryu" version = "1.0.5" @@ -1444,6 +2066,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.130" @@ -1461,6 +2092,7 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" dependencies = [ + "indexmap", "itoa", "ryu", "serde", @@ -1478,6 +2110,29 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +dependencies = [ + "rustversion", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +dependencies = [ + "darling 0.13.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -1497,6 +2152,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1593,6 +2261,34 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.76" @@ -1604,6 +2300,24 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + [[package]] name = "termcolor" version = "1.1.2" @@ -1642,6 +2356,27 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tiger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443e531cbcf9de83258cfef70bcd56c91188de5819ebd4b19c85f589e0617005" +dependencies = [ + "block-buffer", + "byteorder", + "digest", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "time" version = "0.2.27" @@ -1702,19 +2437,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" dependencies = [ "bytes 0.5.6", + "fnv", "futures-core", "iovec", "lazy_static", "libc", "memchr", "mio", + "mio-named-pipes", "mio-uds", + "num_cpus", "pin-project-lite 0.1.12", "signal-hook-registry", "slab", + "tokio-macros", "winapi 0.3.9", ] +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +dependencies = [ + "futures-core", + "rustls 0.17.0", + "tokio", + "webpki", +] + [[package]] name = "tokio-rustls" version = "0.14.1" @@ -1722,7 +2484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" dependencies = [ "futures-core", - "rustls", + "rustls 0.18.1", "tokio", "webpki", ] @@ -1741,6 +2503,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + [[package]] name = "tracing" version = "0.1.28" @@ -1779,13 +2547,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" dependencies = [ "async-trait", + "backtrace", "cfg-if 1.0.0", "enum-as-inner", "futures", "idna", "lazy_static", "log", - "rand", + "rand 0.7.3", "smallvec", "thiserror", "tokio", @@ -1811,6 +2580,23 @@ dependencies = [ "trust-dns-proto", ] +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typed-builder" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc955f27acc7a547f328f52f4a5a568986a31efec2fc6de865279f3995787b9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.14.0" @@ -1877,6 +2663,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", +] + [[package]] name = "v_escape" version = "0.15.0" @@ -1921,12 +2716,28 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.78" @@ -1934,6 +2745,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ "cfg-if 1.0.0", + "serde", + "serde_json", "wasm-bindgen-macro", ] @@ -1952,6 +2765,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.78" @@ -2010,6 +2835,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + [[package]] name = "widestring" version = "0.4.3" @@ -2068,6 +2902,47 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "wither" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e6fce5f641da433789cf4376cd698874666bf2741eb4d72444b4006cc0954a" +dependencies = [ + "async-trait", + "chrono", + "futures", + "log", + "mongodb", + "serde", + "thiserror", + "wither_derive", +] + +[[package]] +name = "wither_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cc57cffdfd2239926b5b9e068591944444c71c8e9baa2bd1cbde3492d78973" +dependencies = [ + "Inflector", + "async-trait", + "darling 0.10.2", + "proc-macro-error", + "proc-macro2", + "quote", + "serde", + "syn", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 2f390d40cc86e14a3e61aab05d42e13997b5cbde..b8a874e5fe4b15c0bbb40d5ba89902411d1770a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,13 @@ actix-web = { version = "3", features=["rustls"] } actix-web-middleware-redirect-https = "3.0.1" rustls="0.18.1" actix-files="0.5" +futures = "0.3.17" +serde = "1" +serde_json="1" +wither="0.9" +magic-crypt="3" env_logger="0.9" +chrono="0.4" +rand="0.8" dotenv="0.15" +tokio = { version = "0.2", features = ["full"] } \ No newline at end of file diff --git a/Makefile b/Makefile index 22fccea0db98748532ab536152d1a77ba167d0db..637aa2cb19cd0a1dd1469d9a330b57a42327df8b 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,17 @@ doc: bash-api: docker exec -it kuadrado_server bash -build-front: +build-website: npm run --prefix ./website build -build-front-debug: +build-website-debug: npm run --prefix ./website build debug +build-admin: + npm run --prefix ./admin-frontend build + +build-admin-debug: + npm run --prefix ./admin-frontend build debug + logs: docker-compose logs -f \ No newline at end of file diff --git a/admin-frontend/build.js b/admin-frontend/build.js new file mode 100644 index 0000000000000000000000000000000000000000..808ab562d63c7cfd73f7ee2f1c9fcfbe8449249d --- /dev/null +++ b/admin-frontend/build.js @@ -0,0 +1,12 @@ +#!/usr/bin/env node +const { bundle } = require("simple-browser-js-bundler"); +const path = require("path"); +const dir = process.cwd(); + +bundle( + `${dir}/src/index.js`, + path.resolve(dir, "../public/views/admin-panel/assets/bundle.js"), + { + minify: !process.argv.includes("debug") + } +); \ No newline at end of file diff --git a/admin-frontend/package-lock.json b/admin-frontend/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..1106f6cd34a61d173e7b13ec04538d0120a9a75f --- /dev/null +++ b/admin-frontend/package-lock.json @@ -0,0 +1,3720 @@ +{ + "name": "admin-frontend", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "GPL-3.0", + "dependencies": { + "object-to-html-renderer": "^1.1.3" + }, + "devDependencies": { + "dotenv": "^10.0.0", + "simple-browser-js-bundler": "^0.1.1" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "dependencies": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + }, + "bin": { + "browser-pack": "bin/cmd.js" + } + }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "dependencies": { + "resolve": "^1.17.0" + } + }, + "node_modules/browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, + "dependencies": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "browserify": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "dependencies": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "node_modules/deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "dependencies": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + }, + "bin": { + "deps-sort": "bin/cmd.js" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "dependencies": { + "source-map": "~0.5.3" + } + }, + "node_modules/insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, + "dependencies": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + }, + "bin": { + "insert-module-globals": "bin/cmd.js" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "node_modules/lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "dependencies": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-to-html-renderer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-to-html-renderer/-/object-to-html-renderer-1.1.3.tgz", + "integrity": "sha512-OWZd0lRBOQylycJEuFf9CfeYEOsylU5CUf44yFWN6JEE3MpVts1nSwLCIQpUCcASwHJ0qa33DpI3eNLwcXiDWA==" + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "dependencies": { + "path-platform": "~0.11.15" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-browser-js-bundler": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/simple-browser-js-bundler/-/simple-browser-js-bundler-0.1.1.tgz", + "integrity": "sha512-T95fsFjDb8SG8ZF5s2Hn6rolpCrZWqGh+nqOMkZsVaDuAz2/yz/jegGICS22XAPA98chLeFyxgePmKR4E1AM4g==", + "dev": true, + "dependencies": { + "browserify": "^17.0.0", + "uglify-js": "^3.13.10" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "dependencies": { + "minimist": "^1.1.0" + } + }, + "node_modules/syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "dependencies": { + "acorn-node": "^1.2.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "dependencies": { + "process": "~0.11.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true, + "bin": { + "umd": "bin/cli.js" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + }, + "bin": { + "undeclared-identifiers": "bin.js" + } + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + } + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "requires": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "requires": { + "resolve": "^1.17.0" + } + }, + "browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, + "requires": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, + "requires": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + } + }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, + "requires": { + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-to-html-renderer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-to-html-renderer/-/object-to-html-renderer-1.1.3.tgz", + "integrity": "sha512-OWZd0lRBOQylycJEuFf9CfeYEOsylU5CUf44yFWN6JEE3MpVts1nSwLCIQpUCcASwHJ0qa33DpI3eNLwcXiDWA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, + "requires": { + "fast-safe-stringify": "^2.0.7" + } + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "simple-browser-js-bundler": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/simple-browser-js-bundler/-/simple-browser-js-bundler-0.1.1.tgz", + "integrity": "sha512-T95fsFjDb8SG8ZF5s2Hn6rolpCrZWqGh+nqOMkZsVaDuAz2/yz/jegGICS22XAPA98chLeFyxgePmKR4E1AM4g==", + "dev": true, + "requires": { + "browserify": "^17.0.0", + "uglify-js": "^3.13.10" + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "^1.1.0" + } + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "requires": { + "acorn-node": "^1.2.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "~0.11.0" + } + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz", + "integrity": "sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==", + "dev": true + }, + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "requires": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } +} diff --git a/admin-frontend/package.json b/admin-frontend/package.json new file mode 100644 index 0000000000000000000000000000000000000000..925ce8641b24bf6d1c8d50e7a6c2339d449de98b --- /dev/null +++ b/admin-frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "admin-frontend", + "version": "1.0.0", + "description": "An admin panel app for the Mentalo API", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "node ./build.js", + "build-debug": "node ./build.js debug" + }, + "author": "kuadrado-software", + "license": "GPL-3.0", + "dependencies": { + "object-to-html-renderer": "^1.1.3" + }, + "devDependencies": { + "dotenv": "^10.0.0", + "simple-browser-js-bundler": "^0.1.1" + } +} \ No newline at end of file diff --git a/admin-frontend/src/article.js b/admin-frontend/src/article.js new file mode 100644 index 0000000000000000000000000000000000000000..6ba6b37e8409d9ff8667e83f2e72264a9a9401c7 --- /dev/null +++ b/admin-frontend/src/article.js @@ -0,0 +1,25 @@ +"use strict"; + +class Article { + constructor(data) { + if (data) { + this.from(data) + } else { + this.title = ""; + this.subtitle = ""; + this.category = ""; + this.details = []; + this.images = []; + this.body = ""; + this.locale = ""; + } + } + + from(data) { + Object.entries(data).forEach(k_v => { + const [key, value] = k_v; + this[key] = value; + }); + } +} +module.exports = Article; \ No newline at end of file diff --git a/admin-frontend/src/components/create-article-form.js b/admin-frontend/src/components/create-article-form.js new file mode 100644 index 0000000000000000000000000000000000000000..e7651c0d4883e24c905110f162e39dda1e21f11a --- /dev/null +++ b/admin-frontend/src/components/create-article-form.js @@ -0,0 +1,327 @@ +"use strict"; + +const Article = require("../article"); +const { images_url } = require("../constants"); +const { fetch_post_article, fetch_article, fetch_update_article } = require("../xhr"); + +class CreateArticleForm { + constructor(params) { + this.params = params || {}; + this.state = { + output: new Article(this.params.data), + article_sent: {}, + } + } + + reset() { + this.state.output = new Article(); + this.state.article_sent = {}; + this.refresh(); + } + + handle_text_input(field, e) { + this.state.output[field] = e.target.value; + } + + handle_del_detail(index) { + this.state.output.details.splice(index, 1); + this.refresh_details(); + } + + handle_add_detail() { + this.state.output.details.push({ label: "", value: "" }); + this.refresh_details(); + } + + handle_del_image(index) { + this.state.output.images.splice(index, 1); + this.refresh_images(); + } + + handle_add_image() { + this.state.output.images.push("") + this.refresh_images(); + } + + refresh_details() { + obj2htm.subRender( + this.render_details_inputs(), + document.getElementById("create-article-form-details"), + { mode: "replace" } + ); + } + + render_details_inputs() { + return { + tag: "ul", + style_rules: { + gridColumn: "1 / span 2", + display: "flex", + flexDirection: "column", + gap: "10px", + listStyleType: "none", + padding: 0, + }, + id: "create-article-form-details", + contents: this.state.output.details.map((detail, i) => { + return { + tag: "li", + style_rules: { + display: "grid", + gridTemplateColumns: "200px auto 60px", + gap: "10px", + }, + contents: [ + { + tag: "input", + type: "text", + placeholder: "Label", + value: detail.label, + oninput: e => { + this.state.output.details[i].label = e.target.value; + } + }, + { + tag: "input", + type: "text", + placeholder: "Value", + value: detail.value, + oninput: e => { + this.state.output.details[i].value = e.target.value; + } + }, + { + tag: "button", contents: "DEL", + onclick: this.handle_del_detail.bind(this, i) + } + ] + } + }).concat([ + { + tag: "li", contents: [{ + tag: "button", contents: "ADD DETAIL", + onclick: this.handle_add_detail.bind(this) + }] + } + ]) + } + } + + refresh_images() { + obj2htm.subRender( + this.render_images_inputs(), + document.getElementById("create-article-form-images"), + { mode: "replace" } + ); + } + + render_images_inputs() { + return { + tag: "ul", + style_rules: { + gridColumn: "1 / span 2", + display: "flex", + flexDirection: "column", + gap: "10px", + listStyleType: "none", + padding: 0, + }, + id: "create-article-form-images", + contents: this.state.output.images.map((img, i) => { + return { + tag: "li", + style_rules: { + display: "flex", + alignItems: "center", + gap: "10px", + }, + contents: [ + { + tag: "div", + style_rules: { + display: "flex", + flexDirection: "center", + alignItems: "center", + justifyContent: "center", + width: "150px", + height: "150px", + overflow: "hidden", + }, + contents: [ + { + tag: "img", + style_rules: { minWidth: "100%", minHeight: "100%" }, + src: img ? `${images_url}/${img}` : "", + } + ], + }, + { + tag: "input", + type: "text", + placeholder: "image file name", + value: img, + oninput: e => { + this.state.output.images[i] = e.target.value; + } + }, + { + tag: "button", contents: "OK", + onclick: this.refresh_images.bind(this) + }, + { + tag: "button", contents: "DEL", + onclick: this.handle_del_image.bind(this, i) + } + ] + } + }).concat([ + { + tag: "li", contents: [{ + tag: "button", contents: "ADD IMAGE", + onclick: this.handle_add_image.bind(this) + }] + } + ]) + } + } + + render_article_sent() { + const article = this.state.article_sent; + return { + tag: "div", + style_rules: { + maxWidth: "800px", + }, + contents: [ + { tag: "button", contents: "RESET", onclick: this.reset.bind(this) }, + { tag: "h2", contents: article.title }, + { tag: "h4", contents: article.subtitle }, + { tag: "p", contents: article.body.replace(/\n/g, "<br>") }, + { + tag: "ul", contents: article.details.map(det => { + return { + tag: "li", + style_rules: { + display: "flex", + gap: "20px", + justifyContent: "space-between", + }, + contents: [ + { tag: "span", contents: det.label }, + { tag: "span", contents: det.value } + ] + }; + }) + }, + { + tag: "div", style_rules: { display: "flex", gap: "10px" }, + contents: article.images.map(img => { + return { + tag: "img", + style_rules: { height: "100px", width: "auto" }, + src: `${images_url}/${img}` + } + }) + } + ] + } + } + + refresh() { + obj2htm.subRender( + this.render(), + document.getElementById("create-article-form"), + { mode: "replace" } + ); + } + + render() { + return { + tag: "form", + id: "create-article-form", + style_rules: { + display: "grid", + maxWidth: "800px", + gridTemplateColumns: "1fr 1fr", + gap: "20px", + }, + onsubmit: e => { + e.preventDefault(); + + const __fetch = this.params.data + ? fetch_update_article + : fetch_post_article; + + __fetch(this.state.output) + .then(res => { + const id = res.insertedId ? res.insertedId.$oid : res._id ? res._id.$oid : undefined; + if (!id) { + alert("error") + } else { + fetch_article(id) + .then(article => { + this.state.article_sent = article; + this.params.on_article_sent && this.params.on_article_sent(); + this.refresh(); + }) + .catch(er => console.log(er)); + } + }) + .catch(err => console.log(err)) + }, + contents: this.state.article_sent._id ? [this.render_article_sent()] : [ + { + tag: "input", type: "text", placeholder: "category", + value: this.state.output.category, + oninput: this.handle_text_input.bind(this, "category") + }, + { + tag: "select", value: this.state.output.locale, + onchange: e => this.state.output.locale = e.target.value, + contents: [{ + tag: "option", + value: "", + contents: "-- LOCALE --" + }].concat(["fr", "en", "es"].map(loc => { + return { + tag: "option", + value: loc, + contents: loc, + selected: this.state.output.locale === loc + } + })) + }, + { + tag: "input", type: "text", + placeholder: "Article title", + value: this.state.output.title, + oninput: this.handle_text_input.bind(this, "title") + }, + { + tag: "input", type: "text", + style_rules: { + gridColumn: "1 / span 2" + }, + placeholder: "Article subtitle", + value: this.state.output.subtitle, + oninput: this.handle_text_input.bind(this, "subtitle") + }, + { + tag: "textarea", + style_rules: { + gridColumn: "1 / span 2", + height: "300px", + }, + value: this.state.output.body, + placeholder: "Article body", + oninput: this.handle_text_input.bind(this, "body") + }, + this.render_details_inputs(), + this.render_images_inputs(), + { tag: "input", type: "submit" } + ] + } + } +} + +module.exports = CreateArticleForm; \ No newline at end of file diff --git a/admin-frontend/src/components/root.js b/admin-frontend/src/components/root.js new file mode 100644 index 0000000000000000000000000000000000000000..e4cf91679b9ed73c8c0773b96b0a0737a4e044f6 --- /dev/null +++ b/admin-frontend/src/components/root.js @@ -0,0 +1,54 @@ + +const CreateArticleForm = require("./create-article-form"); +const UpdateArticleForm = require("./update-article-form"); + +class RootComponent { + constructor() { + this.state = { + selected_tab: "" + }; + } + + handle_nav_click(e) { + this.state.selected_tab = e.target.tab_name; + obj2htm.renderCycle(); + } + + render_state() { + switch (this.state.selected_tab) { + case "create": + return new CreateArticleForm().render(); + case "update": + return new UpdateArticleForm().render(); + default: + return undefined; + } + } + + render() { + return { + tag: "main", + contents: [ + { tag: "h1", contents: "Kuadrado admin panel" }, + { + tag: "nav", + contents: [ + { + tag: "span", contents: "Create article", tab_name: "create", + class: this.state.selected_tab === "create" ? "selected" : "", + onclick: this.handle_nav_click.bind(this), + }, + { + tag: "span", contents: "Update article", tab_name: "update", + class: this.state.selected_tab === "update" ? "selected" : "", + onclick: this.handle_nav_click.bind(this), + }, + ], + }, + this.render_state(), + ], + }; + } +} + +module.exports = RootComponent; \ No newline at end of file diff --git a/admin-frontend/src/components/update-article-form.js b/admin-frontend/src/components/update-article-form.js new file mode 100644 index 0000000000000000000000000000000000000000..beff4462e4b1246367e431ba4a60f281d1dfa574 --- /dev/null +++ b/admin-frontend/src/components/update-article-form.js @@ -0,0 +1,151 @@ +"use strict"; + +const { fetch_article_by_title, fetch_delete_article } = require("../xhr"); +const CreateArticleForm = require("./create-article-form"); + +class UpdateArticleForm { + constructor() { + this.state = { + search_article_title: "", + search_result: {}, + article_to_update: {}, + } + } + + reset() { + this.state = { + search_article_title: "", + search_result: {}, + article_to_update: {}, + }; + } + + handle_search_article() { + fetch_article_by_title(this.state.search_article_title) + .then(res => { + this.state.search_result = res; + this.state.article_to_update = {}; + this.refresh_search_result(); + this.refresh_update_form(); + }) + .catch(err => alert(err)); + } + + handle_select_result() { + this.state.article_to_update = { ...this.state.search_result }; + this.refresh_update_form(); + } + + handle_delete_article() { + fetch_delete_article(this.state.search_result._id.$oid) + .then(res => { + alert(res); + this.reset(); + this.refresh(); + }) + .catch(err => alert(err)) + } + + render_search() { + return { + tag: "form", + onsubmit: e => { + e.preventDefault(); + this.handle_search_article(); + }, + style_rules: { display: "flex", gap: "10px", width: "100%" }, + contents: [ + { + tag: "input", type: "text", value: this.state.search_article_title, + style_rules: { flex: 1 }, + placeholder: "Search article by title", + oninput: e => this.state.search_article_title = e.target.value, + }, + { + tag: "input", type: "submit", value: "SEARCH" + } + ] + } + } + + refresh_search_result() { + obj2htm.subRender( + this.render_search_result(), + document.getElementById("update-article-form-search-result"), + { mode: "replace" }, + ); + } + + render_search_result() { + const { search_result } = this.state; + return { + tag: "div", + id: "update-article-form-search-result", + style_rules: { + display: "flex", + gap: "10px", + alignItems: "center" + }, + contents: search_result.title ? [ + { tag: "strong", contents: search_result.title }, + { + tag: "button", contents: "SELECT", + onclick: this.handle_select_result.bind(this) + }, + { + tag: "button", contents: "DELETE", + onclick: this.handle_delete_article.bind(this) + } + ] : [] + } + } + + refresh_update_form() { + obj2htm.subRender( + this.render_update_form(), + document.getElementById("update-article-form-container"), + { mode: "replace" }, + ); + } + + render_update_form() { + return { + tag: "div", + id: "update-article-form-container", + contents: this.state.article_to_update._id + ? [new CreateArticleForm({ + data: this.state.article_to_update, + on_article_sent: () => { + this.reset(); + this.refresh_search_result(); + } + }).render()] + : [] + } + } + + refresh() { + obj2htm.subRender(this.render(), document.getElementById("update-article-multiform"), { mode: "replace" }) + } + + render() { + return { + tag: "div", + id: "update-article-multiform", + style_rules: { + display: "flex", + flexDirection: "column", + gap: "20px", + maxWidth: "800px", + }, + contents: [ + this.render_search(), + this.render_search_result(), + { tag: "hr", style_rules: { width: "100%" } }, + this.render_update_form(), + ] + } + } +} + +module.exports = UpdateArticleForm; \ No newline at end of file diff --git a/admin-frontend/src/constants.js b/admin-frontend/src/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..ffe6ed3440749abe33a335520686fda62d878cf2 --- /dev/null +++ b/admin-frontend/src/constants.js @@ -0,0 +1,3 @@ +module.exports = { + images_url: "/assets/images" +} \ No newline at end of file diff --git a/admin-frontend/src/index.js b/admin-frontend/src/index.js new file mode 100644 index 0000000000000000000000000000000000000000..7be69ac961e84325f2127c80af1a740d9e9b2abb --- /dev/null +++ b/admin-frontend/src/index.js @@ -0,0 +1,6 @@ +const renderer = require("object-to-html-renderer"); +const RootComponent = require("./components/root"); + +renderer.register("obj2htm"); +obj2htm.setRenderCycleRoot(new RootComponent()); +obj2htm.renderCycle(); \ No newline at end of file diff --git a/admin-frontend/src/utils.js b/admin-frontend/src/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..13416856875be5e280545f139a1b92260dae622a --- /dev/null +++ b/admin-frontend/src/utils.js @@ -0,0 +1,8 @@ +function get_text_date(iso_str) { + const date = new Date(iso_str); + return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()} - ${date.getHours()}h${date.getMinutes()}mn` +} + +module.exports = { + get_text_date +} \ No newline at end of file diff --git a/admin-frontend/src/xhr.js b/admin-frontend/src/xhr.js new file mode 100644 index 0000000000000000000000000000000000000000..6d1e210272f6f5341dcf919b90e2a5e0a04c202b --- /dev/null +++ b/admin-frontend/src/xhr.js @@ -0,0 +1,118 @@ +async function fetch_article(article_id) { + return new Promise((resolve, reject) => { + fetch(`/article/${article_id}`).then(async res => { + if (res.status >= 400 && res.status < 600) { + const text = await res.text(); + reject(text); + } else { + resolve(await res.json()); + } + }).catch(e => reject(e)) + }) +} + +async function fetch_article_by_title(article_title) { + const form_data = new FormData(); + form_data.append("title", article_title); + + return new Promise((resolve, reject) => { + fetch(`/article-by-title/`, { + method: "POST", + body: new URLSearchParams(form_data), + }).then(async res => { + if (res.status >= 400 && res.status < 600) { + const text = await res.text(); + reject(text); + } else { + resolve(await res.json()); + } + }).catch(e => reject(e)) + }) +} + +async function fetch_articles_by_category(category) { + return new Promise((resolve, reject) => { + fetch(`/articles/${category}`).then(async res => { + if (res.status >= 400 && res.status < 600) { + const text = await res.text(); + reject(text); + } else { + resolve(await res.json()); + } + }).catch(e => reject(e)) + }) +} + +async function fetch_post_article(article_data) { + return new Promise((resolve, reject) => { + fetch("/post-article", { + credentials: 'include', + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(article_data), + }) + .then(async res => { + if (res.status >= 400 && res.status < 600) { + const text = await res.text(); + reject(text) + } else { + const json = await res.json(); + resolve(json); + } + }) + .catch(err => reject(err)) + }) +} + +async function fetch_update_article(article_data) { + return new Promise((resolve, reject) => { + fetch(`/update-article/${article_data._id.$oid}`, { + credentials: 'include', + method: "PUT", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(article_data), + }) + .then(async res => { + if (res.status >= 400 && res.status < 600) { + const text = await res.text(); + reject(text) + } else { + const json = await res.json(); + resolve(json); + } + }) + .catch(err => reject(err)) + }) +} + +async function fetch_delete_article(article_id) { + return new Promise((resolve, reject) => { + fetch(`/delete-article/${article_id}`, { + credentials: 'include', + method: "DELETE" + }) + .then(async res => { + const text = await res.text(); + if (res.status >= 400 && res.status < 600) { + reject(text) + } else { + resolve(text); + } + }) + .catch(err => reject(err)) + }); +} + + +module.exports = { + fetch_article, + fetch_article_by_title, + fetch_articles_by_category, + fetch_post_article, + fetch_update_article, + fetch_delete_article, +} \ No newline at end of file diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 0552a29e35b9facd676617c5dc451b7ae7433248..bc1bd9724d27d18a7bd42b25e01c36635c56fd0a 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -5,6 +5,8 @@ services: context: . dockerfile: ./dev.Dockerfile container_name: "kuadrado_server" + depends_on: + - ${DATABASE_NAME} restart: unless-stopped ports: - 80:${SERVER_PORT} @@ -17,3 +19,17 @@ services: command: cargo run env_file: - ./.env + kuadradodb: + build: ./mongo/ + container_name: ${DATABASE_NAME} + environment: + - MONGO_INITDB_DATABASE=${DATABASE_NAME} + - MONGO_INITDB_ROOT_USERNAME=${DB_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MONGO_INITDB_NON_ROOT_USERNAME=${DB_USERNAME} + - MONGO_INITDB_NON_ROOT_PASSWORD=${DB_USER_PASSWORD} + volumes: + - ./mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro + - /var/${DATABASE_NAME}-volume:/data/db + ports: + - "27017-27019:27017-27019" diff --git a/docker-compose.yml b/docker-compose.yml index d65e02efb0ff4d1c6443ec06b82e76caee85e484..35cef6e09d669b7b328b8afc4ee37f9b9d572f8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,8 @@ services: context: . dockerfile: ./Dockerfile container_name: "kuadrado_server" + depends_on: + - ${DATABASE_NAME} restart: unless-stopped ports: - 80:${SERVER_PORT} @@ -14,3 +16,17 @@ services: - /etc/letsencrypt/:${RESOURCES_DIR}/certs:ro env_file: - ./.env + kuadradodb: + build: ./mongo/ + container_name: ${DATABASE_NAME} + environment: + - MONGO_INITDB_DATABASE=${DATABASE_NAME} + - MONGO_INITDB_ROOT_USERNAME=${DB_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MONGO_INITDB_NON_ROOT_USERNAME=${DB_USERNAME} + - MONGO_INITDB_NON_ROOT_PASSWORD=${DB_USER_PASSWORD} + volumes: + - ./mongo/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro + - /var/${DATABASE_NAME}-volume:/data/db + ports: + - "27017-27019:27017-27019" diff --git a/mongo/Dockerfile b/mongo/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1623104f669b51bcdbfda018f9ba40dcb39c1471 --- /dev/null +++ b/mongo/Dockerfile @@ -0,0 +1,2 @@ +from mongo +RUN mkdir /mongoinit && chown mongodb -R /mongoinit && chgrp mongodb -R /mongoinit \ No newline at end of file diff --git a/mongo/init-mongo.js b/mongo/init-mongo.js new file mode 100644 index 0000000000000000000000000000000000000000..52495ec801115591c325a1fb66688ec64ce99199 --- /dev/null +++ b/mongo/init-mongo.js @@ -0,0 +1,33 @@ +function getEnvVariable(envVar) { + // Thanks for the tip: https://dev.to/jsheridanwells/dockerizing-a-mongo-database-4jf2 + + const command = run("sh", "-c", `printenv --null ${envVar} >/mongoinit/${envVar}.txt`); + // note: 'printenv --null' prevents adding line break to value + + if (command != 0) return Error("Failed to retrieve env variable : " + envVar); + + // .replace(/\0/g, '') removes the NULL characters + return cat(`/mongoinit/${envVar}.txt`).replace(/\0/g, ''); +} + +db.createUser({ + user: getEnvVariable("MONGO_INITDB_NON_ROOT_USERNAME"), + pwd: getEnvVariable("MONGO_INITDB_NON_ROOT_PASSWORD"), + roles: [ + { + role: "readWrite", + db: getEnvVariable("MONGO_INITDB_DATABASE") + } + ] +}); + +db = new Mongo().getDB(getEnvVariable("MONGO_INITDB_DATABASE")); + +db.createCollection("articles"); +db.createCollection("administrators"); + +db.administrators.createIndex({ "username": 1 }, { unique: true }); +db.administrators.createIndex({ "auth_token": 1 }, { unique: true }); + + +run("sh", "-c", "rm -rf /mongoinit/*"); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..59e62eb67e7f12ba241559f5f01dc4c90c3e9cae --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "kuadrado-website", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} diff --git a/public/articles/software/object-to-html-renderer/images/obj-to-html-logo.png b/public/articles/software/object-to-html-renderer/images/obj2htm-logo.png similarity index 100% rename from public/articles/software/object-to-html-renderer/images/obj-to-html-logo.png rename to public/articles/software/object-to-html-renderer/images/obj2htm-logo.png diff --git a/public/assets/images/mental-eau.png b/public/assets/images/mental-eau.png new file mode 100644 index 0000000000000000000000000000000000000000..ed6c2eeced7fbb226d7c30e1a526b7c0b09c6d93 Binary files /dev/null and b/public/assets/images/mental-eau.png differ diff --git a/public/assets/images/obj2htm-logo.png b/public/assets/images/obj2htm-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8efda75f697267280b833f5aeb6b1a36b96cee67 Binary files /dev/null and b/public/assets/images/obj2htm-logo.png differ diff --git a/public/assets/images/screen_make_frames.png b/public/assets/images/screen_make_frames.png new file mode 100644 index 0000000000000000000000000000000000000000..1cad3ad3fa68becd61855f7055cfacfc231ef401 Binary files /dev/null and b/public/assets/images/screen_make_frames.png differ diff --git a/public/standard/favicon.ico b/public/standard/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6c215345b05c43669984f26ba69e8d1e708608e2 Binary files /dev/null and b/public/standard/favicon.ico differ diff --git a/public/standard/robots.txt b/public/standard/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..9a5052424dc62c19fe15ef92eda82aeb4dfcf42b --- /dev/null +++ b/public/standard/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: /articles/ +Disallow: /style/ +Sitemap: https://kuadrado-software.fr/sitemap.xml diff --git a/public/standard/sitemap.xml b/public/standard/sitemap.xml new file mode 100644 index 0000000000000000000000000000000000000000..5ab5812c32912e5280107c300fcb733ec1bcaba5 --- /dev/null +++ b/public/standard/sitemap.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + <url> + <loc>https://kuadrado-software.fr</loc> + <lastmod>2021-11-21</lastmod> + </url> + <url> + <loc>https://kuadrado-software.fr/games/</loc> + <lastmod>2021-11-21</lastmod> + </url> + <url> + <loc>https://kuadrado-software.fr/education/</loc> + <lastmod>2021-11-21</lastmod> + </url> + <url> + <loc>https://kuadrado-software.fr/software-development/</loc> + <lastmod>2021-11-21</lastmod> + </url> +</urlset> diff --git a/public/views/404/404.html b/public/views/404/404.html new file mode 100644 index 0000000000000000000000000000000000000000..435c37509f8eeec80b791649c27a33caebca3c5e --- /dev/null +++ b/public/views/404/404.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>Page not found</title> +</head> + +<body> + <h1>404 : Page not found</h1> +</body> + +</html> \ No newline at end of file diff --git a/public/views/admin-login/assets/login.js b/public/views/admin-login/assets/login.js new file mode 100644 index 0000000000000000000000000000000000000000..aee791408d27aad0cb9475a11d6755cc8b12f9ab --- /dev/null +++ b/public/views/admin-login/assets/login.js @@ -0,0 +1,16 @@ +const login_form = document.getElementById("login-form"); +login_form.onsubmit = e => { + e.preventDefault(); + fetch("/admin-auth", { + method: "POST", + body: new URLSearchParams(new FormData(e.target)) + }).then(res => { + if (res.status >= 400) { + alert(res.statusText) + } else { + window.location.assign("/v/admin-panel") + } + }).catch(err => { + alert(err.statusText || "Server error") + }); +} \ No newline at end of file diff --git a/public/views/admin-login/index.html b/public/views/admin-login/index.html new file mode 100644 index 0000000000000000000000000000000000000000..ce8dfbf824d5682b33b3e1f454f28dc55b6caad2 --- /dev/null +++ b/public/views/admin-login/index.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>Kuadrado admin login</title> +</head> + +<body> + <h1>Admin login</h1> + <form id="login-form"> + <label for="username"></label> + <input type="text" id="username" name="username"> + <label for="password"></label> + <input type="password" id="password" name="password"> + <input type="submit"> + </form> +</body> +<script src="/v/admin-login/assets/login.js"></script> + +</html> \ No newline at end of file diff --git a/public/views/admin-panel/assets/style.css b/public/views/admin-panel/assets/style.css new file mode 100644 index 0000000000000000000000000000000000000000..67e8a839c5a5454c2698a4ebc6effa38e8e28bb5 --- /dev/null +++ b/public/views/admin-panel/assets/style.css @@ -0,0 +1,35 @@ +body * { + font-family: monospace; + + box-sizing: border-box; +} + +input[type="text"], +textarea { + padding: 8px; +} + +button, +input[type="submit"] { + padding: 10px; + cursor: pointer; +} + +nav { + display: flex; + gap: 1px; + margin: 20px 0; +} + +nav span { + padding: 20px; + font-weight: bold; + background-color: #ddd; + cursor: pointer; +} + +nav span:hover, +nav span.selected { + background-color: #555; + color: white; +} diff --git a/public/views/admin-panel/index.html b/public/views/admin-panel/index.html new file mode 100644 index 0000000000000000000000000000000000000000..89ca456451ad609dc85877ee2813f9ea7c374a0f --- /dev/null +++ b/public/views/admin-panel/index.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>Kuadrado admin panel</title> + <link rel="stylesheet" href="/v/admin-panel/assets/style.css"> +</head> + +<body></body> +<script src="/v/admin-panel/assets/bundle.js"></script> + +</html> \ No newline at end of file diff --git a/public/views/test-view-auth/index.html b/public/views/test-view-auth/index.html new file mode 100644 index 0000000000000000000000000000000000000000..91c451a43d074893c7a157b6d2e13aea1c79fec2 --- /dev/null +++ b/public/views/test-view-auth/index.html @@ -0,0 +1 @@ +<h1>TEST AUTH</h1> \ No newline at end of file diff --git a/public/views/test-view/index.html b/public/views/test-view/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6e4c7653e016d0d4cbb6b93fd020a37242de4202 --- /dev/null +++ b/public/views/test-view/index.html @@ -0,0 +1 @@ +<h1>TEST</h1> \ No newline at end of file diff --git a/public/views/unauthorized/unauthorized.html b/public/views/unauthorized/unauthorized.html new file mode 100644 index 0000000000000000000000000000000000000000..af9827a5bc04bff7fb0c3cbb5a753a7ecfd59508 --- /dev/null +++ b/public/views/unauthorized/unauthorized.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <title>Unauthorized</title> +</head> + +<body> + <h1>Unauthorized</h1> + <p>You must login as an administrator to access this page</p> + <a href='/v/admin-login'>Login page</a> +</body> + +</html> \ No newline at end of file diff --git a/src/app_state.rs b/src/app_state.rs new file mode 100644 index 0000000000000000000000000000000000000000..c1744971b663d64b95f1f77909f114b77b3b4f25 --- /dev/null +++ b/src/app_state.rs @@ -0,0 +1,66 @@ +use crate::{crypto::Encryption, env::Env, init_admin::create_default_admin_if_none}; +use wither::mongodb::{options::ClientOptions, Client, Database}; + +#[derive(Debug, Clone)] +/// The app_state that must be given to the actix::App instance using App::new().app_data(web::Data::new(AppState::new())) +/// It holds the database client connection, an Env struct which provides all values defined in the .env file, +/// and an Encryption struct which is reponsible for encrypting and decrypting data such as passwords and auth tokens. +pub struct AppState { + pub db: Database, + pub env: Env, + pub encryption: Encryption, +} + +impl AppState { + /// Creates the Mongodb database client connection + async fn get_db_connection(host: &str, env: &Env) -> Database { + let db_connection_string = format!( + "mongodb://{}:{}@{}:{}/{}", + env.db_username, env.db_user_pwd, host, env.db_port, env.db_name + ); + + let client_options = ClientOptions::parse(&db_connection_string) + .await + .expect("Error creating database client options"); + + let client = Client::with_options(client_options).expect("Couldn't connect to database."); + client.database(&env.db_name) + } + + pub async fn new() -> Self { + let env = Env::new(); + let db = Self::get_db_connection(&env.db_name, &env).await; + + let encryption = Encryption::new(env.crypt_key.to_owned()); + + AppState { + db, + env, + encryption, + } + } + + /// This calls Self::new() and creates a default administrator before returning the instance + pub async fn with_default_admin_user() -> Self { + let instance = Self::new().await; + if let Err(e) = create_default_admin_if_none(&instance).await { + panic!("Error creating admin user: {}\nWill exit process now.", e); + }; + instance + } + + #[cfg(test)] + /// Provides an instance with some specificities for testing + pub async fn for_test() -> Self { + let env = Env::for_test(); + let db = Self::get_db_connection("localhost", &env).await; + + let encryption = Encryption::new(env.crypt_key.to_owned()); + + AppState { + db, + env, + encryption, + } + } +} diff --git a/src/crypto.rs b/src/crypto.rs new file mode 100644 index 0000000000000000000000000000000000000000..fb02f6af89913bb0685ddd41222af196ebeeaadb --- /dev/null +++ b/src/crypto.rs @@ -0,0 +1,91 @@ +use magic_crypt::{MagicCrypt, MagicCryptTrait, SecureBit}; +use rand::{distributions::Alphanumeric, Rng}; + +#[derive(Debug, Clone)] +/// A structure responsible of encrypting and decrypting data such as auth token, passwords and email addresses. +pub struct Encryption { + /// The encryption key must be keeped secret and is loaded from the $CRYPT_KEY environment variable + pub key: String, +} + +impl Encryption { + pub fn new(key: String) -> Self { + Encryption { key } + } + + /// Gets a string as an argument and returns a base64 hash of the string based on the secret key and magic_crypt::SecureBit::Bit256 algorithm + pub fn encrypt(&self, source: &String) -> String { + let mc = MagicCrypt::new(&self.key, SecureBit::Bit256, None::<String>); + mc.encrypt_str_to_base64(source) + } + + /// Gets a string base64 hash as an argument and returns the decryted string. + /// Panics if the source base64 string cannot be decrypted (should happen if trying to decrypt a regular string) + #[cfg(test)] + pub fn decrypt(&self, source: &String) -> String { + let mc = MagicCrypt::new(&self.key, SecureBit::Bit256, None::<String>); + mc.decrypt_base64_to_string(source).unwrap() + } + + /// Generates a random ascii lowercase string. Length being given as argument. + pub fn random_ascii_lc_string(&self, length: usize) -> String { + // Thanks to https://stackoverflow.com/questions/54275459/how-do-i-create-a-random-string-by-sampling-from-alphanumeric-characters#54277357 + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(length) + .map(char::from) + .collect::<String>() + .to_ascii_lowercase() + } +} + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + * _______ ______ ______ _______ *@@ + * |__ __@ | ____@ / ____@ |__ __@ *@@ + * | @ | @__ \_ @_ | @ *@@ + * | @ | __@ \ @_ | @ *@@ + * | @ | @___ ____\ @ | @ *@@ + * |__@ |______@ \______@ |__@ *@@ + * *@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@*/ + +#[cfg(test)] +mod test_encryption { + use super::*; + + #[test] + fn test_random_ascii_lc_string() { + dotenv::dotenv().ok(); + let key = std::env::var("CRYPT_KEY").unwrap(); + let enc = Encryption::new(key); + let rdm_str = enc.random_ascii_lc_string(32); + assert_eq!(rdm_str.len(), 32); + assert!(rdm_str.chars().all(char::is_alphanumeric)); + assert_eq!(rdm_str, rdm_str.to_lowercase()); + } + + #[test] + fn test_encrypt() { + dotenv::dotenv().ok(); + let key = std::env::var("CRYPT_KEY").unwrap(); + let enc = Encryption::new(key); + + let an_email = String::from("kuadrado-email@test.com"); + let email_hash = enc.encrypt(&an_email); + assert_ne!(an_email, email_hash); + } + + #[test] + fn test_decrypt() { + dotenv::dotenv().ok(); + let key = std::env::var("CRYPT_KEY").unwrap(); + let enc = Encryption::new(key); + + let an_email = String::from("kuadrado-email@test.com"); + let email_hash = enc.encrypt(&an_email); + let decrypted = enc.decrypt(&email_hash); + assert_eq!(an_email, decrypted); + } +} diff --git a/src/env.rs b/src/env.rs index e1ab149816238b5dec64c2015e023d341fbd31f2..cf5860ba3b3edac6a6ce52757cd4b1000b0c336a 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,5 +1,19 @@ use std::env; +#[derive(Debug, Clone)] +/// Makes a copy of all required values defined in the system environment variables +pub struct Env { + pub release_mode: String, + pub db_username: String, + pub db_user_pwd: String, + pub db_name: String, + pub db_port: String, + pub server_host: String, + pub crypt_key: String, + pub default_admin_username: String, + pub default_admin_password: String, +} + static RELEASE_MODES: [&str; 3] = ["debug", "test", "prod"]; pub fn get_release_mode() -> String { @@ -25,3 +39,39 @@ pub fn get_log_level() -> String { _ => String::from("info"), } } + +impl Env { + pub fn new() -> Env { + Env { + release_mode: get_release_mode(), + db_username: env::var("DB_USERNAME").expect("DB_USERNAME is not defined."), + db_user_pwd: env::var("DB_USER_PASSWORD").expect("DB_USER_PASSWORD is not defined."), + db_name: env::var("DATABASE_NAME").expect("DATABASE_NAME is not defined."), + db_port: env::var("DB_PORT").expect("DB_PORT is not defined."), + server_host: env::var("SERVER_HOST").expect("SERVER_HOST is not defined"), + crypt_key: env::var("CRYPT_KEY").expect("CRYPT_KEY is not defined."), + default_admin_username: env::var("DEFAULT_ADMIN_USERNAME") + .expect("DEFAULT_ADMIN_USERNAME is not defined"), + default_admin_password: env::var("DEFAULT_ADMIN_PASSWORD") + .expect("DEFAULT_ADMIN_PASSWORD is not defined"), + } + } + + #[cfg(test)] + /// Returns an instance with some values adjusted for testing such as email addresses + pub fn for_test() -> Env { + Env { + release_mode: String::from("debug"), + db_username: env::var("DB_USERNAME").expect("DB_USERNAME is not defined."), + db_user_pwd: env::var("DB_USER_PASSWORD").expect("DB_USER_PASSWORD is not defined."), + db_name: env::var("DATABASE_NAME").expect("DATABASE_NAME is not defined."), + db_port: env::var("DB_PORT").expect("DB_PORT is not defined."), + server_host: env::var("SERVER_HOST").expect("SERVER_HOST is not defined"), + crypt_key: env::var("CRYPT_KEY").expect("CRYPT_KEY is not defined."), + default_admin_username: env::var("DEFAULT_ADMIN_USERNAME") + .expect("DEFAULT_ADMIN_USERNAME is not defined"), + default_admin_password: env::var("DEFAULT_ADMIN_PASSWORD") + .expect("DEFAULT_ADMIN_PASSWORD is not defined"), + } + } +} diff --git a/src/init_admin.rs b/src/init_admin.rs new file mode 100644 index 0000000000000000000000000000000000000000..dbe1f2906689770d962d15b74a2ea8c4a6cbc927 --- /dev/null +++ b/src/init_admin.rs @@ -0,0 +1,35 @@ +use crate::model::Administrator; +use crate::AppState; +use wither::{bson::doc, prelude::Model}; + +/// Creates the default administrator if it doesn't already exists and returns a Result. +pub async fn create_default_admin_if_none(app_state: &AppState) -> Result<(), String> { + let admin_username = app_state.env.default_admin_username.to_owned(); + let admin_password = app_state.env.default_admin_password.to_owned(); + + let admin = Administrator::from_values(app_state, admin_username, admin_password); + + let admin_doc = doc! { + "username": &admin.username, + "password_hash": &admin.password_hash + }; + + match Administrator::find_one(&app_state.db, admin_doc, None).await { + Ok(found_user) => match found_user { + Some(_) => Ok(()), + None => { + println!("Kuadrado admin will be created"); + match app_state + .db + .collection_with_type::<Administrator>("administrators") + .insert_one(admin, None) + .await + { + Ok(_) => Ok(()), + Err(e) => Err(format!("Error creating administrator: {:?}", e)), + } + } + }, + Err(e) => Err(format!("Error creating administrator: {:?}", e)), + } +} diff --git a/src/main.rs b/src/main.rs index fa51ed907681b9ee70647924e5f625a97ba26284..3b83cc9cb176d13ad6690363143a9a0391a07eae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,32 @@ -//! # REST API server for the Mentalo application +//! # WEB SERVER FOR THE KUADRADO SOFTWARE WEBSITE +mod app_state; +mod crypto; mod env; +mod init_admin; +mod middleware; +mod model; +mod service; mod standard_static_files; mod tls; +mod view; +mod view_resource; use actix_files::Files; use actix_web::{ - middleware::Logger, - web::{get, resource, to}, + middleware::{normalize::TrailingSlash, Logger, NormalizePath}, + web::{get, resource, scope, to, Data}, App, HttpResponse, HttpServer, }; use actix_web_middleware_redirect_https::RedirectHTTPS; +use app_state::AppState; use env::get_log_level; use env_logger::Env; +use middleware::AuthenticatedAdminMiddleware; +use service::*; use standard_static_files::{favicon, robots, sitemap}; use std::env::var as env_var; use tls::get_tls_config; +use view::get_view; +use view_resource::{ViewResourceDescriptor, ViewResourceManager}; #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -25,6 +38,8 @@ async fn main() -> std::io::Result<()> { std::path::PathBuf::from(env_var("RESOURCES_DIR").expect("RESOURCES_DIR is not defined")) .join("public"); + let app_state = AppState::with_default_admin_user().await; + HttpServer::new(move || { App::new() .wrap(Logger::default()) @@ -33,15 +48,68 @@ async fn main() -> std::io::Result<()> { format!(":{}", server_port), format!(":{}", server_port_tls), )])) - // .wrap(NormalizePath::new(TrailingSlash::Trim)) - // Allow json payload to have size until ~32MB - // .app_data(JsonConfig::default().limit(1 << 25u8)) + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .app_data(Data::new(ViewResourceManager::with_views(vec![ + ViewResourceDescriptor { + path_str: "admin-panel", + index_file_name: "index.html", + resource_name: "admin-panel", + apply_auth_middleware: true, + }, + ViewResourceDescriptor { + path_str: "admin-login", + index_file_name: "index.html", + resource_name: "admin-login", + apply_auth_middleware: false, + }, + ViewResourceDescriptor { + path_str: "404", + index_file_name: "404.html", + resource_name: "404", + apply_auth_middleware: false, + }, + ViewResourceDescriptor { + path_str: "unauthorized", + index_file_name: "unauthorized.html", + resource_name: "unauthorized", + apply_auth_middleware: false, + }, + ]))) + .wrap(NormalizePath::new(TrailingSlash::Trim)) + // .app_data(JsonConfig::default().limit(1 << 25u8)) // Allow json payload to have size until ~32MB + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // REST API ///////////////////////////////////////////////////////////////////////////////////////////////// + .service(admin_authentication) + .service(post_article) + .service(update_article) + .service(delete_article) + .service(get_articles_by_category) + .service(get_article) + .service(get_article_by_title) ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // STANDARD FILES /////////////////////////////////////////////////////////////////////////////////////////// .service(resource("/favicon.ico").route(get().to(favicon))) .service(resource("/robots.txt").route(get().to(robots))) .service(resource("/sitemap.xml").route(get().to(sitemap))) ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + // VIEWS //////////////////////////////////////////////////////////////////////////////////////////////////// + .service( + scope("/v") + .service(Files::new( + "/admin-panel/assets", + public_dir.join("views/admin-panel/assets"), + )) + .service(Files::new( + "/admin-login/assets", + public_dir.join("views/admin-login/assets"), + )) + // get_view will match any url to we put it at last + .service(get_view), + ) + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC WEBSITE ////////////////////////////////////////////////////////////////////////////////////////////// .service(Files::new("/", &public_dir).index_file("index.html")) ///////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/middleware.rs b/src/middleware.rs new file mode 100644 index 0000000000000000000000000000000000000000..1dde054b02d777b3c567627416f2001192bd3e19 --- /dev/null +++ b/src/middleware.rs @@ -0,0 +1,121 @@ +use crate::{ + model::{AdminAuthCredentials, Administrator}, + AppState, +}; +use actix_web::{cookie::SameSite, http::Cookie, web::Form, HttpMessage, HttpRequest}; +use wither::{bson::doc, prelude::Model}; + +/// Returns a Secure actix_web::http::Cookie. +pub fn get_auth_cookie(name: &'static str, value: String) -> Cookie<'static> { + Cookie::build(name, value) + .secure(true) + .http_only(true) + .same_site(SameSite::Strict) + .path("/") + .finish() +} + +/// This is not a real middleware as it is meant to be executed only after having processed the request and not before. +/// It must be registered in the actix App instance with app_data. +/// ``` +/// App::new() +/// .app_data(Data::new(AuthenticatedAdminMiddleware::new("some-auth-cookie-name"))) +/// ``` +/// If a service need to perform an authentication before doing anything, this "pseudo-middleware" should be run before anything else in the function. +/// Example: +/// ``` +/// #[post("/some-url")] +/// pub async fn some_service( +/// app_state: Data<AppState>, +/// middleware: Data<AuthenticatedAdminMiddleware<'_>>, +/// req: HttpRequest, +/// ) -> impl Responder { +/// if middleware.exec(&app_state, &req, None).await.is_err() { +/// return HttpResponse::Unauthorized().finish(); +/// } +/// ... Authenticated action .... +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct AuthenticatedAdminMiddleware<'a> { + /// The name of the authentication cookie + pub cookie_name: &'a str, +} + +impl<'a> AuthenticatedAdminMiddleware<'a> { + pub fn new(cookie_name: &'a str) -> Self { + AuthenticatedAdminMiddleware { cookie_name } + } + + /// Performs Administrator authentication from form data with username and password + /// Returns an authentication Cookie instance if the authentication succeeds, or an error. + async fn try_auth_from_form_data( + &self, + app_state: &AppState, + form_data: Form<AdminAuthCredentials>, + ) -> Result<Cookie<'static>, ()> { + match Administrator::authenticated(app_state, form_data.into_inner()).await { + Ok(ref mut admin) => { + let auth_token = app_state.encryption.random_ascii_lc_string(256); + admin.auth_token = Some(app_state.encryption.encrypt(&auth_token)); + + if admin + .save(&app_state.db, Some(doc!("_id": admin.id().unwrap()))) + .await + .is_err() + { + println!("Failed to update admin auth_token"); + return Err(()); + } + + let cookie = get_auth_cookie("kuadrado-admin-auth", auth_token.to_owned()); + + return Ok(cookie); + } + Err(_) => return Err(()), + } + } + + /// Performs Administrator authentication from the authentication cookie value + async fn try_auth_from_auth_cookie( + &self, + app_state: &AppState, + cookie: &Cookie<'static>, + ) -> Result<Cookie<'static>, ()> { + match Administrator::authenticated_with_cookie(app_state, &cookie).await { + Ok(_) => return Ok(cookie.clone()), + Err(_) => return Err(()), + } + } + + /// The function that must be called in order to execute the verification. + /// Example : + /// ``` + /// #[post("/some-url")] + /// pub async fn some_service( + /// app_state: Data<AppState>, + /// middleware: Data<AuthenticatedAdminMiddleware<'_>>, + /// req: HttpRequest, + /// ) -> impl Responder { + /// if middleware.exec(&app_state, &req, None).await.is_err() { + /// return HttpResponse::Unauthorized().finish(); + /// } + /// ... Authenticated actions .... + /// } + /// ``` + pub async fn exec( + &self, + app_state: &AppState, + req: &HttpRequest, + auth_form_data: Option<Form<AdminAuthCredentials>>, + ) -> Result<Cookie<'static>, ()> { + let auth_cookie = req.cookie(self.cookie_name); + if let Some(form_data) = auth_form_data { + return self.try_auth_from_form_data(app_state, form_data).await; + } else if let Some(cookie) = auth_cookie { + return self.try_auth_from_auth_cookie(app_state, &cookie).await; + } else { + return Err(()); + } + } +} diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000000000000000000000000000000000000..6df3c2bbf1d8375ca1f8f94db9f87e36b472fbff --- /dev/null +++ b/src/model.rs @@ -0,0 +1,4 @@ +mod administrator; +mod article; +pub use administrator::*; +pub use article::*; diff --git a/src/model/administrator.rs b/src/model/administrator.rs new file mode 100644 index 0000000000000000000000000000000000000000..852881ee77b737185433d500a7b1b8229076914a --- /dev/null +++ b/src/model/administrator.rs @@ -0,0 +1,83 @@ +use crate::AppState; +use actix_web::http::Cookie; +use serde::{Deserialize, Serialize}; +use wither::{ + bson::{doc, oid::ObjectId}, + prelude::Model, +}; + +#[derive(Debug, Serialize, Deserialize)] +/// The data type that must sent by form data POST to authenticate an administrator. +pub struct AdminAuthCredentials { + pub username: String, + pub password: String, +} + +#[derive(Debug, Deserialize, Serialize, Model)] +#[model(index( + keys = r#"doc!{"email": 1, "username": 1}"#, + options = r#"doc!{"unique": true}"# +))] +/// An administrator is a user with registered authentication credentials access right to the admin-panel and has ability to perform admin actions such as gam review, moderation, etc. +pub struct Administrator { + #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] + pub id: Option<ObjectId>, + pub username: String, + pub password_hash: String, + pub auth_token: Option<String>, +} + +impl Administrator { + /// Creates an administrator with values for username and password. + /// The auth_token fields remains None as it must be created if the user authenticates itself with the provided credentials + /// The password is stored as password_hash, it is encrypted with the AppState::Encryption. + pub fn from_values(app_state: &AppState, username: String, password: String) -> Self { + Administrator { + id: None, + password_hash: app_state.encryption.encrypt(&password), + username, + auth_token: None, + } + } + + /// Performs authentication with form data <username, password>. + /// Returns a Result with either an authenticated Administrator instance or an error. + pub async fn authenticated( + app_state: &AppState, + credentials: AdminAuthCredentials, + ) -> Result<Self, ()> { + let filter_doc = doc! { + "password_hash": app_state.encryption.encrypt(&credentials.password), + "username": credentials.username + }; + + match Administrator::find_one(&app_state.db, filter_doc, None).await { + Ok(user_option) => match user_option { + Some(admin) => Ok(admin), + None => Err(()), + }, + Err(_) => Err(()), + } + } + + /// Performs authenticattion with auth cookie. The cookie value must match the Administrator auth_token value. + /// Returns a result with either the authenticated admin, or an empty Err. + pub async fn authenticated_with_cookie( + app_state: &AppState, + auth_cookie: &Cookie<'_>, + ) -> Result<Self, ()> { + let cookie_value = auth_cookie.value().to_string(); + + let filter_doc = doc! { + "auth_token": app_state.encryption.encrypt(&cookie_value), + }; + + match Administrator::find_one(&app_state.db, filter_doc, None).await { + Ok(user_option) => match user_option { + Some(admin) => Ok(admin), + None => Err(()), + }, + Err(_) => Err(()), + } + } +} diff --git a/src/model/article.rs b/src/model/article.rs new file mode 100644 index 0000000000000000000000000000000000000000..f96872206f99ff027fbfd8829363328b26d16f23 --- /dev/null +++ b/src/model/article.rs @@ -0,0 +1,54 @@ +#[cfg(test)] +use chrono::Utc; +use serde::{Deserialize, Serialize}; +use wither::{ + bson::{doc, oid::ObjectId, DateTime}, + prelude::Model, +}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ArticleDetail { + pub label: String, + pub value: String, +} + +#[derive(Debug, Serialize, Deserialize, Model, Clone)] +#[model(index(keys = r#"doc!{"title": 1}"#, options = r#"doc!{"unique": true}"#))] +pub struct Article { + #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] + pub id: Option<ObjectId>, + pub title: String, + pub subtitle: String, + pub date: Option<DateTime>, + pub body: String, + pub details: Vec<ArticleDetail>, + pub images: Vec<String>, + pub category: String, + pub locale: String, +} + +impl Article { + #[cfg(test)] + pub fn test_article() -> Self { + Article { + id: None, + title: "Test Article".to_string(), + subtitle: "An article for testing".to_string(), + date: Some(DateTime(Utc::now())), + body: "blablabla".to_string(), + details: vec![ + ArticleDetail { + label: "A label".to_string(), + value: "A value".to_string(), + }, + ArticleDetail { + label: "Another label".to_string(), + value: "Another value".to_string(), + }, + ], + images: vec!["an_image.png".to_string()], + category: "testing".to_string(), + locale: "fr".to_string(), + } + } +} diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000000000000000000000000000000000000..91376c0ac331b964e71130ef122c5047fb8a152c --- /dev/null +++ b/src/service.rs @@ -0,0 +1,4 @@ +mod admin_auth; +mod articles; +pub use admin_auth::*; +pub use articles::*; diff --git a/src/service/admin_auth.rs b/src/service/admin_auth.rs new file mode 100644 index 0000000000000000000000000000000000000000..f3313cd16bb1876f688fff7a398fbef42ab4be4c --- /dev/null +++ b/src/service/admin_auth.rs @@ -0,0 +1,122 @@ +use crate::{middleware::AuthenticatedAdminMiddleware, model::AdminAuthCredentials, AppState}; +use actix_web::{ + post, + web::{Data, Form}, + HttpMessage, HttpRequest, HttpResponse, Responder, +}; + +/// Performs administrator authentication from form data +/// If the authentication succeed, a cookie with an auth token is returned +/// If not, 401 is returned and if an auth cookie is found it is deleted. +#[post("/admin-auth")] +pub async fn admin_authentication<'a>( + app_state: Data<AppState>, + auth_mw: Data<AuthenticatedAdminMiddleware<'a>>, + req: HttpRequest, + form_data: Form<AdminAuthCredentials>, +) -> impl Responder { + let cookie_opt = auth_mw.exec(&app_state, &req, Some(form_data)).await; + match cookie_opt { + Ok(cookie) => HttpResponse::Accepted().cookie(cookie).finish(), + Err(_) => { + return match req.cookie(auth_mw.cookie_name) { + Some(c) => { + // Invalidate auth_cookie if auth failed in any way + HttpResponse::Unauthorized().del_cookie(&c).finish() + } + None => HttpResponse::Unauthorized().finish(), + }; + } + } +} + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + * _______ ______ ______ _______ *@@ + * |__ __@ | ____@ / ____@ |__ __@ *@@ + * | @ | @__ \_ @_ | @ *@@ + * | @ | __@ \ @_ | @ *@@ + * | @ | @___ ____\ @ | @ *@@ + * |__@ |______@ \______@ |__@ *@@ + * *@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@*/ + +#[cfg(test)] +mod test_admin_auth { + use super::*; + use crate::model::Administrator; + use actix_web::{ + http::{Method, StatusCode}, + test, + web::Data, + App, + }; + use futures::stream::StreamExt; + use wither::prelude::Model; + + #[tokio::test] + async fn test_admin_auth() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + let admin_user = Administrator::find(&app_state.db, None, None) + .await + .unwrap() + .next() + .await + .unwrap() + .unwrap(); // Get the first admin user we find. At least one should exist. + + let password = app_state.encryption.decrypt(&admin_user.password_hash); + let username = admin_user.username.to_owned(); + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(admin_authentication), + ) + .await; + + let req = test::TestRequest::with_uri("/admin-auth") + .method(Method::POST) + .set_form(&AdminAuthCredentials { username, password }) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::ACCEPTED); + } + + #[tokio::test] + async fn test_admin_auth_unauthorized() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(admin_authentication), + ) + .await; + + let req = test::TestRequest::with_uri("/admin-auth") + .method(Method::POST) + .set_form(&AdminAuthCredentials { + username: String::from("whatever"), + password: String::from("whatever"), + }) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); + } +} diff --git a/src/service/articles.rs b/src/service/articles.rs new file mode 100644 index 0000000000000000000000000000000000000000..3d5abc23619db42a21a9ed5055150c0707cf0fc4 --- /dev/null +++ b/src/service/articles.rs @@ -0,0 +1,608 @@ +use crate::{middleware::AuthenticatedAdminMiddleware, model::Article, AppState}; +use actix_web::{ + delete, get, post, put, + web::{Data, Form, Json, Path}, + HttpRequest, HttpResponse, Responder, +}; +use chrono::Utc; +use futures::stream::StreamExt; +use serde::{Deserialize, Serialize}; +use wither::{ + bson::{doc, oid::ObjectId, DateTime}, + mongodb::Collection, + prelude::Model, +}; + +#[derive(Deserialize, Serialize)] +pub struct ArticleTitleFormData { + pub title: String, +} + +fn get_collection(app_state: &AppState) -> Collection<Article> { + app_state.db.collection_with_type::<Article>("articles") +} + +#[post("/post-article")] +pub async fn post_article( + app_state: Data<AppState>, + article_data: Json<Article>, + middleware: Data<AuthenticatedAdminMiddleware<'_>>, + req: HttpRequest, +) -> impl Responder { + if middleware.exec(&app_state, &req, None).await.is_err() { + return HttpResponse::Unauthorized().finish(); + } + + let mut article_data = article_data.into_inner(); + article_data.date = Some(DateTime(Utc::now())); + + match get_collection(&app_state) + .insert_one(article_data, None) + .await + { + Ok(res) => HttpResponse::Created().json(res), + Err(e) => { + HttpResponse::InternalServerError().body(format!("Error inserting new article {:?}", e)) + } + } +} + +#[put("/update-article/{article_id}")] +pub async fn update_article( + app_state: Data<AppState>, + article_data: Json<Article>, + middleware: Data<AuthenticatedAdminMiddleware<'_>>, + article_id: Path<String>, + req: HttpRequest, +) -> impl Responder { + if middleware.exec(&app_state, &req, None).await.is_err() { + return HttpResponse::Unauthorized().finish(); + } + + let article_id = match ObjectId::with_string(&article_id.into_inner()) { + Ok(id) => id, + Err(_) => { + return HttpResponse::BadRequest() + .body("Failed to convert article_id to ObjectId. String may be malformed") + } + }; + + let mut article_data = article_data.into_inner(); + article_data.date = Some(DateTime(Utc::now())); + + match get_collection(&app_state) + .find_one_and_replace(doc! {"_id": &article_id}, article_data, None) + .await + { + Ok(res) => HttpResponse::Ok().json(res.unwrap()), + Err(_) => HttpResponse::InternalServerError().finish(), + } +} + +#[delete("/delete-article/{article_id}")] +pub async fn delete_article( + app_state: Data<AppState>, + middleware: Data<AuthenticatedAdminMiddleware<'_>>, + article_id: Path<String>, + req: HttpRequest, +) -> impl Responder { + if middleware.exec(&app_state, &req, None).await.is_err() { + return HttpResponse::Unauthorized().finish(); + } + + let article_id = match ObjectId::with_string(&article_id.into_inner()) { + Ok(id) => id, + Err(_) => { + return HttpResponse::BadRequest() + .body("Failed to convert article_id to ObjectId. String may be malformed") + } + }; + + match get_collection(&app_state) + .find_one_and_delete(doc! {"_id": &article_id}, None) + .await + { + Ok(_) => HttpResponse::Accepted().body("Article was deleted"), + Err(e) => HttpResponse::InternalServerError().body(&format!("{:?}", e)), + } +} + +#[get("/articles/{category}")] +pub async fn get_articles_by_category( + app_state: Data<AppState>, + category: Path<String>, +) -> impl Responder { + match get_collection(&app_state) + .find(doc! {"category": category.into_inner()}, None) + .await + { + Ok(mut cursor) => { + let mut results: Vec<Article> = Vec::new(); + + while let Some(result) = cursor.next().await { + match result { + Ok(article) => { + results.push(article); + } + Err(_) => { + return HttpResponse::InternalServerError().finish(); + } + } + } + HttpResponse::Ok().json(results) + } + Err(_) => HttpResponse::InternalServerError().finish(), + } +} + +#[get("/article/{article_id}")] +pub async fn get_article(app_state: Data<AppState>, article_id: Path<String>) -> impl Responder { + let article_id = match ObjectId::with_string(&article_id.into_inner()) { + Ok(id) => id, + Err(_) => { + return HttpResponse::BadRequest() + .body("Failed to convert article_id to ObjectId. String may be malformed") + } + }; + + match Article::find_one(&app_state.db, doc! {"_id":&article_id}, None).await { + Ok(art) => { + if art.is_none() { + return HttpResponse::NotFound().body("Article was not found"); + } + + HttpResponse::Ok().json(art) + } + Err(e) => HttpResponse::InternalServerError().body(format!("Database error: {:#?}", e)), + } +} + +#[post("/article-by-title")] +pub async fn get_article_by_title( + app_state: Data<AppState>, + form_data: Form<ArticleTitleFormData>, +) -> impl Responder { + let title = form_data.into_inner().title; + match Article::find_one(&app_state.db, doc! {"title":title}, None).await { + Ok(art) => { + if art.is_none() { + return HttpResponse::NotFound().body("Article was not found"); + } + + HttpResponse::Ok().json(art) + } + Err(e) => HttpResponse::InternalServerError().body(format!("Database error: {:#?}", e)), + } +} + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + * _______ ______ ______ _______ *@@ + * |__ __@ | ____@ / ____@ |__ __@ *@@ + * | @ | @__ \_ @_ | @ *@@ + * | @ | __@ \ @_ | @ *@@ + * | @ | @___ ____\ @ | @ *@@ + * |__@ |______@ \______@ |__@ *@@ + * *@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@*/ + +#[cfg(test)] +mod test_articles { + use super::*; + use crate::middleware::get_auth_cookie; + use crate::model::{AdminAuthCredentials, Administrator}; + use actix_web::{ + http::{Method, StatusCode}, + test, + web::Bytes, + App, + }; + use wither::bson::Bson; + + async fn insert_test_article( + app_state: &AppState, + test_article: Article, + ) -> Result<(ObjectId, String), String> { + let title = test_article.title.to_owned(); + match get_collection(&app_state) + .insert_one(test_article, None) + .await + { + Ok(inserted) => match inserted.inserted_id { + Bson::ObjectId(id) => Ok((id, title)), + _ => Err(String::from("Failed to parse inserted_id")), + }, + Err(e) => Err(format!("{:?}", e)), + } + } + + async fn delete_test_article( + app_state: &AppState, + article_id: &ObjectId, + ) -> Result<i64, String> { + match get_collection(&app_state) + .delete_one(doc! {"_id": article_id}, None) + .await + { + Ok(delete_result) => Ok(delete_result.deleted_count), + Err(e) => Err(format!("{:?}", e)), + } + } + + async fn get_authenticated_admin(app_state: &AppState) -> Administrator { + Administrator::authenticated( + app_state, + AdminAuthCredentials { + username: app_state.env.default_admin_username.to_owned(), + password: app_state.env.default_admin_password.to_owned(), + }, + ) + .await + .unwrap() + } + + #[tokio::test] + async fn test_post_article() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(post_article), + ) + .await; + + let article = Article::test_article(); + + let admin_user = get_authenticated_admin(&app_state).await; + + let req = test::TestRequest::with_uri("/post-article") + .method(Method::POST) + .header("Content-Type", "application/json") + .header("Accept", "text/html") + .cookie(get_auth_cookie( + "kuadrado-admin-auth", + app_state + .encryption + .decrypt(&admin_user.auth_token.unwrap()) + .to_owned(), + )) + .set_payload(Bytes::from(serde_json::to_string(&article).unwrap())) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::CREATED); + + let find_inserted = Article::find_one(&app_state.db, doc! {"title": &article.title}, None) + .await + .unwrap(); + + assert!(find_inserted.is_some()); + assert_eq!(find_inserted.unwrap().title, article.title); + + get_collection(&app_state) + .delete_one(doc! {"title": article.title}, None) + .await + .unwrap(); + } + + #[tokio::test] + async fn test_post_article_unauthorized() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(post_article), + ) + .await; + + let article = Article::test_article(); + + let req = test::TestRequest::with_uri("/post-article") + .method(Method::POST) + .header("Content-Type", "application/json") + .header("Accept", "text/html") + .cookie(get_auth_cookie( + "wrong-cookie", + app_state.encryption.random_ascii_lc_string(32), + )) + .set_payload(Bytes::from(serde_json::to_string(&article).unwrap())) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn test_update_article() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(update_article), + ) + .await; + + let mut article = Article::test_article(); + + let (article_id, _) = insert_test_article(&app_state, article.clone()) + .await + .unwrap(); + + article.title = "changed title".to_string(); + + let admin_user = get_authenticated_admin(&app_state).await; + + let req = test::TestRequest::with_uri( + format!("/update-article/{}", article_id.to_hex()).as_str(), + ) + .method(Method::PUT) + .header("Content-Type", "application/json") + .header("Accept", "text/html") + .cookie(get_auth_cookie( + "kuadrado-admin-auth", + app_state + .encryption + .decrypt(&admin_user.auth_token.unwrap()) + .to_owned(), + )) + .set_payload(Bytes::from(serde_json::to_string(&article).unwrap())) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::OK); + + let find_inserted = Article::find_one(&app_state.db, doc! {"_id": &article_id}, None) + .await + .unwrap(); + + assert!(find_inserted.is_some()); + assert_eq!(find_inserted.unwrap().title, "changed title"); + + let del_count = delete_test_article(&app_state, &article_id).await.unwrap(); + assert_eq!(del_count, 1); + } + + #[tokio::test] + async fn test_update_article_unauthorized() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(update_article), + ) + .await; + + let article = Article::test_article(); + + let req = test::TestRequest::with_uri( + format!("/update-article/{}", ObjectId::new().to_hex()).as_str(), + ) + .method(Method::PUT) + .header("Content-Type", "application/json") + .header("Accept", "text/html") + .cookie(get_auth_cookie( + "wrong-cookie", + app_state.encryption.random_ascii_lc_string(32), + )) + .set_payload(Bytes::from(serde_json::to_string(&article).unwrap())) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn test_delete_article() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(delete_article), + ) + .await; + + let article = Article::test_article(); + let (article_id, _) = insert_test_article(&app_state, article.clone()) + .await + .unwrap(); + + let admin_user = get_authenticated_admin(&app_state).await; + + let req = test::TestRequest::with_uri( + format!("/delete-article/{}", article_id.to_hex()).as_str(), + ) + .method(Method::DELETE) + .cookie(get_auth_cookie( + "kuadrado-admin-auth", + app_state + .encryption + .decrypt(&admin_user.auth_token.unwrap()) + .to_owned(), + )) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::ACCEPTED); + + let find_inserted = Article::find_one(&app_state.db, doc! {"_id": &article_id}, None) + .await + .unwrap(); + + assert!(find_inserted.is_none()); + } + + #[tokio::test] + async fn test_delete_article_unauthorized() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .service(delete_article), + ) + .await; + + let article = Article::test_article(); + + let req = test::TestRequest::with_uri( + format!("/delete-article/{}", ObjectId::new().to_hex()).as_str(), + ) + .method(Method::DELETE) + .cookie(get_auth_cookie( + "wrong-cookie", + app_state.encryption.random_ascii_lc_string(32), + )) + .set_payload(Bytes::from(serde_json::to_string(&article).unwrap())) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn test_get_article() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .service(get_article), + ) + .await; + + let article = Article::test_article(); + let (article_id, article_title) = insert_test_article(&app_state, article.clone()) + .await + .unwrap(); + + let req = test::TestRequest::with_uri(format!("/article/{}", article_id.to_hex()).as_str()) + .header("Accept", "application/json") + .method(Method::GET) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::OK); + + let result: Article = test::read_body_json(resp).await; + assert_eq!(result.title, article_title); + + let del_count = delete_test_article(&app_state, &article_id).await.unwrap(); + assert_eq!(del_count, 1); + } + + #[tokio::test] + async fn test_get_article_by_title() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .service(get_article_by_title), + ) + .await; + + let article = Article::test_article(); + let (article_id, article_title) = insert_test_article(&app_state, article.clone()) + .await + .unwrap(); + + let req = test::TestRequest::with_uri("/article-by-title") + .header("Accept", "application/json") + .method(Method::POST) + .set_form(&ArticleTitleFormData { + title: article_title.to_owned(), + }) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::OK); + let result: Article = test::read_body_json(resp).await; + assert_eq!(result.title, article_title); + + let del_count = delete_test_article(&app_state, &article_id).await.unwrap(); + assert_eq!(del_count, 1); + } + + #[tokio::test] + async fn test_get_articles_by_category() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .service(get_articles_by_category), + ) + .await; + + let article = Article::test_article(); + let (article_id, article_title) = insert_test_article(&app_state, article.clone()) + .await + .unwrap(); + + let req = test::TestRequest::with_uri("/articles/testing") + .header("Accept", "application/json") + .method(Method::GET) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + + assert_eq!(resp.status(), StatusCode::OK); + + let results: Vec<Article> = test::read_body_json(resp).await; + let find_inserted = results.iter().find(|&art| art.title == article_title); + assert!(find_inserted.is_some()); + + let del_count = delete_test_article(&app_state, &article_id).await.unwrap(); + assert_eq!(del_count, 1); + } +} diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000000000000000000000000000000000000..49f05b1507ab12596f6ecf282cd013923e1f10f3 --- /dev/null +++ b/src/view.rs @@ -0,0 +1,191 @@ +use crate::{ + middleware::AuthenticatedAdminMiddleware, view_resource::ViewResourceManager, AppState, +}; +use actix_web::{ + get, + web::{Data, Path}, + HttpRequest, Responder, +}; + +/// Returns the content of a ViewResource after retrieving it by name. +/// If the resource is not found (has not been registered), it returns the 404 ViewResource. +/// The regex matches uris with more than 3 characters so we don't match the /fr /es etc urls used for the websites translation directories +#[get("/{resource_name:.{3,}}")] +pub async fn get_view<'a>( + app_state: Data<AppState>, + resource_manager: Data<ViewResourceManager>, + auth_middleware: Data<AuthenticatedAdminMiddleware<'a>>, + req: HttpRequest, + resource_name: Path<String>, +) -> impl Responder { + resource_manager + .get_resource_as_http_response( + &app_state, + &auth_middleware, + &req, + None, + &resource_name.into_inner(), + ) + .await +} + +/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + * _______ ______ ______ _______ *@@ + * |__ __@ | ____@ / ____@ |__ __@ *@@ + * | @ | @__ \_ @_ | @ *@@ + * | @ | __@ \ @_ | @ *@@ + * | @ | @___ ____\ @ | @ *@@ + * |__@ |______@ \______@ |__@ *@@ + * *@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@@ + *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@*/ + +#[cfg(test)] +mod test_views { + use super::*; + use crate::{ + middleware::get_auth_cookie, + model::{AdminAuthCredentials, Administrator}, + view_resource::ViewResourceDescriptor, + }; + use actix_web::{ + http::StatusCode, + test, + web::{Bytes, Data}, + App, + }; + + fn get_views_manager() -> ViewResourceManager { + ViewResourceManager::with_views(vec![ + ViewResourceDescriptor { + path_str: "test-view", + index_file_name: "index.html", + resource_name: "test-view", + apply_auth_middleware: false, + }, + ViewResourceDescriptor { + path_str: "test-view-auth", + index_file_name: "index.html", + resource_name: "test-view-auth", + apply_auth_middleware: true, + }, + ]) + } + + async fn get_authenticated_admin(app_state: &AppState) -> Administrator { + Administrator::authenticated( + app_state, + AdminAuthCredentials { + username: app_state.env.default_admin_username.to_owned(), + password: app_state.env.default_admin_password.to_owned(), + }, + ) + .await + .unwrap() + } + + #[tokio::test] + async fn test_get_view() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .app_data(Data::new(get_views_manager())) + .service(get_view), + ) + .await; + + let req = test::TestRequest::with_uri("/test-view").to_request(); + let resp = test::call_service(&mut app, req).await; + assert_eq!(resp.status(), StatusCode::OK); + + let body = test::read_body(resp).await; + assert_eq!(body, Bytes::from("<h1>TEST</h1>")); + } + + #[tokio::test] + async fn test_get_view_auth() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .app_data(Data::new(get_views_manager())) + .service(get_view), + ) + .await; + let admin_user = get_authenticated_admin(&app_state).await; + + let req = test::TestRequest::with_uri("/test-view-auth") + .cookie(get_auth_cookie( + "kuadrado-admin-auth", + app_state + .encryption + .decrypt(&admin_user.auth_token.unwrap()) + .to_owned(), + )) + .to_request(); + + let resp = test::call_service(&mut app, req).await; + assert_eq!(resp.status(), StatusCode::OK); + + let body = test::read_body(resp).await; + assert_eq!(body, Bytes::from("<h1>TEST AUTH</h1>")); + } + + #[tokio::test] + async fn test_get_view_auth_unauthorized() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .app_data(Data::new(get_views_manager())) + .service(get_view), + ) + .await; + + let req = test::TestRequest::with_uri("/test-view-auth").to_request(); + let resp = test::call_service(&mut app, req).await; + assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn test_get_view_not_found() { + dotenv::dotenv().ok(); + + let app_state = AppState::for_test().await; + + let mut app = test::init_service( + App::new() + .app_data(Data::new(app_state.clone())) + .app_data(Data::new(AuthenticatedAdminMiddleware::new( + "kuadrado-admin-auth", + ))) + .app_data(Data::new(get_views_manager())) + .service(get_view), + ) + .await; + + let req = test::TestRequest::with_uri("/whatever").to_request(); + let resp = test::call_service(&mut app, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } +} diff --git a/src/view_resource.rs b/src/view_resource.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a4449032bd7467c0b05548c05d6304ed162a62a --- /dev/null +++ b/src/view_resource.rs @@ -0,0 +1,156 @@ +use crate::{ + middleware::AuthenticatedAdminMiddleware, + model::AdminAuthCredentials, + // view_resource::{ViewResource, ViewResourceDescriptor}, + AppState, +}; +use actix_web::{web::Form, HttpMessage, HttpRequest, HttpResponse}; +use std::{env::var as env_var, fs::read_to_string as file_to_string, path::PathBuf}; + +#[derive(Debug, Clone)] +/// Loads a static resource data allowing it to be served by the get_view service. +/// It holds a name, allowing the resource to be retrived by name, +/// a content which can be any text content stored in a string (like an html document), +/// a path to the directory of the actual static resource, and a boolean which indicates wether +/// or not an authentication verification should be applied. +pub struct ViewResource { + pub name: String, + pub string_contents: String, + pub dir_path: PathBuf, + pub apply_auth_middleware: bool, +} + +#[derive(Debug, Clone)] +/// Defines the values that will be used to construct a ViewResource. +/// It must be passed to the AppViewResourceManager for resource registration +pub struct ViewResourceDescriptor<'a> { + pub path_str: &'a str, + pub index_file_name: &'a str, + pub resource_name: &'a str, + pub apply_auth_middleware: bool, +} + +#[derive(Debug, Clone)] +/// A structure reponsible of registering and retrieving static resources. +pub struct ViewResourceManager { + resources: Vec<ViewResource>, +} + +impl ViewResourceManager { + pub fn new() -> Self { + ViewResourceManager { resources: vec![] } + } + + /// Calls the constructor and registers the resources described as argument before returning the instance + pub fn with_views(resource_descriptors: Vec<ViewResourceDescriptor>) -> Self { + let mut instance = Self::new(); + instance.register_batch(resource_descriptors); + instance + } + + /// Registers a new static resource in the instance. + /// The path provided in the argument must point to an existing file + pub fn register(&mut self, desc: ViewResourceDescriptor) { + let static_dir = std::path::PathBuf::from( + env_var("RESOURCES_DIR").expect("RESOURCES_DIR is not defined"), + ) + .join("public/views"); + + let dir_path = static_dir.join(desc.path_str); + + let path: PathBuf = format!("{}/{}", dir_path.to_str().unwrap(), desc.index_file_name) + .parse() + .expect(&format!( + "Failed to pare resource index file path {:?}", + desc.index_file_name + )); + + let string_contents = file_to_string(path).unwrap(); + + &self.resources.push(ViewResource { + name: desc.resource_name.to_string(), + dir_path, + string_contents, + apply_auth_middleware: desc.apply_auth_middleware, + }); + } + + /// Registers a collection of multiple resources. + pub fn register_batch(&mut self, resource_descriptors: Vec<ViewResourceDescriptor>) { + for desc in resource_descriptors.iter() { + self.register(desc.clone()); + } + } + + /// Retrieves a resource by name and returns a reference to it or None. + pub fn get_resource(&self, name: &str) -> Option<&ViewResource> { + self.resources.iter().find(|res| res.name == name) + } + + /// Retrieves a resource by name and returns it as an http response. + /// This can be returned as it by a service. + pub async fn get_resource_as_http_response<'a>( + &self, + app_state: &AppState, + auth_middleware: &AuthenticatedAdminMiddleware<'a>, + req: &HttpRequest, + auth_data: Option<Form<AdminAuthCredentials>>, + resource_name: &str, + ) -> HttpResponse { + match self.get_resource(resource_name) { + Some(res) => { + if res.apply_auth_middleware { + let auth_cookie = auth_middleware.exec(app_state, req, auth_data).await; + if auth_cookie.is_err() { + let unauthorized_view = match self.get_resource("unauthorized") { + Some(res_404) => res_404.string_contents.to_string(), + None => { + println!("WARNING: missing Unauthorized view resource"); + + " + <h1>Unauthorized</h1> + <p>You must login as an administrator to access this page</p> + <a href='/v/admin-login'>Login page</a> + " + .to_string() + } + }; + + let mut response_builder = HttpResponse::Unauthorized(); + + return match req.cookie(auth_middleware.cookie_name) { + Some(cookie) => { + // Invalidate auth_cookie if auth failed in any way + response_builder + .del_cookie(&cookie) + .content_type("text/html") + .body(unauthorized_view) + } + None => response_builder + .content_type("text/html") + .body(unauthorized_view), + }; + } else { + return HttpResponse::Ok() + .content_type("text/html") + .cookie(auth_cookie.unwrap()) + .body(&res.string_contents); + } + } + + HttpResponse::Ok() + .content_type("text/html") + .body(&res.string_contents) + } + None => match self.get_resource("404") { + Some(res_404) => HttpResponse::NotFound() + .content_type("text/html") + .body(&res_404.string_contents), + None => { + println!("WARNING: missing 404 view resource"); + HttpResponse::NotFound().finish() + } + }, + } + } +}