Display listing
This commit is contained in:
parent
15363fc19d
commit
bf6cb6f587
7
.env
7
.env
@ -1,8 +1,12 @@
|
||||
DATABASE_URL=postgres://postgres@localhost/bazzar
|
||||
|
||||
PASS_SALT=18CHwV7eGFAea16z+qMKZg
|
||||
RUST_LOG=debug
|
||||
SESSION_SECRET="NEPJs#8jjn8SK8GC7QEC^*P844UgsyEbQB8mRWXkT%3mPrwewZoc25MMby9H#R*w2KzaQgMkk#Pif$kxrLy*N5L!Ch%jxbWoa%gb"
|
||||
JWT_SECRET="42^iFq&ZnQbUf!hwGWXd&CpyY6QQyJmkPU%esFCvne5&Ejcb3nJ4&GyHZp!MArZLf^9*5c6!!VgM$iZ8T%d#&bWTi&xbZk2S@4RN"
|
||||
SIGNATURE=David
|
||||
SERVICE_NAME="BaZZaR develop"
|
||||
|
||||
PGDATESTYLE=
|
||||
|
||||
SENDGRID_SECRET=SG.CUWRM-eoQfGJNqSU2bbwkg.NW5aBy5vZueCSOwIIyWUBqq5USChGiwAFrWzreBKvOU
|
||||
@ -13,7 +17,8 @@ PAYU_CLIENT_ID="145227"
|
||||
PAYU_CLIENT_SECRET="12f071174cb7eb79d4aac5bc2f07563f"
|
||||
PAYU_CLIENT_MERCHANT_ID=300746
|
||||
|
||||
WEB_HOST=https://bazzar.ita-prog.pl
|
||||
#WEB_HOST=https://bazzar.ita-prog.pl
|
||||
WEB_HOST=0.0.0.0
|
||||
|
||||
FILES_PUBLIC_PATH=/files
|
||||
FILES_LOCAL_PATH=./tmp
|
||||
|
214
Cargo.lock
generated
214
Cargo.lock
generated
@ -113,7 +113,6 @@ dependencies = [
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio-uring 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -240,7 +239,6 @@ dependencies = [
|
||||
"actix-macros",
|
||||
"futures-core",
|
||||
"tokio",
|
||||
"tokio-uring 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -258,7 +256,6 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-uring 0.3.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -1259,7 +1256,10 @@ dependencies = [
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"sendgrid",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tinytemplate",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@ -1849,7 +1849,7 @@ dependencies = [
|
||||
"hyper",
|
||||
"rustls 0.20.4",
|
||||
"tokio",
|
||||
"tokio-rustls 0.23.3",
|
||||
"tokio-rustls 0.23.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1920,16 +1920,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.0"
|
||||
@ -2055,9 +2045,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "local-channel"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f"
|
||||
checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -2067,9 +2057,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "local-waker"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "902eb695eb0591864543cbfbf6d742510642a605a61fc5e97fe6ceb5a30ac4fb"
|
||||
checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@ -2129,9 +2119,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@ -2208,25 +2198,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
|
||||
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2290,15 +2269,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.3.3"
|
||||
@ -2322,9 +2292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@ -2341,9 +2311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -2370,9 +2340,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.28.3"
|
||||
version = "0.28.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456"
|
||||
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2397,18 +2367,30 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.38"
|
||||
version = "0.10.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
|
||||
checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
@ -2417,9 +2399,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.72"
|
||||
version = "0.9.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@ -2501,7 +2483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.2",
|
||||
"parking_lot_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2520,9 +2502,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@ -2754,9 +2736,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@ -2923,7 +2905,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls 0.23.3",
|
||||
"tokio-rustls 0.23.4",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@ -2958,6 +2940,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal_macros"
|
||||
version = "1.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c70be9367d4bc095d10b48d41b819d09ed4dafc528765a144d32ed1d530654"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"rust_decimal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
@ -2979,7 +2971,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.7",
|
||||
"semver 1.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3016,6 +3008,16 @@ dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusty-money"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b28f881005eac7ad8d46b6f075da5f322bd7f4f83a38720fc069694ddadd683"
|
||||
dependencies = [
|
||||
"rust_decimal",
|
||||
"rust_decimal_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
@ -3041,12 +3043,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@ -3153,9 +3149,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.7"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@ -3337,12 +3333,12 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
|
||||
checksum = "ca642ba17f8b2995138b1d7711829c92e98c0a25ea019de790f4f09279c4e296"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3709,6 +3705,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
@ -3751,9 +3757,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.18.1"
|
||||
version = "1.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc"
|
||||
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@ -3803,9 +3809,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.23.3"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e"
|
||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||
dependencies = [
|
||||
"rustls 0.20.4",
|
||||
"tokio",
|
||||
@ -3823,34 +3829,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-uring"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "062a33613d97344c5054d9635b35beb14492b66d9aa4bdbf21ecde1682d256df"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"scoped-tls",
|
||||
"slab",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-uring"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3ad494f39874984d990ade7f6319dafbcd3301ff0b1841f8a55a1ebb3e742c8"
|
||||
dependencies = [
|
||||
"io-uring",
|
||||
"libc",
|
||||
"scoped-tls",
|
||||
"slab",
|
||||
"socket2",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.6.9"
|
||||
@ -4052,9 +4030,9 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
@ -4270,6 +4248,8 @@ dependencies = [
|
||||
"chrono",
|
||||
"gloo-timers",
|
||||
"indexmap",
|
||||
"model",
|
||||
"rusty-money",
|
||||
"seed",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
@ -4378,9 +4358,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
|
||||
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
@ -4391,33 +4371,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
|
@ -20,7 +20,7 @@ where
|
||||
{
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, local_path, file_name
|
||||
SELECT id, local_path, file_name, unique_name
|
||||
FROM photos
|
||||
"#,
|
||||
)
|
||||
@ -39,6 +39,7 @@ pub struct CreatePhoto {
|
||||
pub local_path: model::LocalPath,
|
||||
/// Only file name, this part should be also included in `local_path`
|
||||
pub file_name: model::FileName,
|
||||
pub unique_name: model::UniqueName,
|
||||
}
|
||||
|
||||
crate::db_async_handler!(CreatePhoto, create_photo, model::Photo, inner_create_photo);
|
||||
@ -49,13 +50,14 @@ where
|
||||
{
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO photos (file_name, local_path)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, local_path, file_name
|
||||
INSERT INTO photos (file_name, local_path, unique_name)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, local_path, file_name, unique_name
|
||||
"#,
|
||||
)
|
||||
.bind(msg.file_name)
|
||||
.bind(msg.local_path)
|
||||
.bind(msg.unique_name)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@ -101,7 +103,7 @@ pub(crate) async fn photos_for_products(
|
||||
) {
|
||||
log::debug!("scoped product ids {:?}", ids);
|
||||
let query: String = r#"
|
||||
SELECT photos.id, photos.local_path, photos.file_name, product_photos.product_id
|
||||
SELECT photos.id, photos.local_path, photos.file_name, product_photos.product_id, photos.unique_name
|
||||
FROM photos
|
||||
INNER JOIN product_photos
|
||||
ON photos.id = product_photos.photo_id
|
||||
|
@ -19,3 +19,8 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
|
||||
tinytemplate = { version = "1.2.1" }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = [] }
|
||||
|
34
actors/email_manager/assets/reset-password.html
Normal file
34
actors/email_manager/assets/reset-password.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Reset password</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
{style}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="debug-screens">
|
||||
<div class="container p-8 mx-auto mt-12 bg-white">
|
||||
<div class="rounded-lg">
|
||||
<h1 class="mb-3 text-xl font-semibold tracking-tight text-sky-600">Trouble signing in?</h1>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">Resetting your password is easy.</p>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">Just press the button below and follow the instructions. We’ll have you up and running in no time.</p>
|
||||
<p>
|
||||
<a
|
||||
class="px-4 py-2 inline-block text-white bg-blue-600 border border-transparent rounded-r hover:bg-blue-700"
|
||||
href="{ url }"
|
||||
>
|
||||
Reset Password
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">If you did not make this request then please ignore this email.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
9
actors/email_manager/assets/style.css
Normal file
9
actors/email_manager/assets/style.css
Normal file
@ -0,0 +1,9 @@
|
||||
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content:''}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}
|
||||
sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}
|
||||
input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding-top:.5rem;padding-right:.75rem;padding-bottom:.5rem;padding-left:.75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[type=text]:focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}
|
||||
input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}[multiple]{background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;color-adjust:unset;print-color-adjust:unset}
|
||||
[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
|
||||
[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}
|
||||
*,:before,:after{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scroll-snap-strictness:proximity;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.debug-screens:before{position:fixed;z-index:2147483647;bottom:0;left:0;padding:.3333333em .5em;font-size:12px;line-height:1;font-family:sans-serif;background-color:#000;color:#fff;box-shadow:0 0 0 1px #fff;content:'screen: _'}@media (min-width:640px){.debug-screens:before{content:'screen: sm'}}@media (min-width:768px){.debug-screens:before{content:'screen: md'}
|
||||
}@media (min-width:1024px){.debug-screens:before{content:'screen: lg'}}@media (min-width:1280px){.debug-screens:before{content:'screen: xl'}}@media (min-width:1536px){.debug-screens:before{content:'screen: 2xl'}}.mx-auto{margin-left:auto;margin-right:auto}.mt-12{margin-top:3rem}.mb-3{margin-bottom:.75rem}.mb-2{margin-bottom:.5rem}.inline-block{display:inline-block}.max-w-sm{max-width:24rem}.rounded-lg{border-radius:.5rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.border{border-width:1px}.border-transparent{border-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-semibold{font-weight:600}.leading-normal{line-height:1.5}.tracking-tight{letter-spacing:-.025em}
|
||||
.text-sky-600{--tw-text-opacity:1;color:rgb(2 132 199/var(--tw-text-opacity))}.text-sky-900{--tw-text-opacity:1;color:rgb(12 74 110/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.shadow-md{--tw-shadow:0 4px 6px -1px rgb(0 0 0/0.1),0 2px 4px -2px rgb(0 0 0/0.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sky-600{--tw-shadow-color:#0284c7;--tw-shadow:var(--tw-shadow-colored)}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}
|
27
actors/email_manager/assets/welcome.html
Normal file
27
actors/email_manager/assets/welcome.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Welcome to { service_name }</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
{style}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section class="debug-screens">
|
||||
<div class="container p-8 mx-auto mt-12 bg-white">
|
||||
<div class="rounded-lg">
|
||||
<h1 class="mb-3 text-xl font-semibold tracking-tight text-sky-600">Hi { login }</h1>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">
|
||||
Welcome to {service_name} – we’re excited to have you on board and we’d love to say thank you on behalf
|
||||
of our whole company for chosing us.
|
||||
</p>
|
||||
<p class="mb-2 leading-normal text-sky-900">Take care,</p>
|
||||
<p class="mb-2 leading-normal text-sky-900">{signature}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use config::SharedAppConfig;
|
||||
use serde::Serialize;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mail_async_handler {
|
||||
@ -17,8 +18,13 @@ macro_rules! mail_async_handler {
|
||||
};
|
||||
}
|
||||
|
||||
static STYLE: &str = include_str!("../assets/style.css");
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {}
|
||||
pub enum Error {
|
||||
#[error("Failed to render reset password template")]
|
||||
ResetPassTemplate,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@ -31,6 +37,7 @@ pub struct EmailManager(Arc<Inner>);
|
||||
pub(crate) struct Inner {
|
||||
config: SharedAppConfig,
|
||||
send_grid: sendgrid::SGClient,
|
||||
template: Arc<tinytemplate::TinyTemplate<'static>>,
|
||||
}
|
||||
|
||||
impl actix::Actor for EmailManager {
|
||||
@ -39,36 +46,143 @@ impl actix::Actor for EmailManager {
|
||||
|
||||
impl EmailManager {
|
||||
pub fn build(config: SharedAppConfig) -> Result<Self> {
|
||||
let template = {
|
||||
use tinytemplate::*;
|
||||
let mut t = TinyTemplate::new();
|
||||
t.add_template(
|
||||
"reset-password",
|
||||
include_str!("../assets/reset-password.html"),
|
||||
)
|
||||
.expect("Failed to load e-mail template reset-password");
|
||||
t.add_template("welcome", include_str!("../assets/welcome.html"))
|
||||
.expect("Failed to load e-mail template welcome");
|
||||
t
|
||||
};
|
||||
|
||||
Ok(Self(Arc::new(Inner {
|
||||
config: config.clone(),
|
||||
send_grid: sendgrid::SGClient::new(config.lock().mail().sendgrid_secret()),
|
||||
template: Arc::new(template),
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<SendState>")]
|
||||
#[rtype(result = "Result<()>")]
|
||||
pub struct TestMail {
|
||||
pub receiver: model::Email,
|
||||
}
|
||||
|
||||
mail_async_handler!(TestMail, test_mail, SendState);
|
||||
mail_async_handler!(TestMail, test_mail, ());
|
||||
|
||||
pub(crate) async fn test_mail(msg: TestMail, inner: Arc<Inner>) -> Result<()> {
|
||||
welcome(
|
||||
Welcome {
|
||||
login: model::Login::new("Test User"),
|
||||
email: msg.receiver,
|
||||
},
|
||||
inner,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(actix::Message, Debug)]
|
||||
#[rtype(result = "Result<()>")]
|
||||
pub struct ResetPassword {
|
||||
pub login: model::Login,
|
||||
pub email: model::Email,
|
||||
pub reset_token: model::ResetToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ResetPasswordContext {
|
||||
url: String,
|
||||
style: &'static str,
|
||||
}
|
||||
|
||||
mail_async_handler!(ResetPassword, reset_password, ());
|
||||
|
||||
pub(crate) async fn reset_password(msg: ResetPassword, inner: Arc<Inner>) -> Result<()> {
|
||||
let host = { inner.config.lock().web().host() };
|
||||
let context = ResetPasswordContext {
|
||||
url: format!(
|
||||
"{host}/reset-password/{reset_token}",
|
||||
reset_token = msg.reset_token
|
||||
),
|
||||
style: STYLE,
|
||||
};
|
||||
let html = inner
|
||||
.template
|
||||
.render("reset-password", &context)
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
Error::ResetPassTemplate
|
||||
})?;
|
||||
|
||||
pub(crate) async fn test_mail(msg: TestMail, inner: Arc<Inner>) -> Result<SendState> {
|
||||
let status = inner
|
||||
.send_grid
|
||||
.send(
|
||||
sendgrid::Mail::new()
|
||||
.add_to((msg.receiver.as_str(), "User").into())
|
||||
.add_to((msg.email.as_str(), msg.login.as_str()).into())
|
||||
.add_from(&inner.config.lock().mail().smtp_from())
|
||||
.add_subject("Test e-mail")
|
||||
.add_html("<h1>Test e-mail</h1>")
|
||||
.add_subject("Reset Password")
|
||||
.add_html(html.as_str())
|
||||
.build(),
|
||||
)
|
||||
.await;
|
||||
|
||||
log::debug!("{:?}", status);
|
||||
Ok(SendState {
|
||||
success: status.is_ok(),
|
||||
})
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<()>")]
|
||||
pub struct Welcome {
|
||||
pub login: model::Login,
|
||||
pub email: model::Email,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WelcomeContext {
|
||||
login: model::Login,
|
||||
service_name: String,
|
||||
signature: String,
|
||||
style: &'static str,
|
||||
}
|
||||
|
||||
mail_async_handler!(Welcome, welcome, ());
|
||||
|
||||
pub(crate) async fn welcome(msg: Welcome, inner: Arc<Inner>) -> Result<()> {
|
||||
let (signature, service_name) = {
|
||||
let l = inner.config.lock();
|
||||
let w = l.web();
|
||||
(w.signature(), w.service_name())
|
||||
};
|
||||
let context = WelcomeContext {
|
||||
login: msg.login.clone(),
|
||||
service_name,
|
||||
signature,
|
||||
style: STYLE,
|
||||
};
|
||||
let html = inner.template.render("welcome", &context).map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
Error::ResetPassTemplate
|
||||
})?;
|
||||
|
||||
let status = inner
|
||||
.send_grid
|
||||
.send(
|
||||
sendgrid::Mail::new()
|
||||
.add_to((msg.email.as_str(), msg.login.as_str()).into())
|
||||
.add_from(&inner.config.lock().mail().smtp_from())
|
||||
.add_subject("Welcome")
|
||||
.add_html(html.as_str())
|
||||
.build(),
|
||||
)
|
||||
.await;
|
||||
|
||||
log::debug!("{:?}", status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::ffi::OsStr;
|
||||
use std::io::Write;
|
||||
|
||||
use config::SharedAppConfig;
|
||||
use model::{FileName, LocalPath};
|
||||
use model::{FileName, LocalPath, UniqueName};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fs_async_handler {
|
||||
@ -147,9 +147,10 @@ pub(crate) async fn remove_file(msg: RemoveFile, config: SharedAppConfig) -> Res
|
||||
|
||||
pub struct WriteResult {
|
||||
/// Unique file name created with UUID and original file extension
|
||||
pub unique_name: FileName,
|
||||
pub unique_name: UniqueName,
|
||||
/// Path to file in local storage
|
||||
pub local_path: LocalPath,
|
||||
pub file_name: FileName,
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
@ -218,7 +219,8 @@ pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Resul
|
||||
log::debug!("File {:?} successfully written", unique_name);
|
||||
|
||||
Ok(WriteResult {
|
||||
unique_name: FileName::from(unique_name),
|
||||
file_name: FileName::new(file_name),
|
||||
unique_name: UniqueName::new(unique_name),
|
||||
local_path: LocalPath::from(path.to_str().unwrap_or_default().to_string()),
|
||||
})
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ actix-identity = { version = "0.4", features = [] }
|
||||
actix-web-opentelemetry = { version = "0.12", features = [] }
|
||||
actix-session = { version = "0.6", features = ["actix-redis", "redis-actor-session"] }
|
||||
actix-redis = { version = "0.11", features = [] }
|
||||
actix-files = { version = "0.6", features = ["experimental-io-uring"] }
|
||||
actix-files = { version = "0.6", features = [] }
|
||||
actix-multipart = { version = "0.4", features = [] }
|
||||
|
||||
gumdrop = { version = "0.8", features = [] }
|
||||
|
@ -88,6 +88,7 @@ async fn server(opts: ServerOpts) -> Result<()> {
|
||||
.service({
|
||||
let l = app_config.lock();
|
||||
actix_files::Files::new(&l.files().public_path(), l.files().local_path())
|
||||
.use_etag(true)
|
||||
})
|
||||
.default_service(actix_web::web::to(actix_web::HttpResponse::Ok))
|
||||
})
|
||||
@ -181,20 +182,14 @@ async fn test_mailer(opts: TestMailerOpts) -> Result<()> {
|
||||
let manager = email_manager::EmailManager::build(config)
|
||||
.expect("Invalid email manager config")
|
||||
.start();
|
||||
if manager
|
||||
manager
|
||||
.send(TestMail {
|
||||
receiver: opts.receiver.expect("e-mail address is required"),
|
||||
})
|
||||
.await
|
||||
.expect("Failed to execute actor")
|
||||
.expect("Failed to send email")
|
||||
.success
|
||||
{
|
||||
println!("Success!");
|
||||
} else {
|
||||
eprintln!("Failure!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
.expect("Failed to send email");
|
||||
println!("Success!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::Database;
|
||||
use model::{
|
||||
api, Days, Price, ProductCategory, ProductId, ProductLongDesc, ProductName, ProductShortDesc,
|
||||
@ -18,9 +19,15 @@ use crate::{admin_send_db, routes};
|
||||
async fn products(
|
||||
session: Session,
|
||||
db: Data<Addr<Database>>,
|
||||
config: Data<SharedAppConfig>,
|
||||
) -> routes::Result<Json<api::Products>> {
|
||||
session.require_admin()?;
|
||||
|
||||
let public_path = {
|
||||
let l = config.lock();
|
||||
l.files().public_path()
|
||||
};
|
||||
|
||||
let db = db.into_inner();
|
||||
|
||||
let products = admin_send_db!(db, database_manager::AllProducts);
|
||||
@ -30,7 +37,7 @@ async fn products(
|
||||
product_ids: products.iter().map(|p| p.id).collect()
|
||||
}
|
||||
);
|
||||
Ok(Json((products, photos).into()))
|
||||
Ok(Json((products, photos, public_path).into()))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -64,9 +64,10 @@ async fn upload_product_image(
|
||||
let write = async {
|
||||
let fs_manager::WriteResult {
|
||||
local_path,
|
||||
unique_name: file_name,
|
||||
unique_name,
|
||||
file_name,
|
||||
} = match fs.send(msg).await {
|
||||
Ok(Ok(file_name)) => file_name,
|
||||
Ok(Ok(res)) => res,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e}");
|
||||
return Err(UploadError::FileStreamBroken);
|
||||
@ -80,7 +81,8 @@ async fn upload_product_image(
|
||||
db,
|
||||
database_manager::CreatePhoto {
|
||||
local_path,
|
||||
file_name
|
||||
file_name,
|
||||
unique_name
|
||||
},
|
||||
UploadError::DbSave
|
||||
);
|
||||
|
@ -12,8 +12,15 @@ use crate::routes::{self, Result};
|
||||
use crate::{public_send_db, Login, Password};
|
||||
|
||||
#[get("/products")]
|
||||
async fn products(db: Data<Addr<Database>>) -> Result<Json<api::Products>> {
|
||||
async fn products(
|
||||
db: Data<Addr<Database>>,
|
||||
config: Data<SharedAppConfig>,
|
||||
) -> Result<Json<api::Products>> {
|
||||
let db = db.into_inner();
|
||||
let public_path = {
|
||||
let l = config.lock();
|
||||
l.files().public_path()
|
||||
};
|
||||
|
||||
let products: Vec<model::Product> = public_send_db!(owned, db, database_manager::AllProducts);
|
||||
let photos: Vec<model::ProductLinkedPhoto> = public_send_db!(
|
||||
@ -23,7 +30,7 @@ async fn products(db: Data<Addr<Database>>) -> Result<Json<api::Products>> {
|
||||
product_ids: products.iter().map(|p| p.id).collect()
|
||||
}
|
||||
);
|
||||
Ok(Json((products, photos).into()))
|
||||
Ok(Json((products, photos, public_path).into()))
|
||||
}
|
||||
|
||||
#[get("/stocks")]
|
||||
|
2
migrations/20220509124526_add_unique_name_to_files.sql
Normal file
2
migrations/20220509124526_add_unique_name_to_files.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE photos
|
||||
ADD COLUMN unique_name TEXT NOT NULL DEFAULT gen_random_uuid() :: text UNIQUE;
|
1
migrations/20220509133117_update_photos.sql
Normal file
1
migrations/20220509133117_update_photos.sql
Normal file
@ -0,0 +1 @@
|
||||
update photos set unique_name = substring(local_path, 7);
|
@ -46,7 +46,9 @@ pub struct PaymentConfig {
|
||||
payu_client_secret: Option<pay_u::ClientSecret>,
|
||||
/// Create payu account and copy here merchant id
|
||||
payu_client_merchant_id: Option<pay_u::MerchantPosId>,
|
||||
currency: Option<String>,
|
||||
/// Allow customers to pay on site
|
||||
#[serde(default)]
|
||||
optional_payment: bool,
|
||||
}
|
||||
|
||||
@ -62,6 +64,7 @@ impl Example for PaymentConfig {
|
||||
/// Create payu account and copy here merchant id
|
||||
payu_client_merchant_id: Some(pay_u::MerchantPosId::from(0)),
|
||||
/// Allow customers to pay on site
|
||||
currency: None,
|
||||
optional_payment: true,
|
||||
}
|
||||
}
|
||||
@ -104,6 +107,14 @@ impl PaymentConfig {
|
||||
"payment config payu_client_merchant_id nor PAYU_CLIENT_MERCHANT_ID env was given",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn currency(&self) -> String {
|
||||
self.currency
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("CURRENCY").ok())
|
||||
.unwrap_or_else(|| "PLN".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
@ -119,17 +130,23 @@ pub struct WebConfig {
|
||||
jwt_secret: Option<String>,
|
||||
bind: Option<String>,
|
||||
port: Option<u16>,
|
||||
/// Name used in email signature
|
||||
signature: Option<String>,
|
||||
/// Shop name
|
||||
service_name: Option<String>,
|
||||
}
|
||||
|
||||
impl Example for WebConfig {
|
||||
fn example() -> Self {
|
||||
Self {
|
||||
host: Some(String::from("https://your.comain.com")),
|
||||
pass_salt: Some(String::from("Generate it with bazzar generate-hash")),
|
||||
session_secret: Some(String::from("100 characters long random string")),
|
||||
jwt_secret: Some(String::from("100 characters long random string")),
|
||||
bind: Some(String::from("0.0.0.0")),
|
||||
host: Some("https://your.comain.com".into()),
|
||||
pass_salt: Some("Generate it with bazzar generate-hash".into()),
|
||||
session_secret: Some("100 characters long random string".into()),
|
||||
jwt_secret: Some("100 characters long random string".into()),
|
||||
bind: Some("0.0.0.0".into()),
|
||||
port: Some(8080),
|
||||
signature: Some("John Doe".into()),
|
||||
service_name: Some("bazzar".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,6 +215,22 @@ impl WebConfig {
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
self.port = Some(port);
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> String {
|
||||
self.signature
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("SIGNATURE").ok())
|
||||
.expect("Web config signature nor SIGNATURE env was given")
|
||||
}
|
||||
|
||||
pub fn service_name(&self) -> String {
|
||||
self.service_name
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("SERVICE_NAME").ok())
|
||||
.expect("Web config service_name nor SERVICE_NAME env was given")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
@ -210,15 +243,11 @@ pub struct MailConfig {
|
||||
impl Example for MailConfig {
|
||||
fn example() -> Self {
|
||||
Self {
|
||||
sendgrid_secret: Some(String::from(
|
||||
"Create sendgrid account and copy credentials here",
|
||||
)),
|
||||
sendgrid_api_key: Some(String::from(
|
||||
"Create sendgrid account and copy credentials here",
|
||||
)),
|
||||
smtp_from: Some(String::from(
|
||||
"Valid sendgrid authorized email address. Example: contact@example.com",
|
||||
)),
|
||||
sendgrid_secret: Some("Create sendgrid account and copy credentials here".into()),
|
||||
sendgrid_api_key: Some("Create sendgrid account and copy credentials here".into()),
|
||||
smtp_from: Some(
|
||||
"Valid sendgrid authorized email address. Example: contact@example.com".into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -257,7 +286,7 @@ pub struct DatabaseConfig {
|
||||
impl Example for DatabaseConfig {
|
||||
fn example() -> Self {
|
||||
Self {
|
||||
url: Some(String::from("postgres://postgres@localhost/bazzar")),
|
||||
url: Some("postgres://postgres@localhost/bazzar".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,6 +311,7 @@ pub struct SearchConfig {
|
||||
sonic_search_pass: Option<String>,
|
||||
sonic_ingest_addr: Option<String>,
|
||||
sonic_ingest_pass: Option<String>,
|
||||
#[serde(default)]
|
||||
search_active: bool,
|
||||
}
|
||||
|
||||
@ -300,10 +330,10 @@ impl Example for SearchConfig {
|
||||
impl Default for SearchConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sonic_search_addr: Some(String::from("0.0.0.0:1491")),
|
||||
sonic_search_pass: Some(String::from("SecretPassword")),
|
||||
sonic_ingest_addr: Some(String::from("0.0.0.0:1491")),
|
||||
sonic_ingest_pass: Some(String::from("SecretPassword")),
|
||||
sonic_search_addr: Some("0.0.0.0:1491".into()),
|
||||
sonic_search_pass: Some("SecretPassword".into()),
|
||||
sonic_ingest_addr: Some("0.0.0.0:1491".into()),
|
||||
sonic_ingest_pass: Some("SecretPassword".into()),
|
||||
search_active: true,
|
||||
}
|
||||
}
|
||||
@ -359,8 +389,8 @@ pub struct FilesConfig {
|
||||
impl Example for FilesConfig {
|
||||
fn example() -> Self {
|
||||
Self {
|
||||
public_path: Some(String::from("/uploads")),
|
||||
local_path: Some(String::from("/var/local/bazzar")),
|
||||
public_path: Some("/uploads".into()),
|
||||
local_path: Some("/var/local/bazzar".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -368,8 +398,8 @@ impl Example for FilesConfig {
|
||||
impl Default for FilesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
public_path: Some(String::from("/uploads")),
|
||||
local_path: Some(String::from("/var/local/bazzar")),
|
||||
public_path: Some("/uploads".into()),
|
||||
local_path: Some("/var/local/bazzar".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -380,7 +410,7 @@ impl FilesConfig {
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("FILES_PUBLIC_PATH").ok())
|
||||
.unwrap_or_else(|| String::from("/uploads"))
|
||||
.unwrap_or_else(|| "/uploads".into())
|
||||
}
|
||||
|
||||
pub fn local_path(&self) -> String {
|
||||
@ -388,17 +418,23 @@ impl FilesConfig {
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("FILES_LOCAL_PATH").ok())
|
||||
.unwrap_or_else(|| String::from("/var/local/bazzar"))
|
||||
.unwrap_or_else(|| "/var/local/bazzar".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
#[serde(default)]
|
||||
payment: PaymentConfig,
|
||||
#[serde(default)]
|
||||
web: WebConfig,
|
||||
#[serde(default)]
|
||||
mail: MailConfig,
|
||||
#[serde(default)]
|
||||
database: DatabaseConfig,
|
||||
#[serde(default)]
|
||||
search: SearchConfig,
|
||||
#[serde(default)]
|
||||
files: FilesConfig,
|
||||
#[serde(skip)]
|
||||
config_path: String,
|
||||
@ -514,6 +550,8 @@ SESSION_SECRET - 100 characters admin session encryption
|
||||
JWT_SECRET - 100 characters user session encryption
|
||||
BAZZAR_BIND - address to which server should be bind, typically 0.0.0.0
|
||||
BAZZAR_PORT - port which server should use, typically 80
|
||||
SIGNATURE - Signature used in e-mails
|
||||
SERVICE_NAME - Shop name
|
||||
|
||||
SENDGRID_SECRET - e-mail sending service secret
|
||||
SENDGRID_API_KEY - e-mail sending service api key
|
||||
@ -536,6 +574,6 @@ FILES_LOCAL_PATH - path where files are saved on server
|
||||
}
|
||||
|
||||
pub fn save(config_path: &str, config: &mut AppConfig) {
|
||||
config.config_path = String::from(config_path);
|
||||
config.config_path = config_path.into();
|
||||
std::fs::write(config_path, toml::to_string_pretty(&config).unwrap()).unwrap();
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
use serde::Serialize;
|
||||
use derive_more::Deref;
|
||||
#[cfg(feature = "dummy")]
|
||||
use fake::Fake;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ProductLinkedPhoto;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(transparent)]
|
||||
pub struct AccountOrders(pub Vec<AccountOrder>);
|
||||
|
||||
@ -58,7 +62,8 @@ impl From<(crate::AccountOrder, Vec<crate::OrderItem>)> for AccountOrder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct AccountOrder {
|
||||
pub id: crate::AccountOrderId,
|
||||
pub buyer_id: crate::AccountId,
|
||||
@ -67,7 +72,8 @@ pub struct AccountOrder {
|
||||
pub items: Vec<crate::OrderItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ShoppingCartItem {
|
||||
pub id: crate::ShoppingCartId,
|
||||
pub product_id: crate::ProductId,
|
||||
@ -76,7 +82,8 @@ pub struct ShoppingCartItem {
|
||||
pub quantity_unit: crate::QuantityUnit,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ShoppingCart {
|
||||
pub id: crate::ShoppingCartId,
|
||||
pub buyer_id: crate::AccountId,
|
||||
@ -124,13 +131,17 @@ impl From<(crate::ShoppingCart, Vec<crate::ShoppingCartItem>)> for ShoppingCart
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Photo {
|
||||
pub id: crate::PhotoId,
|
||||
pub file_name: crate::FileName,
|
||||
pub url: String,
|
||||
pub unique_name: crate::UniqueName,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Product {
|
||||
pub id: crate::ProductId,
|
||||
pub name: crate::ProductName,
|
||||
@ -142,7 +153,7 @@ pub struct Product {
|
||||
pub photos: Vec<Photo>,
|
||||
}
|
||||
|
||||
impl From<(crate::Product, &mut Vec<crate::ProductLinkedPhoto>)> for Product {
|
||||
impl<'path> From<(crate::Product, &mut Vec<ProductLinkedPhoto>, &'path str)> for Product {
|
||||
fn from(
|
||||
(
|
||||
crate::Product {
|
||||
@ -155,7 +166,8 @@ impl From<(crate::Product, &mut Vec<crate::ProductLinkedPhoto>)> for Product {
|
||||
deliver_days_flag,
|
||||
},
|
||||
photos,
|
||||
): (crate::Product, &mut Vec<ProductLinkedPhoto>),
|
||||
public_path,
|
||||
): (crate::Product, &mut Vec<ProductLinkedPhoto>, &'path str),
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
@ -173,23 +185,32 @@ impl From<(crate::Product, &mut Vec<crate::ProductLinkedPhoto>)> for Product {
|
||||
local_path: _,
|
||||
file_name,
|
||||
product_id: _,
|
||||
}| Photo { id, file_name },
|
||||
unique_name,
|
||||
}| Photo {
|
||||
id,
|
||||
url: format!("{}/{}", public_path, unique_name.as_str()),
|
||||
unique_name,
|
||||
file_name,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref)]
|
||||
#[serde(transparent)]
|
||||
pub struct Products(Vec<Product>);
|
||||
pub struct Products(pub Vec<Product>);
|
||||
|
||||
impl From<(Vec<crate::Product>, Vec<crate::ProductLinkedPhoto>)> for Products {
|
||||
fn from((products, mut photos): (Vec<crate::Product>, Vec<crate::ProductLinkedPhoto>)) -> Self {
|
||||
impl From<(Vec<crate::Product>, Vec<ProductLinkedPhoto>, String)> for Products {
|
||||
fn from(
|
||||
(products, mut photos, public_path): (Vec<crate::Product>, Vec<ProductLinkedPhoto>, String),
|
||||
) -> Self {
|
||||
Self(
|
||||
products
|
||||
.into_iter()
|
||||
.map(|p| (p, &mut photos).into())
|
||||
.map(|p| (p, &mut photos, public_path.as_str()).into())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use std::str::FromStr;
|
||||
use derive_more::{Deref, Display, From};
|
||||
#[cfg(feature = "dummy")]
|
||||
use fake::Fake;
|
||||
#[cfg(feature = "dummy")]
|
||||
use rand::Rng;
|
||||
use serde::de::{Error, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
@ -181,10 +182,16 @@ impl TryFrom<i32> for Quantity {
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Deserialize, Serialize, Debug, Deref, From, Display)]
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, Deref, From, Display)]
|
||||
#[serde(transparent)]
|
||||
pub struct Login(String);
|
||||
|
||||
impl Login {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Debug, Deref, From, Display)]
|
||||
@ -422,6 +429,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, Display)]
|
||||
#[serde(transparent)]
|
||||
pub struct ResetToken(String);
|
||||
|
||||
impl ResetToken {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, From, Display)]
|
||||
@ -728,12 +747,36 @@ pub struct Token {
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, Display, From)]
|
||||
pub struct TokenString(String);
|
||||
|
||||
impl TokenString {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, Display, From)]
|
||||
pub struct LocalPath(String);
|
||||
|
||||
impl LocalPath {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, Display, From)]
|
||||
pub struct UniqueName(String);
|
||||
|
||||
impl UniqueName {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
@ -765,6 +808,7 @@ pub struct Photo {
|
||||
pub id: PhotoId,
|
||||
pub local_path: LocalPath,
|
||||
pub file_name: FileName,
|
||||
pub unique_name: UniqueName,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
@ -774,6 +818,7 @@ pub struct ProductLinkedPhoto {
|
||||
pub id: PhotoId,
|
||||
pub local_path: LocalPath,
|
||||
pub file_name: FileName,
|
||||
pub unique_name: UniqueName,
|
||||
pub product_id: ProductId,
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ edition = "2021"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../shared/model", features = ["dummy"] }
|
||||
|
||||
seed = { version = "0.9.1", features = [] }
|
||||
|
||||
chrono = { version = "*" }
|
||||
@ -21,6 +23,8 @@ web-sys = { version = "0.3.57", features = [] }
|
||||
|
||||
indexmap = { version = "1", features = ["serde-1"] }
|
||||
|
||||
rusty-money = { version = "0.4.1", features = ["iso"] }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 's'
|
||||
|
@ -1,2 +1,10 @@
|
||||
[build]
|
||||
target = "index.html"
|
||||
|
||||
[[proxy]]
|
||||
rewrite = "/api/v1/"
|
||||
backend = "http://localhost:8080/api/v1"
|
||||
|
||||
[[proxy]]
|
||||
rewrite = "/files"
|
||||
backend = "http://localhost:8080/files"
|
||||
|
@ -4,9 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Bazzar</title>
|
||||
<!-- <link data-trunk rel="css" href="public/index.css"/>-->
|
||||
<link data-trunk rel="css" href="assets/css/normalize.css"/>
|
||||
<link data-trunk rel="css" href="assets/css/skeleton.css"/>
|
||||
<link data-trunk rel="css" href="tmp/tailwind.css"/>
|
||||
<base data-trunk-public-url/>
|
||||
<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
12
web/package.json
Normal file
12
web/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||
"@tailwindcss/forms": "^0.5.1",
|
||||
"@tailwindcss/line-clamp": "^0.4.0",
|
||||
"@tailwindcss/typography": "^0.5.2",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"postcss": "^8.4.13",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"tailwindcss-debug-screens": "^2.2.1"
|
||||
}
|
||||
}
|
1
web/src/api.rs
Normal file
1
web/src/api.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod public;
|
11
web/src/api/public.rs
Normal file
11
web/src/api/public.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use seed::prelude::*;
|
||||
|
||||
pub async fn fetch_products() -> fetch::Result<model::api::Products> {
|
||||
Request::new("/api/v1/products")
|
||||
.method(Method::Get)
|
||||
.fetch()
|
||||
.await?
|
||||
.check_status()?
|
||||
.json()
|
||||
.await
|
||||
}
|
3
web/src/input.css
Normal file
3
web/src/input.css
Normal file
@ -0,0 +1,3 @@
|
||||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
@ -1,18 +1,32 @@
|
||||
pub mod api;
|
||||
mod model;
|
||||
mod pages;
|
||||
|
||||
use model::Model;
|
||||
use seed::empty;
|
||||
use seed::prelude::*;
|
||||
|
||||
use crate::model::Model;
|
||||
use crate::pages::{Msg, Page, PublicPage};
|
||||
|
||||
macro_rules! fetch_page {
|
||||
(public $model: expr, $page: ident, $ret: expr) => {{
|
||||
let p = match &mut $model.page {
|
||||
crate::pages::Page::Public(p) => p,
|
||||
_ => return $ret,
|
||||
};
|
||||
match p {
|
||||
crate::pages::PublicPage::$page(p) => p,
|
||||
_ => return $ret,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
Model {
|
||||
token: LocalStorage::get("auth-token").ok(),
|
||||
page: Page::Public(PublicPage::Listing(pages::public::listing::init(
|
||||
url,
|
||||
&mut orders.proxy(|msg| Msg::Public(pages::public::Msg::Listing(msg))),
|
||||
&mut orders.proxy(proxy_public_listing),
|
||||
))),
|
||||
}
|
||||
}
|
||||
@ -20,7 +34,10 @@ fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::UrlChanged(subs::UrlChanged(url)) => model.page = Page::init(url, orders),
|
||||
Msg::Public(_) => {}
|
||||
Msg::Public(pages::public::Msg::Listing(msg)) => {
|
||||
let page = fetch_page!(public model, Listing, ());
|
||||
pages::public::listing::update(msg, page, &mut orders.proxy(proxy_public_listing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,3 +53,7 @@ fn view(model: &Model) -> Node<Msg> {
|
||||
pub fn start() {
|
||||
App::start("main", init, update, view);
|
||||
}
|
||||
|
||||
fn proxy_public_listing(msg: pages::public::listing::Msg) -> Msg {
|
||||
Msg::Public(pages::public::Msg::Listing(msg))
|
||||
}
|
||||
|
@ -3,25 +3,93 @@ use seed::prelude::*;
|
||||
use seed::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Model {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {}
|
||||
|
||||
pub fn init(url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
Model {}
|
||||
pub struct Model {
|
||||
pub products: Vec<model::api::Product>,
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {}
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {
|
||||
FetchProducts,
|
||||
ProductFetched(fetch::Result<model::api::Products>),
|
||||
}
|
||||
|
||||
pub fn init(_url: Url, orders: &mut impl Orders<Msg>) -> Model {
|
||||
orders.send_msg(Msg::FetchProducts);
|
||||
Model {
|
||||
products: vec![],
|
||||
errors: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::FetchProducts => {
|
||||
orders.skip().perform_cmd({
|
||||
async { Msg::ProductFetched(crate::api::public::fetch_products().await) }
|
||||
});
|
||||
}
|
||||
Msg::ProductFetched(Ok(products)) => {
|
||||
model.products = products.0;
|
||||
}
|
||||
Msg::ProductFetched(Err(_e)) => {
|
||||
model.errors.push("Failed to load products".into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(model: &Model) -> Node<Msg> {
|
||||
let products = model.products.iter().map(product);
|
||||
|
||||
div![
|
||||
C!["container"],
|
||||
C!["grid grid-cols-1 gap-4 lg:grid-cols-6 sm:grid-cols-2"],
|
||||
products
|
||||
]
|
||||
}
|
||||
|
||||
fn product(product: &model::api::Product) -> Node<Msg> {
|
||||
use rusty_money::{iso, Money};
|
||||
|
||||
let price = Money::from_minor(**product.price as i64, iso::PLN).to_string();
|
||||
let _description = product.short_description.as_str();
|
||||
let name = product.name.as_str();
|
||||
let img = product
|
||||
.photos
|
||||
.first()
|
||||
.map(|photo| photo.url.as_str())
|
||||
.unwrap_or_default();
|
||||
|
||||
div![
|
||||
C!["w-full px-4 lg:px-0"],
|
||||
div![
|
||||
C!["row"],
|
||||
C!["p-3 bg-white rounded shadow-md"],
|
||||
div![
|
||||
C!["one-half column"],
|
||||
p!["This index.html page is a placeholder with the CSS, font and favicon."]
|
||||
div![
|
||||
C!["relative w-full mb-3 h-62 lg:mb-0"],
|
||||
img![attrs![
|
||||
"src" => img,
|
||||
"alt" => name,
|
||||
"class" => "object-fill w-full h-full rounded"
|
||||
]],
|
||||
],
|
||||
div![
|
||||
C!["flex-auto p-2 justify-evenly"],
|
||||
div![
|
||||
C!["flex flex-wrap"],
|
||||
div![
|
||||
C!["flex items-center justify-between w-full min-w-0"],
|
||||
h2![
|
||||
C!["mr-auto text-lg cursor-pointer hover:text-gray-900"],
|
||||
name
|
||||
]
|
||||
]
|
||||
],
|
||||
div![
|
||||
C!["flex items-center justify-between"],
|
||||
a![C!["px-6 py-2 text-sm text-white bg-indigo-500 rounded-lg outline-none hover:bg-indigo-600 ring-indigo-300"], "Add to cart"],
|
||||
div![C!["mt-1 text-xl font-semibold"], price],
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
822
web/static/output.css
Normal file
822
web/static/output.css
Normal file
@ -0,0 +1,822 @@
|
||||
/*
|
||||
! tailwindcss v3.0.24 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
border-width: 0;
|
||||
/* 2 */
|
||||
border-style: solid;
|
||||
/* 2 */
|
||||
border-color: #e5e7eb;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
::before,
|
||||
::after {
|
||||
--tw-content: '';
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.5;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
-moz-tab-size: 4;
|
||||
/* 3 */
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 3 */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
/* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
border-top-width: 1px;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0;
|
||||
/* 1 */
|
||||
border-color: inherit;
|
||||
/* 2 */
|
||||
border-collapse: collapse;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
background-color: transparent;
|
||||
/* 2 */
|
||||
background-image: none;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
/* 1 */
|
||||
vertical-align: middle;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Ensure the default browser behavior of the `hidden` attribute.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border-color: #6b7280;
|
||||
border-width: 1px;
|
||||
border-radius: 0px;
|
||||
padding-top: 0.5rem;
|
||||
padding-right: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: #2563eb;
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
color: #6b7280;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
|
||||
color: #6b7280;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input::placeholder,textarea::placeholder {
|
||||
color: #6b7280;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-date-and-time-value {
|
||||
min-height: 1.5em;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
select {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
||||
background-position: right 0.5rem center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1.5em 1.5em;
|
||||
padding-right: 2.5rem;
|
||||
-webkit-print-color-adjust: exact;
|
||||
color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
|
||||
[multiple] {
|
||||
background-image: initial;
|
||||
background-position: initial;
|
||||
background-repeat: unset;
|
||||
background-size: initial;
|
||||
padding-right: 0.75rem;
|
||||
-webkit-print-color-adjust: unset;
|
||||
color-adjust: unset;
|
||||
print-color-adjust: unset;
|
||||
}
|
||||
|
||||
[type='checkbox'],[type='radio'] {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
padding: 0;
|
||||
-webkit-print-color-adjust: exact;
|
||||
color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-origin: border-box;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
color: #2563eb;
|
||||
background-color: #fff;
|
||||
border-color: #6b7280;
|
||||
border-width: 1px;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
[type='checkbox'] {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
[type='radio'] {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
[type='checkbox']:focus,[type='radio']:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||
--tw-ring-offset-width: 2px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: #2563eb;
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
|
||||
[type='checkbox']:checked,[type='radio']:checked {
|
||||
border-color: transparent;
|
||||
background-color: currentColor;
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
[type='checkbox']:checked {
|
||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
[type='radio']:checked {
|
||||
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
|
||||
border-color: transparent;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
[type='checkbox']:indeterminate {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
|
||||
border-color: transparent;
|
||||
background-color: currentColor;
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
|
||||
border-color: transparent;
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
[type='file'] {
|
||||
background: unset;
|
||||
border-color: inherit;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
font-size: unset;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
[type='file']:focus {
|
||||
outline: 1px solid ButtonText;
|
||||
outline: 1px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.container {
|
||||
max-width: 640px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
max-width: 768px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
max-width: 1024px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1536px) {
|
||||
.container {
|
||||
max-width: 1536px;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-screens::before {
|
||||
position: fixed;
|
||||
z-index: 2147483647;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: .3333333em .5em;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
font-family: sans-serif;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 0 1px #fff;
|
||||
content: 'screen: _';
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.debug-screens::before {
|
||||
content: 'screen: sm';
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.debug-screens::before {
|
||||
content: 'screen: md';
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.debug-screens::before {
|
||||
content: 'screen: lg';
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.debug-screens::before {
|
||||
content: 'screen: xl';
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1536px) {
|
||||
.debug-screens::before {
|
||||
content: 'screen: 2xl';
|
||||
}
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.rounded-r {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.border-transparent {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-blue-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(37 99 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-8 {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.leading-normal {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tracking-tight {
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.text-sky-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(2 132 199 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-sky-900 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(12 74 110 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-blue-700:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
|
||||
}
|
32
web/static/reset-password.html
Normal file
32
web/static/reset-password.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Reset password</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/output.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<section class="debug-screens">
|
||||
<div class="container p-8 mx-auto mt-12 bg-white">
|
||||
<div class="rounded-lg">
|
||||
<h1 class="mb-3 text-xl font-semibold tracking-tight text-sky-600">Trouble signing in?</h1>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">Resetting your password is easy.</p>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">Just press the button below and follow the instructions. We’ll have you up and running in no time.</p>
|
||||
<p>
|
||||
<a
|
||||
class="px-4 py-2 inline-block text-white bg-blue-600 border border-transparent rounded-r hover:bg-blue-700"
|
||||
href="{url}"
|
||||
>
|
||||
Reset Password
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">If you did not make this request then please ignore this email.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
25
web/static/test-email.html
Normal file
25
web/static/test-email.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Welcome to {service_name}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="/output.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<section class="debug-screens">
|
||||
<div class="container p-8 mx-auto mt-12 bg-white">
|
||||
<div class="rounded-lg">
|
||||
<h1 class="mb-3 text-xl font-semibold tracking-tight text-sky-600">Hi {login}</h1>
|
||||
|
||||
<p class="mb-2 leading-normal text-sky-900">
|
||||
Welcome to {service_name} – we’re excited to have you on board and we’d love to say thank you on behalf
|
||||
of our whole company for chosing us.
|
||||
</p>
|
||||
<p class="mb-2 leading-normal text-sky-900">Take care,</p>
|
||||
<p class="mb-2 leading-normal text-sky-900">{signature}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
24
web/tailwind.config.js
Normal file
24
web/tailwind.config.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
spacing: {
|
||||
'96': '24rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
content: [
|
||||
"./static/*.{html,js}",
|
||||
"./src/**/*.{html,js,rs}"
|
||||
],
|
||||
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/line-clamp'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('tailwindcss-debug-screens'),
|
||||
],
|
||||
experimental: {
|
||||
optimizeUniversalDefaults: true
|
||||
}
|
||||
}
|
507
web/yarn.lock
Normal file
507
web/yarn.lock
Normal file
@ -0,0 +1,507 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
|
||||
"@nodelib/fs.walk@^1.2.3":
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
|
||||
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
|
||||
dependencies:
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@tailwindcss/aspect-ratio@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.0.tgz#c635dd7331cbcc1b111cebdc2647dd3493ebdd3e"
|
||||
integrity sha512-WJu0I4PpqNPuutpaA9zDUq2JXR+lorZ7PbLcKNLmb6GL9/HLfC7w3CRsMhJF4BbYd/lkY6CfXOvkYpuGnZfkpQ==
|
||||
|
||||
"@tailwindcss/forms@^0.5.1":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.1.tgz#7fe86b9b67e6d91cb902e2d3f4ebe561cc057a13"
|
||||
integrity sha512-QSwsFORnC2BAP0lRzQkz1pw+EzIiiPdk4e27vGQjyXkwJPeC7iLIRVndJzf9CJVbcrrIcirb/TfxF3gRTyFEVA==
|
||||
dependencies:
|
||||
mini-svg-data-uri "^1.2.3"
|
||||
|
||||
"@tailwindcss/line-clamp@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/line-clamp/-/line-clamp-0.4.0.tgz#03353e31e77636b785f2336e8c978502cec1de81"
|
||||
integrity sha512-HQZo6gfx1D0+DU3nWlNLD5iA6Ef4JAXh0LeD8lOGrJwEDBwwJNKQza6WoXhhY1uQrxOuU8ROxV7CqiQV4CoiLw==
|
||||
|
||||
"@tailwindcss/typography@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.2.tgz#24b069dab24d7a2467d01aca0dd432cb4b29f0ee"
|
||||
integrity sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==
|
||||
dependencies:
|
||||
lodash.castarray "^4.4.0"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.merge "^4.6.2"
|
||||
|
||||
acorn-node@^1.6.1:
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
|
||||
integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
|
||||
dependencies:
|
||||
acorn "^7.0.0"
|
||||
acorn-walk "^7.0.0"
|
||||
xtend "^4.0.2"
|
||||
|
||||
acorn-walk@^7.0.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||
|
||||
acorn@^7.0.0:
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
arg@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb"
|
||||
integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==
|
||||
|
||||
autoprefixer@^10.4.7:
|
||||
version "10.4.7"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf"
|
||||
integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==
|
||||
dependencies:
|
||||
browserslist "^4.20.3"
|
||||
caniuse-lite "^1.0.30001335"
|
||||
fraction.js "^4.2.0"
|
||||
normalize-range "^0.1.2"
|
||||
picocolors "^1.0.0"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
braces@^3.0.2, braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
browserslist@^4.20.3:
|
||||
version "4.20.3"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf"
|
||||
integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001332"
|
||||
electron-to-chromium "^1.4.118"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^2.0.3"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
camelcase-css@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
|
||||
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
||||
|
||||
caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335:
|
||||
version "1.0.30001338"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz#b5dd7a7941a51a16480bdf6ff82bded1628eec0d"
|
||||
integrity sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==
|
||||
|
||||
chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
color-name@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
defined@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
|
||||
integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
|
||||
|
||||
detective@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
|
||||
integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==
|
||||
dependencies:
|
||||
acorn-node "^1.6.1"
|
||||
defined "^1.0.0"
|
||||
minimist "^1.1.1"
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||
integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
|
||||
|
||||
dlv@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
|
||||
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
||||
|
||||
electron-to-chromium@^1.4.118:
|
||||
version "1.4.137"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f"
|
||||
integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
fast-glob@^3.2.11:
|
||||
version "3.2.11"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
|
||||
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.2"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
|
||||
integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fraction.js@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
|
||||
integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
|
||||
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.8.1:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
|
||||
integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
lilconfig@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25"
|
||||
integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==
|
||||
|
||||
lodash.castarray@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115"
|
||||
integrity sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
|
||||
dependencies:
|
||||
braces "^3.0.2"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
mini-svg-data-uri@^1.2.3:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939"
|
||||
integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==
|
||||
|
||||
minimist@^1.1.1:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
nanoid@^3.3.3:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||
|
||||
node-releases@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476"
|
||||
integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
normalize-range@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
|
||||
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
|
||||
|
||||
object-hash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||
|
||||
path-parse@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss-js@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00"
|
||||
integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
|
||||
dependencies:
|
||||
camelcase-css "^2.0.1"
|
||||
|
||||
postcss-load-config@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
|
||||
integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
|
||||
dependencies:
|
||||
lilconfig "^2.0.5"
|
||||
yaml "^1.10.2"
|
||||
|
||||
postcss-nested@5.0.6:
|
||||
version "5.0.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc"
|
||||
integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.6"
|
||||
|
||||
postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6:
|
||||
version "6.0.10"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
|
||||
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.4.12, postcss@^8.4.13:
|
||||
version "8.4.13"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
|
||||
integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
|
||||
dependencies:
|
||||
nanoid "^3.3.3"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
quick-lru@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
resolve@^1.22.0:
|
||||
version "1.22.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
|
||||
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
|
||||
dependencies:
|
||||
is-core-module "^2.8.1"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
reusify@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
|
||||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tailwindcss-debug-screens@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss-debug-screens/-/tailwindcss-debug-screens-2.2.1.tgz#8dd0854a273daa4f30f4c383370872c6bca337cd"
|
||||
integrity sha512-EMyA0CYBzqcZJHtVDvBfmYzfx3NxuK4qDyVO5wnzcGOrmJsv25D9xPpWefVTORTvhE6pCh90Z1WYnLUKsg3yMw==
|
||||
|
||||
tailwindcss@^3.0.24:
|
||||
version "3.0.24"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d"
|
||||
integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==
|
||||
dependencies:
|
||||
arg "^5.0.1"
|
||||
chokidar "^3.5.3"
|
||||
color-name "^1.1.4"
|
||||
detective "^5.2.0"
|
||||
didyoumean "^1.2.2"
|
||||
dlv "^1.1.3"
|
||||
fast-glob "^3.2.11"
|
||||
glob-parent "^6.0.2"
|
||||
is-glob "^4.0.3"
|
||||
lilconfig "^2.0.5"
|
||||
normalize-path "^3.0.0"
|
||||
object-hash "^3.0.0"
|
||||
picocolors "^1.0.0"
|
||||
postcss "^8.4.12"
|
||||
postcss-js "^4.0.0"
|
||||
postcss-load-config "^3.1.4"
|
||||
postcss-nested "5.0.6"
|
||||
postcss-selector-parser "^6.0.10"
|
||||
postcss-value-parser "^4.2.0"
|
||||
quick-lru "^5.1.1"
|
||||
resolve "^1.22.0"
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
xtend@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
||||
yaml@^1.10.2:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
Loading…
Reference in New Issue
Block a user