diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml index 5d7da8c..687046a 100644 --- a/.gitea/workflows/main.yml +++ b/.gitea/workflows/main.yml @@ -22,5 +22,5 @@ jobs: uses: dokku/github-action@master with: branch: 'main' - git_remote_url: 'ssh://dokku@v2.discours.io:22/presence' + git_remote_url: 'ssh://dokku@v2.discours.io:22/quoter' ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 89dc3e7..4dcf239 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash", - "base64", + "base64 0.22.1", "bitflags 2.5.0", "brotli", "bytes", @@ -236,6 +236,18 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-trait" version = "0.1.80" @@ -259,6 +271,381 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-config" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca49303c05d2a740b8a4552fac63a4db6ead84f7e7eeed04761fd3014c26f25" +dependencies = [ + "ahash", + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http-body 0.4.6", + "lru", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "p256", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598b1689d001c4d4dc3cb386adb07d37786783aee3ac4b324bcadac116bf3d23" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6363078f927f612b970edf9d1903ef5cef9a64d1e8423525ebb1f0a1633c858" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.0", + "httparse", + "hyper 0.14.30", + "hyper-rustls", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.72" @@ -274,12 +661,46 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -328,12 +749,34 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bytestring" version = "1.3.1" @@ -360,6 +803,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -374,6 +823,12 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" @@ -416,6 +871,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -425,6 +889,59 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -445,6 +962,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -475,14 +1002,19 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] -name = "discoursio-presence" -version = "0.2.20" +name = "discoursio-quoter" +version = "0.0.1" dependencies = [ "actix-web", + "aws-config", + "aws-sdk-s3", "futures", + "image", + "mime_guess", "redis", "reqwest", "sentry", @@ -493,6 +1025,44 @@ dependencies = [ "uuid", ] +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.34" @@ -518,12 +1088,47 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "findshlibs" version = "0.10.2" @@ -546,6 +1151,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -686,12 +1300,33 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.3.26" @@ -730,11 +1365,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hermit-abi" @@ -748,6 +1397,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "hostname" version = "0.4.0" @@ -781,6 +1439,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -800,7 +1469,7 @@ dependencies = [ "bytes", "futures-core", "http 1.1.0", - "http-body", + "http-body 1.0.0", "pin-project-lite", ] @@ -816,6 +1485,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.3.1" @@ -827,7 +1520,7 @@ dependencies = [ "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body", + "http-body 1.0.0", "httparse", "itoa", "pin-project-lite", @@ -836,6 +1529,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.30", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -844,7 +1553,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -862,8 +1571,8 @@ dependencies = [ "futures-channel", "futures-util", "http 1.1.0", - "http-body", - "hyper", + "http-body 1.0.0", + "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", @@ -882,6 +1591,24 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -913,6 +1640,15 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -934,6 +1670,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.155" @@ -979,6 +1721,25 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.2" @@ -991,6 +1752,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -998,6 +1769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -1029,12 +1801,40 @@ dependencies = [ "tempfile", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -1115,6 +1915,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1182,12 +1999,35 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1209,6 +2049,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" version = "1.0.36" @@ -1249,16 +2098,38 @@ dependencies = [ ] [[package]] -name = "redis" -version = "0.25.4" +name = "rayon" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redis" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e902a69d09078829137b4a5d9d082e0490393537badd7c91a3d69d14639e115f" +dependencies = [ + "arc-swap", "async-trait", "bytes", "combine", "futures-util", "itoa", + "num-bigint", "percent-encoding", "pin-project-lite", "ryu", @@ -1319,7 +2190,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -1327,9 +2198,9 @@ dependencies = [ "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.3.1", "hyper-tls", "hyper-util", "ipnet", @@ -1340,7 +2211,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 2.1.2", "serde", "serde_json", "serde_urlencoded", @@ -1356,6 +2227,32 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1384,13 +2281,46 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pemfile" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64", + "base64 0.22.1", "rustls-pki-types", ] @@ -1400,6 +2330,16 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" @@ -1421,6 +2361,30 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.0" @@ -1571,18 +2535,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", @@ -1629,6 +2593,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1638,6 +2613,22 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -1663,6 +2654,31 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1744,6 +2760,17 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.36" @@ -1830,6 +2857,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.11" @@ -1878,9 +2915,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -1921,6 +2970,15 @@ dependencies = [ "libc", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -1942,13 +3000,19 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64", + "base64 0.22.1", "log", "native-tls", "once_cell", @@ -1967,6 +3031,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "uuid" version = "1.8.0" @@ -1995,6 +3065,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "want" version = "0.3.1" @@ -2086,6 +3162,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "winapi" version = "0.3.9" @@ -2276,6 +3358,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "zerocopy" version = "0.7.34" @@ -2296,6 +3384,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zstd" version = "0.13.1" @@ -2323,3 +3417,12 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index fb82dc8..2377ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "discoursio-presence" -version = "0.2.20" +name = "discoursio-quoter" +version = "0.0.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,11 +12,15 @@ actix-web = "4.5.1" reqwest = { version = "0.12.3", features = ["json"] } sentry = { version = "0.34.0", features = ["tokio"] } uuid = { version = "1.8.0", features = ["v4"] } -redis = { version = "0.25.3", features = ["tokio-comp"] } +redis = { version = "0.26.1", features = ["tokio-comp"] } tokio = { version = "1.37.0", features = ["full"] } -serde = { version = "1.0.197", features = ["derive"] } +serde = { version = "1.0.209", features = ["derive"] } sentry-actix = "0.34.0" +aws-sdk-s3 = "1.47.0" # AWS SDK для работы с S3 +image = "0.24.7" # Библиотека для работы с изображениями (генерация миниатюр) +mime_guess = "2.0.5" +aws-config = "1.5.5" [[bin]] -name = "presence" +name = "quoter" path = "./src/main.rs" diff --git a/Dockerfile b/Dockerfile index 881569f..c3fabf1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,8 +5,8 @@ RUN apt-get update -y && \ apt-get install -y git pkg-config make g++ libssl-dev wget && \ rustup target add x86_64-unknown-linux-gnu -RUN USER=root cargo new --bin presence -WORKDIR /presence +RUN USER=root cargo new --bin quoter +WORKDIR /quoter COPY ./Cargo.lock ./Cargo.lock COPY ./Cargo.toml ./Cargo.toml @@ -20,7 +20,7 @@ RUN rm src/*.rs COPY ./src ./src # build for release -RUN rm ./target/release/deps/presence* +RUN rm ./target/release/deps/quoter* RUN cargo build --release FROM rust @@ -28,8 +28,8 @@ FROM rust ENV RUST_BACKTRACE=full RUN apt-get update && apt install -y openssl libssl-dev -COPY --from=build /presence/target/release/presence . +COPY --from=build /quoter/target/release/quoter . EXPOSE 8080 -CMD ["./presence"] +CMD ["./quoter"] diff --git a/README.md b/README.md index 2569a14..55922e4 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,39 @@ -## Presence - -"Присутствие" - это сервер для пересылки сообщений в реальном времени. Текущая версия использует SSE-транспорт. +## Квотер +Управляет квотами на загрузку файлов и их размещением в S3-хранилище. Поддерживает создание миниатюр для изображений и управляет квотами на использование дискового пространства для каждого пользователя с использованием Redis. ### ENV - - API_BASE - - AUTH_URL - - REDIS_URL - +- `REDIS_URL`: URL для подключения к Redis. Используется для управления квотами и хранения информации о загружаемых файлах. +- `S3_BUCKET`: Имя S3 bucket, используемого для хранения загруженных файлов. +- `CDN_DOMAIN`: Домен CDN для генерации публичных URL-адресов загруженных файлов. ### Как это работает -При каждом обращении к `/connect` создаётся отдельная асинхронная задача с подписками на Redus PubSub каналы, позволяя пользователям получать только те уведомления, которые предназначены непосредственно для них. +1. **Аутентификация**: + - Клиент отправляет файл на сервер с заголовком `Authorization`, содержащим токен. Сервер проверяет наличие и валидность токена, определяя пользователя. -Каналы Redis: +2. **Загрузка файлов**: + - Сервер обрабатывает все загружаемые файлы. Если файл является изображением, создается его миниатюра. И миниатюра, и оригинальное изображение загружаются в S3. Для остальных файлов выполняется простая загрузка в S3 без создания миниатюр. - - `reaction` - - `shout` - - `follower:` - - `chat:` +3. **Создание миниатюр**: + - Для всех загружаемых изображений сервер автоматически создает миниатюры размером 320x320 пикселей. Миниатюры сохраняются как отдельные файлы в том же S3 bucket, что и оригинальные изображения. -Сервис пересылает сообщения из этих каналов, которые предназначены пользователю, подписавшемуся на Server-Sent Events (SSE) по адресу `/connect`. Для авторизации подписки используется токен, который передается клиентом в заголовке `Authorization`, или в пути `/connect/{token}`, или в переменной запроса `/connect/?token={token}`. +4. **Определение MIME-типа и расширения файла**: + - MIME-тип и расширение файла определяются автоматически на основе имени файла и его содержимого с использованием библиотеки `mime_guess`. -При завершении подключения, все подписки автоматически отменяются, так как они связаны с конкретным подключением. Если пользователь снова подключается, процесс подписки повторяется. +5. **Загрузка файлов в S3**: + - Все файлы, включая миниатюры и оригиналы изображений, загружаются в указанный S3 bucket. Сформированные URL-адреса файлов возвращаются клиенту. +6. **Управление квотами**: + - Для каждого пользователя устанавливается квота на загрузку данных, которая составляет 1 ГБ в неделю. Перед загрузкой каждого нового файла проверяется, не превысит ли его размер текущую квоту пользователя. Если квота будет превышена, загрузка файла будет отклонена. После успешной загрузки файл и его размер регистрируются в Redis, и квота пользователя обновляется. -### Формат сообщений межсервисной коммуникации +7. **Сохранение информации о загруженных файлах в Redis**: + - Имя каждого загруженного файла сохраняется в Redis для отслеживания загруженных пользователем файлов. Это позволяет учитывать квоты и управлять пространством, занимаемым файлами. -Между сервисами пересылаются целые сущности и типизация действий с ними, поля стандартного redis-сообщения: +### Основные функции -- `action` наименование операции, примеры: "create" | "delete" | "update" | "join" | "left" -- `payload` json одной из сущностей: Reaction | Shout | Author | Chat | Message \ No newline at end of file + - Миниатюры: Автоматическое создание миниатюр для изображений. + - S3/STORJ интеграция: Загрузка файлов в через `aws-sdk-s3` и возврат публичных URL-адресов. + - Управление квотами: Ограничение объема загружаемых данных для каждого пользователя с использованием Redis. + - Отслеживание файлов: Хранение информации о загруженных файлах в Redis для управления квотами. diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index bf5bfd2..0000000 --- a/src/data.rs +++ /dev/null @@ -1,194 +0,0 @@ -use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; -use reqwest::Client as HTTPClient; -use serde_json::json; -use std::collections::HashMap; -use std::env; -use std::error::Error; - -use crate::SSEMessageData; - -async fn get_author_id(user: &str) -> Result> { - let api_base = env::var("API_BASE")?; - let query_name = "get_author_id"; - let operation = "GetAuthorId"; - let mut headers = HeaderMap::new(); - // headers.insert(AUTHORIZATION, HeaderValue::from_str(token)?); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - - let mut variables = HashMap::::new(); - variables.insert("user".to_string(), user.to_string()); - - let gql = json!({ - "query": format!("query {}($user: String!) {{ {}(user: $user){{ id }} }}", operation, query_name), - "operationName": operation, - "variables": variables - }); - // println!("[get_author_id] GraphQL: {}", gql); - let client = HTTPClient::new(); - let response = client - .post(&api_base) - .headers(headers) - .json(&gql) - .send() - .await?; - - if response.status().is_success() { - let r: HashMap = response.json().await?; - let author_id = r - .get("data") - .and_then(|data| data.get(query_name)) - .and_then(|claims| claims.get("id")) - .and_then(|id| id.as_i64()); - - match author_id { - Some(id) => { - println!("Author ID retrieved: {}", id); - Ok(id as i32) - } - None => Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "No author ID found in the response", - ))), - } - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Request failed with status: {}", response.status()), - ))) - } -} - -pub async fn get_id_by_token(token: &str) -> Result> { - let auth_api_base = env::var("AUTH_URL")?; - let query_name = "validate_jwt_token"; - let operation = "ValidateToken"; - let mut headers = HeaderMap::new(); - // headers.insert(AUTHORIZATION, HeaderValue::from_str(token)?); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - - let mut variables = HashMap::>::new(); - let mut params = HashMap::::new(); - params.insert("token".to_string(), token.to_string()); - params.insert("token_type".to_string(), "access_token".to_string()); - variables.insert("params".to_string(), params); - - let gql = json!({ - "query": format!("query {}($params: ValidateJWTTokenInput!) {{ {}(params: $params) {{ is_valid claims }} }}", operation, query_name), - "operationName": operation, - "variables": variables - }); - println!("[get_id_by_token] GraphQL Query: {}", gql); - let client = HTTPClient::new(); - let response = client - .post(&auth_api_base) - .headers(headers) - .json(&gql) - .send() - .await?; - - if response.status().is_success() { - let r: HashMap = response.json().await?; - let user_id = r - .get("data") - .and_then(|data| data.get(query_name)) - .and_then(|query| query.get("claims")) - .and_then(|claims| claims.get("sub")) - .and_then(|id| id.as_str()) - .map(|id| id.trim()); - - match user_id { - Some(id) => { - println!("[get_id_by_token] User ID retrieved: {}", id); - let author_id = get_author_id(id).await?; - Ok(author_id as i32) - } - None => { - println!("[get_id_by_token] No user ID found in the response"); - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - "No user ID found in the response", - ))) - } - } - } else { - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Request failed with status: {}", response.status()), - ))) - } -} - -async fn get_shout_followers(shout_id: &str) -> Result, Box> { - let api_base = env::var("API_BASE")?; - let query = r#"query GetShoutFollowers($slug: String, shout_id: Int) { - get_shout_followers(slug: $slug, shout_id: $shout_id) { id } - } - "#; - let shout_id = shout_id.parse::()?; - let variables = json!({ - "shout": shout_id - }); - let body = json!({ - "query": query, - "operationName": "GetShoutFollowers", - "variables": variables - }); - - let client = reqwest::Client::new(); - let response = client.post(&api_base).json(&body).send().await?; - - if response.status().is_success() { - let response_body: serde_json::Value = response.json().await?; - let ids: Vec = response_body["data"]["get_shout_followers"] - .as_array() - .ok_or("Failed to parse follower array")? - .iter() - .filter_map(|f| f["id"].as_i64().map(|id| id as i32)) - .collect(); - - Ok(ids) - } else { - println!("Request failed with status: {}", response.status()); - Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "[get_shout_followers] Request failed with status: {}", - response.status() - ), - ))) - } -} - -pub async fn is_fitting( - listener_id: i32, - message_data: SSEMessageData, -) -> Result { - if message_data.entity == "reaction" { - // payload is Reaction - let shout_id = message_data.payload.get("shout").unwrap().as_str().unwrap(); - let recipients = get_shout_followers(shout_id).await.unwrap(); - - Ok(recipients.contains(&listener_id)) - } else if message_data.entity == "shout" { - // payload is Shout - - // TODO: check all shout.communities subscribers if no then - // TODO: check all shout.topics subscribers if no then - // TODO: check all shout.authors subscribers ??? - - Ok(true) - } else if message_data.entity == "chat" { - // payload is Chat - Ok(true) - } else if message_data.entity == "message" { - // payload is Message - Ok(true) - } else if message_data.entity == "follower" { - // payload is Author - Ok(true) - } else { - eprintln!("[data] unknown entity"); - eprintln!("{:?}", message_data); - Ok(false) - } -} diff --git a/src/main.rs b/src/main.rs index d524da0..2e97f1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,204 +1,209 @@ -use actix_web::error::{ErrorInternalServerError as ServerError, ErrorUnauthorized}; -use actix_web::middleware::Logger; -use actix_web::{web, web::Bytes, App, HttpRequest, HttpResponse, HttpServer}; -use futures::StreamExt; -use redis::{AsyncCommands, Client}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use uuid::Uuid; -use std::collections::HashMap; +use actix_web::{ + error::{ErrorInternalServerError, ErrorUnauthorized}, + middleware::Logger, + web, App, HttpRequest, HttpResponse, HttpServer, Result, +}; +use aws_config::{load_defaults, BehaviorVersion}; +use aws_sdk_s3::Client as S3Client; +use aws_sdk_s3::primitives::ByteStream; +use image::DynamicImage; +use image::imageops::FilterType; +use mime_guess::MimeGuess; +use redis::{aio::MultiplexedConnection, AsyncCommands}; +use redis::Client as RedisClient; use std::env; -use std::str::FromStr; -use std::sync::{Arc, Mutex}; -use tokio::sync::broadcast; -use tokio::task::JoinHandle; -use sentry::types::Dsn; -use sentry_actix; +use std::io::Cursor; +use std::path::Path; -mod data; +const MAX_QUOTA_BYTES: u64 = 2 * 1024 * 1024 * 1024; // 2 GB per week #[derive(Clone)] struct AppState { - tasks: Arc>>>, - redis: Client, + redis: MultiplexedConnection, // Redis connection for managing quotas and file names + s3_client: S3Client, // S3 client for uploading files + s3_bucket: String, // S3 bucket name for storing files + cdn_domain: String, // CDN domain for generating URLs } -#[derive(Serialize, Deserialize, Clone, Debug)] -struct RedisMessageData { - payload: HashMap, - action: String +// Generate a thumbnail for the image +fn generate_thumbnail(image: &DynamicImage) -> Result, actix_web::Error> { + let thumbnail = image.resize(320, 320, FilterType::Lanczos3); // Размер миниатюры 320x320 + + let mut buffer = Vec::new(); + thumbnail + .write_to(&mut Cursor::new(&mut buffer), image::ImageFormat::Jpeg) + .map_err(|_| ErrorInternalServerError("Failed to generate thumbnail"))?; + + Ok(buffer) } -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct SSEMessageData { - payload: HashMap, - action: String, - entity: String +// Upload the file to S3 and return the URL +async fn upload_to_s3( + s3_client: &S3Client, + bucket: &str, + key: &str, + body: Vec, + content_type: &str, + cdn_domain: &str, +) -> Result { + let body_stream = ByteStream::from(body); + + s3_client.put_object() + .bucket(bucket) + .key(key) + .body(body_stream) + .content_type(content_type) + .send() + .await + .map_err(|_| ErrorInternalServerError("Failed to upload file to S3"))?; + + Ok(format!("{}/{}", cdn_domain, key)) } -async fn connect_handler( +// Check and update the user's quota +async fn check_and_update_quota( + redis: &mut MultiplexedConnection, + user_id: &str, + file_size: u64, +) -> Result<(), actix_web::Error> { + let current_quota: u64 = redis.get(user_id).await.unwrap_or(0); + + if current_quota + file_size > MAX_QUOTA_BYTES { + return Err(ErrorUnauthorized("Quota exceeded")); + } + + redis.incr(user_id, file_size).await.map_err(|_| ErrorInternalServerError("Failed to update quota in Redis")) +} + +// Proxy handler for serving static files and uploading them to S3 +async fn proxy_handler( req: HttpRequest, + path: web::Path, state: web::Data, ) -> Result { + let token = req.headers().get("Authorization").and_then(|header_value| header_value.to_str().ok()); - let token = match req.headers().get("Authorization") { - Some(val) => val.to_str().unwrap_or("").split(" ").last().unwrap_or(""), - None => match req.match_info().get("token") { - Some(val) => val, - None => match req.query_string().split('=').last() { - Some(val) => val, - None => return Err(ErrorUnauthorized("Unauthorized")), - }, - }, - }; + // Validate token (implementation needed) + if token.is_none() { + return Err(ErrorUnauthorized("Unauthorized")); + } - let listener_id = data::get_id_by_token(&token).await.map_err(|e| { - eprintln!("TOKEN check failed: {}", e); - ErrorUnauthorized("Unauthorized") - })?; + let user_id = token.unwrap(); // Assuming the token is the user ID, adjust as necessary - let mut con = state.redis.get_multiplexed_async_connection().await.map_err(|e| { - eprintln!("Failed to get async connection: {}", e); - ServerError("Internal Server Error") - })?; + // Load the file (implement your file loading logic) + let file_path = path.into_inner(); + let mime_type = MimeGuess::from_path(&file_path).first_or_octet_stream(); + let extension = Path::new(&file_path) + .extension() + .and_then(|ext| ext.to_str()) + .unwrap_or("bin"); - con.sadd::<&str, &i32, usize>("authors-online", &listener_id) - .await - .map_err(|e| { - eprintln!("Failed to add author to online list: {}", e); - ServerError("Internal Server Error") - })?; + // Handle image files: generate thumbnail and upload both + if mime_type.type_() == "image" { + let image = image::open(&file_path).map_err(|_| ErrorInternalServerError("Failed to open image"))?; - let chats: Vec = con - .smembers::>(format!("chats_by_author/{}", listener_id)) - .await - .map_err(|e| { - eprintln!("Failed to get chats by author: {}", e); - ServerError("Internal Server Error") - })?; + // Generate thumbnail + let thumbnail_data = generate_thumbnail(&image)?; + let thumbnail_key = format!("thumbnail_{}.{}", file_path, "jpg"); - let (tx, rx) = broadcast::channel(100); - let state_clone = state.clone(); - let handle = tokio::spawn(async move { - let mut pubsub = state_clone.redis.get_async_pubsub().await.unwrap(); - let followers_channel = format!("follower:{}", listener_id); - pubsub.subscribe(followers_channel.clone()).await.unwrap(); - println!("'{}' pubsub subscribed", followers_channel); - pubsub.subscribe("shout").await.unwrap(); - println!("'shout' pubsub subscribed"); - pubsub.subscribe("reaction").await.unwrap(); - println!("'reaction' pubsub subscribed"); + // Upload the thumbnail + upload_to_s3( + &state.s3_client, + &state.s3_bucket, + &thumbnail_key, + thumbnail_data.clone(), + "image/jpeg", + &state.cdn_domain, + ) + .await?; - // chats by member_id - pubsub.subscribe(format!("chat:{}", listener_id)).await.unwrap(); - println!("'chat:{}' pubsub subscribed", listener_id); + // Prepare original image data + let mut original_buffer = Vec::new(); + image.write_to(&mut Cursor::new(&mut original_buffer), image::ImageFormat::Jpeg) + .map_err(|_| ErrorInternalServerError("Failed to read image data"))?; + + // Upload the original image + let image_key = format!("{}.{}", file_path, extension); + let image_url = upload_to_s3( + &state.s3_client, + &state.s3_bucket, + &image_key, + original_buffer.clone(), + mime_type.essence_str(), + &state.cdn_domain, + ) + .await?; - // messages by chat_id - for chat_id in &chats { - let channel_name = format!("message:{}", chat_id); - pubsub.subscribe(&channel_name).await.unwrap(); - println!("'{}' subscribed", channel_name); - } + // Update quota and save filename + check_and_update_quota(&mut state.redis.clone(), user_id, original_buffer.len() as u64).await?; + save_filename_in_redis(&mut state.redis.clone(), user_id, &image_key).await?; - while let Some(msg) = pubsub.on_message().next().await { - let redis_message_str: String = msg.get_payload().unwrap(); - let redis_message_data: RedisMessageData = serde_json::from_str(&redis_message_str).unwrap(); - let prepared_message_data = SSEMessageData { - payload: redis_message_data.payload, - action: redis_message_data.action, - entity: msg.get_channel_name() - .to_owned() - .split(":") - .next() - .unwrap_or("") - .to_string() - }; - if data::is_fitting( - listener_id, - prepared_message_data.clone(), - ) - .await - .is_ok() - { - let prepared_message_str = serde_json::to_string(&prepared_message_data).unwrap(); - let send_result = tx.send(prepared_message_str.clone()); - if send_result.is_err() { - // remove author from online list - let _ = con - .srem::<&str, &i32, usize>("authors-online", &listener_id) - .await - .map_err(|e| { - eprintln!("Failed to remove author from online list: {}", e); - ServerError("Internal Server Error") - }); - break; - } else { - println!("[handler] message handled {}", prepared_message_str); - } - }; - } - }); - state - .tasks - .lock() - .unwrap() - .insert(format!("{}", listener_id.clone()), handle); + return Ok(HttpResponse::Ok().body(format!("Image and thumbnail uploaded to: {}", image_url))); + } - let server_event_stream = futures::stream::unfold(rx, |mut rx| async { - let result = rx.recv().await; - match result { - Ok(server_event) => { - // Generate a random UUID as the event ID - let event_id = format!("{}", Uuid::new_v4()); + // Handle non-image files + let file_data = std::fs::read(&file_path).map_err(|_| ErrorInternalServerError("Failed to read file"))?; + let file_size = file_data.len() as u64; - let formatted_server_event = format!( - "id: {}\ndata: {}\n\n", - event_id, - server_event - ); + // Check and update the user's quota + check_and_update_quota(&mut state.redis.clone(), user_id, file_size).await?; - Some((Ok::<_, actix_web::Error>(Bytes::from(formatted_server_event)), rx)) - }, - Err(_) => None, - } - }); + // Upload the file + let file_key = format!("{}.{}", file_path, extension); + let file_url = upload_to_s3( + &state.s3_client, + &state.s3_bucket, + &file_key, + file_data, + mime_type.essence_str(), + &state.cdn_domain, + ) + .await?; - Ok(HttpResponse::Ok() - .append_header(("content-type", "text/event-stream")) - .streaming(server_event_stream)) + // Save the filename in Redis for this user + save_filename_in_redis(&mut state.redis.clone(), user_id, &file_key).await?; + + Ok(HttpResponse::Ok().body(format!("File uploaded to: {}", file_url))) } +// Save filename in Redis for a specific user +async fn save_filename_in_redis( + redis: &mut MultiplexedConnection, + user_id: &str, + filename: &str, +) -> Result<(), actix_web::Error> { + redis.sadd(user_id, filename).await.map_err(|_| ErrorInternalServerError("Failed to save filename in Redis")) +} + +// Main function to start the server #[actix_web::main] async fn main() -> std::io::Result<()> { - let redis_url = env::var("REDIS_URL").unwrap_or_else(|_| String::from("redis://127.0.0.1/")); - let client = redis::Client::open(redis_url.clone()).unwrap(); - let tasks = Arc::new(Mutex::new(HashMap::new())); - let state = AppState { - tasks: tasks.clone(), - redis: client.clone(), - }; - println!("Starting..."); - if let Ok(sentry_dsn) = Dsn::from_str( - &env::var("GLITCHTIP_DSN").unwrap_or_default(), - ) { - let sentry_options = sentry::ClientOptions { - release: sentry::release_name!(), - ..Default::default() - }; - let _guard = sentry::init((sentry_dsn, sentry_options)); - println!("Sentry initialized..."); - } else { - eprintln!("Invalid DSN, sentry was not initialized."); - } + let redis_url = env::var("REDIS_URL").expect("REDIS_URL must be set"); + let redis_client = RedisClient::open(redis_url).expect("Invalid Redis URL"); + let redis_connection = redis_client.get_multiplexed_async_connection().await.ok().unwrap(); + + // Initialize AWS S3 client + let s3_bucket = env::var("S3_BUCKET").expect("S3_BUCKET must be set"); + let cdn_domain = env::var("CDN_DOMAIN").expect("CDN_DOMAIN must be set"); + let config = load_defaults(BehaviorVersion::latest()).await; + let s3_client = S3Client::new(&config); + + // Create application state + let app_state = web::Data::new(AppState { + redis: redis_connection, + s3_client, + s3_bucket, + cdn_domain, + }); + + // Start HTTP server HttpServer::new(move || { App::new() - .wrap(sentry_actix::Sentry::new()) + .app_data(app_state.clone()) .wrap(Logger::default()) - .app_data(web::Data::new(state.clone())) - .route("/", web::get().to(connect_handler)) - .route("/{token}", web::get().to(connect_handler)) + .route("/{path:.*}", web::get().to(proxy_handler)) }) - .bind("0.0.0.0:8080")? + .bind("127.0.0.1:8080")? .run() .await }