diff --git a/.env.example b/.env.example index 84f2ed2..5e60b08 100644 --- a/.env.example +++ b/.env.example @@ -24,6 +24,8 @@ REDLIB_DEFAULT_WIDE=off REDLIB_DEFAULT_POST_SORT=hot # Set the default comment sort method (options: confidence, top, new, controversial, old) REDLIB_DEFAULT_COMMENT_SORT=confidence +# Enable blurring Spoiler content by default +REDLIB_DEFAULT_BLUR_SPOILER=off # Enable showing NSFW content by default REDLIB_DEFAULT_SHOW_NSFW=off # Enable blurring NSFW content by default @@ -36,8 +38,12 @@ REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off # Define a default list of subreddit subscriptions (format: sub1+sub2+sub3) REDLIB_DEFAULT_SUBSCRIPTIONS= +# Define a default list of subreddit filters (format: sub1+sub2+sub3) +REDLIB_DEFAULT_FILTERS= # Hide awards by default REDLIB_DEFAULT_HIDE_AWARDS=off +# Hide sidebar and summary +REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY=off # Disable the confirmation before visiting Reddit REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION=off # Hide score by default diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/build-artifacts.yaml b/.github/workflows/build-artifacts.yaml index 57632ef..695d1bf 100644 --- a/.github/workflows/build-artifacts.yaml +++ b/.github/workflows/build-artifacts.yaml @@ -7,6 +7,8 @@ on: - "compose.*" branches: - "main" + release: + types: [published] env: CARGO_TERM_COLOR: always @@ -60,7 +62,6 @@ jobs: - name: Upload release uses: softprops/action-gh-release@v1 - if: github.base_ref != 'main' && github.event_name == 'release' with: tag_name: ${{ steps.version.outputs.VERSION }} name: ${{ steps.version.outputs.VERSION }} - ${{ github.event.head_commit.message }} diff --git a/.github/workflows/main-rust.yml b/.github/workflows/main-rust.yml index 44476a1..ea44565 100644 --- a/.github/workflows/main-rust.yml +++ b/.github/workflows/main-rust.yml @@ -30,9 +30,15 @@ jobs: with: toolchain: stable + - name: Install musl-gcc + run: sudo apt-get install musl-tools + + - name: Install cargo musl target + run: rustup target add x86_64-unknown-linux-musl + # Building actions - name: Build - run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-gnu + run: RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-unknown-linux-musl - name: Versions id: version @@ -45,17 +51,17 @@ jobs: run: cargo publish --no-verify --token ${{ secrets.CARGO_REGISTRY_TOKEN }} - name: Calculate SHA512 checksum - run: sha512sum target/x86_64-unknown-linux-gnu/release/redlib > redlib.sha512 + run: sha512sum target/x86_64-unknown-linux-musl/release/redlib > redlib.sha512 - name: Calculate SHA256 checksum - run: sha256sum target/x86_64-unknown-linux-gnu/release/redlib > redlib.sha256 + run: sha256sum target/x86_64-unknown-linux-musl/release/redlib > redlib.sha256 - uses: actions/upload-artifact@v3 name: Upload a Build Artifact with: name: redlib path: | - target/x86_64-unknown-linux-gnu/release/redlib + target/x86_64-unknown-linux-musl/release/redlib redlib.sha512 redlib.sha256 @@ -68,7 +74,7 @@ jobs: name: ${{ steps.version.outputs.VERSION }} - ${{ github.event.head_commit.message }} draft: true files: | - target/x86_64-unknown-linux-gnu/release/redlib + target/x86_64-unknown-linux-musl/release/redlib redlib.sha512 redlib.sha256 body: | diff --git a/.gitignore b/.gitignore index 323a69d..6ca325c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ /target .env +redlib.toml + # Idea Files .idea/ + +# nix files +.direnv/ +result diff --git a/.replit b/.replit index e973713..b857cc1 100644 --- a/.replit +++ b/.replit @@ -1,2 +1,2 @@ -run = "while :; do set -ex; nix-env -iA nixpkgs.unzip; curl -o./redlib.zip -fsSL -- https://nightly.link/redlib-org/redlib/workflows/main-rust/main/redlib.zip; unzip -n redlib.zip; mv target/x86_64-unknown-linux-gnu/release/redlib .; chmod +x redlib; set +e; ./redlib -H 63115200; sleep 1; done" +run = "while :; do set -ex; nix-env -iA nixpkgs.unzip; curl -o./redlib.zip -fsSL -- https://nightly.link/redlib-org/redlib/workflows/main-rust/main/redlib.zip; unzip -n redlib.zip; mv target/x86_64-unknown-linux-musl/release/redlib .; chmod +x redlib; set +e; ./redlib -H 63115200; sleep 1; done" language = "bash" diff --git a/Cargo.lock b/Cargo.lock index 96e0f2b..8dc4c6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -61,15 +61,21 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "askama" @@ -92,7 +98,7 @@ dependencies = [ "mime_guess", "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -112,13 +118,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -136,15 +142,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -157,21 +163,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.4.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -221,22 +221,22 @@ checksum = "3108fe6fe7ac796fb7625bdde8fa2b67b5a7731496251ca57c7b8cadd78a16a1" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cached" -version = "0.48.1" +version = "0.51.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355face540df58778b96814c48abb3c2ed67c4878a8087ab1819c1fedeec505f" +checksum = "0feb64151eed3da6107fddd2d717a6ca4b9dbd74e43784c55c841d1abfe5a295" dependencies = [ "ahash", "async-trait", "cached_proc_macro", "cached_proc_macro_types", "futures", - "hashbrown 0.14.3", + "hashbrown", "instant", "once_cell", "thiserror", @@ -245,14 +245,14 @@ dependencies = [ [[package]] name = "cached_proc_macro" -version = "0.19.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d52f526f7cbc875b296856ca8c964a9f6290556922c303a8a3883e3c676e6a1" +checksum = "771aa57f3b17da6c8bcacb187bb9ec9bc81c8160e72342e67c329e0e1651a669" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -263,9 +263,9 @@ checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cc" -version = "1.0.89" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" [[package]] name = "cfg-if" @@ -284,18 +284,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstyle", "clap_lex", @@ -303,15 +303,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "cookie" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "time", "version_check", @@ -353,9 +353,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -382,27 +382,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -506,9 +506,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -516,9 +516,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fnv" @@ -579,9 +579,9 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ "fastrand", "futures-core", @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -638,9 +638,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "globset" @@ -657,9 +657,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -676,18 +676,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -723,9 +714,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -741,9 +732,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -798,19 +789,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -828,21 +819,21 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libflate" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" dependencies = [ "adler32", "core2", @@ -853,26 +844,26 @@ dependencies = [ [[package]] name = "libflate_lz77" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" dependencies = [ "core2", - "hashbrown 0.13.2", + "hashbrown", "rle-decode-fast", ] [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lipsum" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5e9ef2d2ad6fe67a59ace27c203c8d3a71d195532ee82e3bbe0d5f9a9ca541" +checksum = "636860251af8963cc40f6b4baadee105f02e21b28131d76eba8e40ce84ab8064" dependencies = [ "rand", "rand_chacha", @@ -880,9 +871,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -896,9 +887,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -924,9 +915,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -994,9 +985,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1021,9 +1012,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1031,15 +1022,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -1050,9 +1041,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1084,9 +1075,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1109,9 +1100,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1143,8 +1134,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "redlib" -version = "0.31.2" +version = "0.35.1" dependencies = [ + "arc-swap", "askama", "base64", "brotli", @@ -1180,18 +1172,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1201,9 +1193,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1212,9 +1204,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "ring" @@ -1257,9 +1249,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.3.0" +version = "8.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745" +checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -1268,22 +1260,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.3.0" +version = "8.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8" +checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.52", + "syn", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.3.0" +version = "8.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581" +checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32" dependencies = [ "globset", "sha2", @@ -1292,17 +1284,17 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -1311,9 +1303,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", @@ -1338,9 +1330,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ "base64", "rustls-pki-types", @@ -1348,15 +1340,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.3.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -1377,9 +1369,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1407,9 +1399,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sealed_test" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a608d94641cc17fe203b102db2ae86d47a236630192f0244ddbbbb0044c0272" +checksum = "2a1867f8f005bd7fb73c367e2e45dd628417906a2ca27597fe59cbf04279a222" dependencies = [ "fs_extra", "rusty-forkfork", @@ -1419,21 +1411,21 @@ dependencies = [ [[package]] name = "sealed_test_derive" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b672e005ae58fef5da619d90b9f1c5b44b061890f4a371b3c96257a8a15e697" +checksum = "77253fb2d4451418d07025826028bcb96ee42d3e58859689a70ce62908009db6" dependencies = [ "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -1442,9 +1434,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -1452,29 +1444,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -1483,18 +1475,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -1516,9 +1508,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -1534,15 +1526,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1556,32 +1548,21 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -1611,29 +1592,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1654,9 +1635,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -1664,9 +1645,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -1679,9 +1660,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -1698,13 +1679,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -1720,23 +1701,22 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -1746,18 +1726,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -1835,9 +1815,9 @@ dependencies = [ [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -1847,9 +1827,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -1858,9 +1838,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.7.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", ] @@ -1905,37 +1885,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -1951,7 +1909,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1971,17 +1929,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1992,9 +1951,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2004,9 +1963,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2016,9 +1975,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2028,9 +1993,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2040,9 +2005,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -2052,9 +2017,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2064,41 +2029,41 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 2b753e7..880c021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "redlib" description = " Alternative private front-end to Reddit" license = "AGPL-3.0" repository = "https://github.com/redlib-org/redlib" -version = "0.31.2" +version = "0.35.1" authors = [ "Matthew Esposito ", "spikecodes <19519553+spikecodes@users.noreply.github.com>", @@ -12,7 +12,7 @@ edition = "2021" [dependencies] askama = { version = "0.12.1", default-features = false } -cached = { version = "0.48.1", features = ["async"] } +cached = { version = "0.51.3", features = ["async"] } clap = { version = "4.4.11", default-features = false, features = [ "std", "env", @@ -31,18 +31,19 @@ time = { version = "0.3.31", features = ["local-offset"] } url = "2.5.0" rust-embed = { version = "8.1.0", features = ["include-exclude"] } libflate = "2.0.0" -brotli = { version = "3.4.0", features = ["std"] } +brotli = { version = "6.0.0", features = ["std"] } toml = "0.8.8" once_cell = "1.19.0" serde_yaml = "0.9.29" build_html = "2.4.0" uuid = { version = "1.6.1", features = ["v4"] } -base64 = "0.21.5" +base64 = "0.22.1" fastrand = "2.0.1" log = "0.4.20" pretty_env_logger = "0.5.0" dotenvy = "0.15.7" rss = "2.0.7" +arc-swap = "1.7.1" [dev-dependencies] lipsum = "0.9.0" diff --git a/Dockerfile b/Dockerfile index 9412565..314a784 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ USER redlib EXPOSE 8080 # Run a healthcheck every minute to make sure redlib is functional -HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider --q http://localhost:8080/settings || exit 1 +HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider -q http://localhost:8080/settings || exit 1 CMD ["redlib"] diff --git a/README.md b/README.md index 90bacf8..533b628 100644 --- a/README.md +++ b/README.md @@ -203,12 +203,6 @@ docker logs -f redlib ### Docker CLI -> [!IMPORTANT] -> If deploying on: -> -> - an `arm64` platform, use the `quay.io/redlib/redlib:latest-arm` image instead. -> - an `armv7` platform, use the `quay.io/redlib/redlib:latest-armv7` image instead. - Deploy Redlib: ```bash @@ -380,12 +374,13 @@ REDLIB_DEFAULT_USE_HLS = "on" Assign a default value for each instance-specific setting by passing environment variables to Redlib in the format `REDLIB_{X}`. Replace `{X}` with the setting name (see list below) in capital letters. -| Name | Possible values | Default value | Description | -| ------------------------- | --------------- | ---------------- | --------------------------------------------------------------------------------------------------------- | -| `SFW_ONLY` | `["on", "off"]` | `off` | Enables SFW-only mode for the instance, i.e. all NSFW content is filtered. | -| `BANNER` | String | (empty) | Allows the server to set a banner to be displayed. Currently this is displayed on the instance info page. | -| `ROBOTS_DISABLE_INDEXING` | `["on", "off"]` | `off` | Disables indexing of the instance by search engines. | +| Name | Possible values | Default value | Description | +| ------------------------- | --------------- | ---------------- | --------------------------------------------------------------------------------------------------------- | +| `SFW_ONLY` | `["on", "off"]` | `off` | Enables SFW-only mode for the instance, i.e. all NSFW content is filtered. | +| `BANNER` | String | (empty) | Allows the server to set a banner to be displayed. Currently this is displayed on the instance info page. | +| `ROBOTS_DISABLE_INDEXING` | `["on", "off"]` | `off` | Disables indexing of the instance by search engines. | | `PUSHSHIFT_FRONTEND` | String | `undelete.pullpush.io` | Allows the server to set the Pushshift frontend to be used with "removed" links. | +| `PORT` | Integer 0-65535 | `8080` | The **internal** port Redlib listens on. | ## Default user settings @@ -393,12 +388,13 @@ Assign a default value for each user-modifiable setting by passing environment v | Name | Possible values | Default value | | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| `THEME` | `["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold", "rosebox", "gruvboxdark", "gruvboxlight"]` | `system` | +| `THEME` | `["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold", "rosebox", "gruvboxdark", "gruvboxlight", "tokyoNight", "icebergDark"]` | `system` | | `FRONT_PAGE` | `["default", "popular", "all"]` | `default` | | `LAYOUT` | `["card", "clean", "compact"]` | `card` | | `WIDE` | `["on", "off"]` | `off` | | `POST_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` | | `COMMENT_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` | +| `BLUR_SPOILER` | `["on", "off"]` | `off` | | `SHOW_NSFW` | `["on", "off"]` | `off` | | `BLUR_NSFW` | `["on", "off"]` | `off` | | `USE_HLS` | `["on", "off"]` | `off` | diff --git a/app.json b/app.json index e1ae650..d73780c 100644 --- a/app.json +++ b/app.json @@ -29,6 +29,9 @@ "REDLIB_DEFAULT_POST_SORT": { "required": false }, + "REDLIB_DEFAULT_BLUR_SPOILER": { + "required": false + }, "REDLIB_DEFAULT_SHOW_NSFW": { "required": false }, @@ -59,6 +62,9 @@ "REDLIB_DEFAULT_SUBSCRIPTIONS": { "required": false }, + "REDLIB_DEFAULT_FILTERS": { + "required": false + }, "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION": { "required": false }, diff --git a/compose.yaml b/compose.yaml index 493e37e..4260d65 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,8 +1,6 @@ services: redlib: image: quay.io/redlib/redlib:latest - # image: quay.io/redlib/redlib:latest-arm # uncomment if you use arm64 - # image: quay.io/redlib/redlib:latest-armv7 # uncomment if you use armv7 restart: always container_name: "redlib" ports: diff --git a/contrib/redlib.conf b/contrib/redlib.conf index 880bc7a..43c1d86 100644 --- a/contrib/redlib.conf +++ b/contrib/redlib.conf @@ -6,6 +6,7 @@ PORT=12345 #REDLIB_DEFAULT_WIDE=off #REDLIB_DEFAULT_POST_SORT=hot #REDLIB_DEFAULT_COMMENT_SORT=confidence +#REDLIB_DEFAULT_BLUR_SPOILER=off #REDLIB_DEFAULT_SHOW_NSFW=off #REDLIB_DEFAULT_BLUR_NSFW=off #REDLIB_DEFAULT_USE_HLS=off diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4569244 --- /dev/null +++ b/flake.lock @@ -0,0 +1,106 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717025063, + "narHash": "sha256-dIubLa56W9sNNz0e8jGxrX3CAkPXsq7snuFA/Ie6dn8=", + "owner": "ipetkov", + "repo": "crane", + "rev": "480dff0be03dac0e51a8dfc26e882b0d123a450e", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1717112898, + "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717121863, + "narHash": "sha256-/3sxIe7MZqF/jw1RTQCSmgTjwVod43mmrk84m50MJQ4=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "2a7b53172ed08f856b8382d7dcfd36a4e0cbd866", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8bcacf6 --- /dev/null +++ b/flake.nix @@ -0,0 +1,71 @@ +{ + description = "Redlib: Private front-end for Reddit"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + }; + + outputs = { nixpkgs, crane, flake-utils, rust-overlay, ... }: + flake-utils.lib.eachSystem [ "x86_64-linux" ] (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ (import rust-overlay) ]; + }; + + inherit (pkgs) lib; + + rustToolchain = pkgs.rust-bin.stable.latest.default.override { + targets = [ "x86_64-unknown-linux-musl" ]; + }; + + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; + + + src = lib.cleanSourceWith { + src = craneLib.path ./.; + filter = path: type: + (lib.hasInfix "/templates/" path) || + (lib.hasInfix "/static/" path) || + (craneLib.filterCargoSources path type); + }; + + redlib = craneLib.buildPackage { + inherit src; + strictDeps = true; + doCheck = false; + + CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; + CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static"; + }; + in + { + checks = { + my-crate = redlib; + }; + + packages.default = redlib; + packages.docker = pkgs.dockerTools.buildImage { + name = "quay.io/redlib/redlib"; + tag = "latest"; + created = "now"; + copyToRoot = with pkgs.dockerTools; [ caCertificates fakeNss ]; + config.Cmd = "${redlib}/bin/redlib"; + }; + }); +} diff --git a/scripts/load_test.py b/scripts/load_test.py new file mode 100644 index 0000000..00e4793 --- /dev/null +++ b/scripts/load_test.py @@ -0,0 +1,31 @@ +import requests +from bs4 import BeautifulSoup +from concurrent.futures import ThreadPoolExecutor + +base_url = "http://localhost:8080" + +full_path = f"{base_url}/r/politics" + +ctr = 0 + +def fetch_url(url): + global ctr + response = requests.get(url) + ctr += 1 + print(f"Request count: {ctr}") + return response + +while full_path: + response = requests.get(full_path) + ctr += 1 + print(f"Request count: {ctr}") + soup = BeautifulSoup(response.text, 'html.parser') + comment_links = soup.find_all('a', class_='post_comments') + comment_urls = [base_url + link['href'] for link in comment_links] + with ThreadPoolExecutor(max_workers=10) as executor: + executor.map(fetch_url, comment_urls) + next_link = soup.find('a', accesskey='N') + if next_link: + full_path = base_url + next_link['href'] + else: + break diff --git a/src/client.rs b/src/client.rs index bf318c7..bc07168 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,17 +1,20 @@ +use arc_swap::ArcSwap; use cached::proc_macro::cached; use futures_lite::future::block_on; use futures_lite::{future::Boxed, FutureExt}; use hyper::client::HttpConnector; +use hyper::header::HeaderValue; use hyper::{body, body::Buf, client, header, Body, Client, Method, Request, Response, Uri}; use hyper_rustls::HttpsConnector; use libflate::gzip; -use log::error; +use log::{error, trace, warn}; use once_cell::sync::Lazy; use percent_encoding::{percent_encode, CONTROLS}; use serde_json::Value; +use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicBool, AtomicU16}; use std::{io, result::Result}; -use tokio::sync::RwLock; use crate::dbg_msg; use crate::oauth::{force_refresh_token, token_daemon, Oauth}; @@ -19,6 +22,7 @@ use crate::server::RequestExt; use crate::utils::format_url; const REDDIT_URL_BASE: &str = "https://oauth.reddit.com"; +const ALTERNATIVE_REDDIT_URL_BASE: &str = "https://www.reddit.com"; pub static CLIENT: Lazy>> = Lazy::new(|| { let https = hyper_rustls::HttpsConnectorBuilder::new() @@ -30,12 +34,16 @@ pub static CLIENT: Lazy>> = Lazy::new(|| { client::Client::builder().build(https) }); -pub static OAUTH_CLIENT: Lazy> = Lazy::new(|| { +pub static OAUTH_CLIENT: Lazy> = Lazy::new(|| { let client = block_on(Oauth::new()); tokio::spawn(token_daemon()); - RwLock::new(client) + ArcSwap::new(client.into()) }); +pub static OAUTH_RATELIMIT_REMAINING: AtomicU16 = AtomicU16::new(99); + +pub static OAUTH_IS_ROLLING_OVER: AtomicBool = AtomicBool::new(false); + /// Gets the canonical path for a resource on Reddit. This is accomplished by /// making a `HEAD` request to Reddit at the path given in `path`. /// @@ -171,7 +179,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo let client: Client<_, Body> = CLIENT.clone(); let (token, vendor_id, device_id, user_agent, loid) = { - let client = block_on(OAUTH_CLIENT.read()); + let client = OAUTH_CLIENT.load_full(); ( client.token.clone(), client.headers_map.get("Client-Vendor-Id").cloned().unwrap_or_default(), @@ -180,6 +188,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo client.headers_map.get("x-reddit-loid").cloned().unwrap_or_default(), ) }; + // Build request to Reddit. When making a GET, request gzip compression. // (Reddit doesn't do brotli yet.) let builder = Request::builder() @@ -214,12 +223,13 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo if !redirect { return Ok(response); }; - + let location_header = response.headers().get(header::LOCATION); + if location_header == Some(&HeaderValue::from_static("https://www.reddit.com/")) { + return Err("Reddit response was invalid".to_string()); + } return request( method, - response - .headers() - .get(header::LOCATION) + location_header .map(|val| { // We need to make adjustments to the URI // we get back from Reddit. Namely, we @@ -232,7 +242,11 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo // required. // // 2. Percent-encode the path. - let new_path = percent_encode(val.as_bytes(), CONTROLS).to_string().trim_start_matches(REDDIT_URL_BASE).to_string(); + let new_path = percent_encode(val.as_bytes(), CONTROLS) + .to_string() + .trim_start_matches(REDDIT_URL_BASE) + .trim_start_matches(ALTERNATIVE_REDDIT_URL_BASE) + .to_string(); format!("{new_path}{}raw_json=1", if new_path.contains('?') { "&" } else { "?" }) }) .unwrap_or_default() @@ -291,7 +305,7 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo } } Err(e) => { - dbg_msg!("{} {}: {}", method, path, e); + dbg_msg!("{method} {REDDIT_URL_BASE}{path}: {}", e); Err(e.to_string()) } @@ -306,19 +320,56 @@ fn request(method: &'static Method, path: String, redirect: bool, quarantine: bo #[cached(size = 100, time = 30, result = true)] pub async fn json(path: String, quarantine: bool) -> Result { // Closure to quickly build errors - let err = |msg: &str, e: String| -> Result { + let err = |msg: &str, e: String, path: String| -> Result { // eprintln!("{} - {}: {}", url, msg, e); - Err(format!("{msg}: {e}")) + Err(format!("{msg}: {e} | {path}")) }; + // First, handle rolling over the OAUTH_CLIENT if need be. + let current_rate_limit = OAUTH_RATELIMIT_REMAINING.load(Ordering::SeqCst); + let is_rolling_over = OAUTH_IS_ROLLING_OVER.load(Ordering::SeqCst); + if current_rate_limit < 10 && !is_rolling_over { + warn!("Rate limit {current_rate_limit} is low. Spawning force_refresh_token()"); + tokio::spawn(force_refresh_token()); + } + OAUTH_RATELIMIT_REMAINING.fetch_sub(1, Ordering::SeqCst); + // Fetch the url... match reddit_get(path.clone(), quarantine).await { Ok(response) => { let status = response.status(); + let reset: Option = if let (Some(remaining), Some(reset), Some(used)) = ( + response.headers().get("x-ratelimit-remaining").and_then(|val| val.to_str().ok().map(|s| s.to_string())), + response.headers().get("x-ratelimit-reset").and_then(|val| val.to_str().ok().map(|s| s.to_string())), + response.headers().get("x-ratelimit-used").and_then(|val| val.to_str().ok().map(|s| s.to_string())), + ) { + trace!( + "Ratelimit remaining: Header says {remaining}, we have {current_rate_limit}. Resets in {reset}. Rollover: {}. Ratelimit used: {used}", + if is_rolling_over { "yes" } else { "no" }, + ); + Some(reset) + } else { + None + }; + // asynchronously aggregate the chunks of the body match hyper::body::aggregate(response).await { Ok(body) => { + let has_remaining = body.has_remaining(); + + if !has_remaining { + // Rate limited, so spawn a force_refresh_token() + tokio::spawn(force_refresh_token()); + return match reset { + Some(val) => Err(format!( + "Reddit rate limit exceeded. Try refreshing in a few seconds.\ + Rate limit will reset in: {val}" + )), + None => Err("Reddit rate limit exceeded".to_string()), + }; + } + // Parse the response from Reddit as JSON match serde_json::from_reader(body.reader()) { Ok(value) => { @@ -331,7 +382,7 @@ pub async fn json(path: String, quarantine: bool) -> Result { let () = force_refresh_token().await; return Err("OAuth token has expired. Please refresh the page!".to_string()); } - Err(format!("Reddit error {} \"{}\": {}", json["error"], json["reason"], json["message"])) + Err(format!("Reddit error {} \"{}\": {} | {path}", json["error"], json["reason"], json["message"])) } else { Ok(json) } @@ -341,21 +392,24 @@ pub async fn json(path: String, quarantine: bool) -> Result { if status.is_server_error() { Err("Reddit is having issues, check if there's an outage".to_string()) } else { - err("Failed to parse page JSON data", e.to_string()) + err("Failed to parse page JSON data", e.to_string(), path) } } } } - Err(e) => err("Failed receiving body from Reddit", e.to_string()), + Err(e) => err("Failed receiving body from Reddit", e.to_string(), path), } } - Err(e) => err("Couldn't send request to Reddit", e), + Err(e) => err("Couldn't send request to Reddit", e, path), } } +#[cfg(test)] +static POPULAR_URL: &str = "/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL"; + #[tokio::test(flavor = "multi_thread")] async fn test_localization_popular() { - let val = json("/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL".to_string(), false).await.unwrap(); + let val = json(POPULAR_URL.to_string(), false).await.unwrap(); assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap()); } diff --git a/src/config.rs b/src/config.rs index 21a3adc..2b8c752 100644 --- a/src/config.rs +++ b/src/config.rs @@ -48,6 +48,10 @@ pub struct Config { #[serde(alias = "LIBREDDIT_DEFAULT_POST_SORT")] pub(crate) default_post_sort: Option, + #[serde(rename = "REDLIB_DEFAULT_BLUR_SPOILER")] + #[serde(alias = "LIBREDDIT_DEFAULT_BLUR_SPOILER")] + pub(crate) default_blur_spoiler: Option, + #[serde(rename = "REDLIB_DEFAULT_SHOW_NSFW")] #[serde(alias = "LIBREDDIT_DEFAULT_SHOW_NSFW")] pub(crate) default_show_nsfw: Option, @@ -68,6 +72,10 @@ pub struct Config { #[serde(alias = "LIBREDDIT_DEFAULT_HIDE_AWARDS")] pub(crate) default_hide_awards: Option, + #[serde(rename = "REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY")] + #[serde(alias = "LIBREDDIT_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY")] + pub(crate) default_hide_sidebar_and_summary: Option, + #[serde(rename = "REDLIB_DEFAULT_HIDE_SCORE")] #[serde(alias = "LIBREDDIT_DEFAULT_HIDE_SCORE")] pub(crate) default_hide_score: Option, @@ -76,6 +84,10 @@ pub struct Config { #[serde(alias = "LIBREDDIT_DEFAULT_SUBSCRIPTIONS")] pub(crate) default_subscriptions: Option, + #[serde(rename = "REDLIB_DEFAULT_FILTERS")] + #[serde(alias = "LIBREDDIT_DEFAULT_FILTERS")] + pub(crate) default_filters: Option, + #[serde(rename = "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")] #[serde(alias = "LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")] pub(crate) default_disable_visit_reddit_confirmation: Option, @@ -122,13 +134,16 @@ impl Config { default_post_sort: parse("REDLIB_DEFAULT_POST_SORT"), default_wide: parse("REDLIB_DEFAULT_WIDE"), default_comment_sort: parse("REDLIB_DEFAULT_COMMENT_SORT"), + default_blur_spoiler: parse("REDLIB_DEFAULT_BLUR_SPOILER"), default_show_nsfw: parse("REDLIB_DEFAULT_SHOW_NSFW"), default_blur_nsfw: parse("REDLIB_DEFAULT_BLUR_NSFW"), default_use_hls: parse("REDLIB_DEFAULT_USE_HLS"), - default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS"), + default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION"), default_hide_awards: parse("REDLIB_DEFAULT_HIDE_AWARDS"), + default_hide_sidebar_and_summary: parse("REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY"), default_hide_score: parse("REDLIB_DEFAULT_HIDE_SCORE"), default_subscriptions: parse("REDLIB_DEFAULT_SUBSCRIPTIONS"), + default_filters: parse("REDLIB_DEFAULT_FILTERS"), default_disable_visit_reddit_confirmation: parse("REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION"), banner: parse("REDLIB_BANNER"), robots_disable_indexing: parse("REDLIB_ROBOTS_DISABLE_INDEXING"), @@ -145,14 +160,17 @@ fn get_setting_from_config(name: &str, config: &Config) -> Option { "REDLIB_DEFAULT_LAYOUT" => config.default_layout.clone(), "REDLIB_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(), "REDLIB_DEFAULT_POST_SORT" => config.default_post_sort.clone(), + "REDLIB_DEFAULT_BLUR_SPOILER" => config.default_blur_spoiler.clone(), "REDLIB_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(), "REDLIB_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(), "REDLIB_DEFAULT_USE_HLS" => config.default_use_hls.clone(), "REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(), "REDLIB_DEFAULT_WIDE" => config.default_wide.clone(), "REDLIB_DEFAULT_HIDE_AWARDS" => config.default_hide_awards.clone(), + "REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY" => config.default_hide_sidebar_and_summary.clone(), "REDLIB_DEFAULT_HIDE_SCORE" => config.default_hide_score.clone(), "REDLIB_DEFAULT_SUBSCRIPTIONS" => config.default_subscriptions.clone(), + "REDLIB_DEFAULT_FILTERS" => config.default_filters.clone(), "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION" => config.default_disable_visit_reddit_confirmation.clone(), "REDLIB_BANNER" => config.banner.clone(), "REDLIB_ROBOTS_DISABLE_INDEXING" => config.robots_disable_indexing.clone(), @@ -225,6 +243,12 @@ fn test_default_subscriptions() { assert_eq!(get_setting("REDLIB_DEFAULT_SUBSCRIPTIONS"), Some("news+bestof".into())); } +#[test] +#[sealed_test(env = [("REDLIB_DEFAULT_FILTERS", "news+bestof")])] +fn test_default_filters() { + assert_eq!(get_setting("REDLIB_DEFAULT_FILTERS"), Some("news+bestof".into())); +} + #[test] #[sealed_test] fn test_pushshift() { diff --git a/src/duplicates.rs b/src/duplicates.rs index d92e8ab..5a7653e 100644 --- a/src/duplicates.rs +++ b/src/duplicates.rs @@ -151,7 +151,7 @@ pub async fn item(req: Request) -> Result, String> { } if have_after { - before = "t3_".to_owned(); + "t3_".clone_into(&mut before); before.push_str(&duplicates[0].id); } @@ -161,7 +161,7 @@ pub async fn item(req: Request) -> Result, String> { if have_before { // The next batch will need to start from one after the // last post in the current batch. - after = "t3_".to_owned(); + "t3_".clone_into(&mut after); after.push_str(&duplicates[l - 1].id); // Here is where things get terrible. Notice that we @@ -182,7 +182,7 @@ pub async fn item(req: Request) -> Result, String> { match json(new_path, true).await { Ok(response) => { if !response[1]["data"]["children"].as_array().unwrap_or(&Vec::new()).is_empty() { - before = "t3_".to_owned(); + "t3_".clone_into(&mut before); before.push_str(&duplicates[0].id); } } diff --git a/src/instance_info.rs b/src/instance_info.rs index 462e09c..20ffc6d 100644 --- a/src/instance_info.rs +++ b/src/instance_info.rs @@ -141,11 +141,13 @@ impl InstanceInfo { ["Wide", &convert(&self.config.default_wide)], ["Comment sort", &convert(&self.config.default_comment_sort)], ["Post sort", &convert(&self.config.default_post_sort)], + ["Blur Spoiler", &convert(&self.config.default_blur_spoiler)], ["Show NSFW", &convert(&self.config.default_show_nsfw)], ["Blur NSFW", &convert(&self.config.default_blur_nsfw)], ["Use HLS", &convert(&self.config.default_use_hls)], ["Hide HLS notification", &convert(&self.config.default_hide_hls_notification)], ["Subscriptions", &convert(&self.config.default_subscriptions)], + ["Filters", &convert(&self.config.default_filters)], ]) .with_header_row(["Default preferences"]), ); @@ -173,11 +175,13 @@ impl InstanceInfo { Default wide: {:?}\n Default comment sort: {:?}\n Default post sort: {:?}\n + Default blur Spoiler: {:?}\n Default show NSFW: {:?}\n Default blur NSFW: {:?}\n Default use HLS: {:?}\n Default hide HLS notification: {:?}\n - Default subscriptions: {:?}\n", + Default subscriptions: {:?}\n + Default filters: {:?}\n", self.package_name, self.crate_version, self.git_commit, @@ -195,11 +199,13 @@ impl InstanceInfo { self.config.default_wide, self.config.default_comment_sort, self.config.default_post_sort, + self.config.default_blur_spoiler, self.config.default_show_nsfw, self.config.default_blur_nsfw, self.config.default_use_hls, self.config.default_hide_hls_notification, self.config.default_subscriptions, + self.config.default_filters, ) } StringType::Html => self.to_table(), diff --git a/src/main.rs b/src/main.rs index 24115b7..406a0d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,7 +135,7 @@ async fn main() { .long("address") .value_name("ADDRESS") .help("Sets address to listen on") - .default_value("0.0.0.0") + .default_value("[::]") .num_args(1), ) .arg( diff --git a/src/oauth.rs b/src/oauth.rs index cea7693..efdf41e 100644 --- a/src/oauth.rs +++ b/src/oauth.rs @@ -1,12 +1,12 @@ -use std::{collections::HashMap, time::Duration}; +use std::{collections::HashMap, sync::atomic::Ordering, time::Duration}; use crate::{ - client::{CLIENT, OAUTH_CLIENT}, + client::{CLIENT, OAUTH_CLIENT, OAUTH_IS_ROLLING_OVER, OAUTH_RATELIMIT_REMAINING}, oauth_resources::ANDROID_APP_VERSION_LIST, }; use base64::{engine::general_purpose, Engine as _}; use hyper::{client, Body, Method, Request}; -use log::info; +use log::{info, trace}; use serde_json::json; @@ -98,21 +98,13 @@ impl Oauth { Some(()) } - - async fn refresh(&mut self) -> Option<()> { - // Refresh is actually just a subsequent login with the same headers (without the old token - // or anything). This logic is handled in login, so we just call login again. - let refresh = self.login().await; - info!("Refreshing OAuth token... {}", if refresh.is_some() { "success" } else { "failed" }); - refresh - } } pub async fn token_daemon() { // Monitor for refreshing token loop { // Get expiry time - be sure to not hold the read lock - let expires_in = { OAUTH_CLIENT.read().await.expires_in }; + let expires_in = { OAUTH_CLIENT.load_full().expires_in }; // sleep for the expiry time minus 2 minutes let duration = Duration::from_secs(expires_in - 120); @@ -125,13 +117,22 @@ pub async fn token_daemon() { // Refresh token - in its own scope { - OAUTH_CLIENT.write().await.refresh().await; + force_refresh_token().await; } } } pub async fn force_refresh_token() { - OAUTH_CLIENT.write().await.refresh().await; + if OAUTH_IS_ROLLING_OVER.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() { + trace!("Skipping refresh token roll over, already in progress"); + return; + } + + trace!("Rolling over refresh token. Current rate limit: {}", OAUTH_RATELIMIT_REMAINING.load(Ordering::SeqCst)); + let new_client = Oauth::new().await; + OAUTH_CLIENT.swap(new_client.into()); + OAUTH_RATELIMIT_REMAINING.store(99, Ordering::SeqCst); + OAUTH_IS_ROLLING_OVER.store(false, Ordering::SeqCst); } #[derive(Debug, Clone, Default)] @@ -179,21 +180,21 @@ fn choose(list: &[T]) -> T { #[tokio::test(flavor = "multi_thread")] async fn test_oauth_client() { - assert!(!OAUTH_CLIENT.read().await.token.is_empty()); + assert!(!OAUTH_CLIENT.load_full().token.is_empty()); } #[tokio::test(flavor = "multi_thread")] async fn test_oauth_client_refresh() { - OAUTH_CLIENT.write().await.refresh().await.unwrap(); + force_refresh_token().await; } #[tokio::test(flavor = "multi_thread")] async fn test_oauth_token_exists() { - assert!(!OAUTH_CLIENT.read().await.token.is_empty()); + assert!(!OAUTH_CLIENT.load_full().token.is_empty()); } #[tokio::test(flavor = "multi_thread")] async fn test_oauth_headers_len() { - assert!(OAUTH_CLIENT.read().await.headers_map.len() >= 3); + assert!(OAUTH_CLIENT.load_full().headers_map.len() >= 3); } #[test] diff --git a/src/oauth_resources.rs b/src/oauth_resources.rs index 9a85cd1..3272939 100644 --- a/src/oauth_resources.rs +++ b/src/oauth_resources.rs @@ -4,6 +4,44 @@ // Filled in with real app versions pub static _IOS_APP_VERSION_LIST: &[&str; 1] = &[""]; pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ + "Version 2023.48.0/Build 1319123", + "Version 2023.49.0/Build 1321715", + "Version 2023.49.1/Build 1322281", + "Version 2023.50.0/Build 1332338", + "Version 2023.50.1/Build 1345844", + "Version 2024.02.0/Build 1368985", + "Version 2024.03.0/Build 1379408", + "Version 2024.04.0/Build 1391236", + "Version 2024.05.0/Build 1403584", + "Version 2024.06.0/Build 1418489", + "Version 2024.07.0/Build 1429651", + "Version 2024.08.0/Build 1439531", + "Version 2024.10.0/Build 1470045", + "Version 2024.10.1/Build 1478645", + "Version 2024.11.0/Build 1480707", + "Version 2024.12.0/Build 1494694", + "Version 2024.13.0/Build 1505187", + "Version 2024.14.0/Build 1520556", + "Version 2024.15.0/Build 1536823", + "Version 2024.16.0/Build 1551366", + "Version 2024.17.0/Build 1568106", + "Version 2024.18.0/Build 1577901", + "Version 2024.18.1/Build 1585304", + "Version 2024.19.0/Build 1593346", + "Version 2024.20.0/Build 1612800", + "Version 2024.20.1/Build 1615586", + "Version 2024.20.2/Build 1624969", + "Version 2024.21.0/Build 1631686", + "Version 2024.22.0/Build 1645257", + "Version 2024.22.1/Build 1652272", + "Version 2023.21.0/Build 956283", + "Version 2023.22.0/Build 968223", + "Version 2023.23.0/Build 983896", + "Version 2023.24.0/Build 998541", + "Version 2023.25.0/Build 1014750", + "Version 2023.25.1/Build 1018737", + "Version 2023.26.0/Build 1019073", + "Version 2023.27.0/Build 1031923", "Version 2023.28.0/Build 1046887", "Version 2023.29.0/Build 1059855", "Version 2023.30.0/Build 1078734", @@ -26,14 +64,14 @@ pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ "Version 2023.44.0/Build 1268622", "Version 2023.45.0/Build 1281371", "Version 2023.47.0/Build 1303604", - "Version 2023.48.0/Build 1319123", - "Version 2023.49.0/Build 1321715", - "Version 2023.49.1/Build 1322281", - "Version 2023.50.0/Build 1332338", - "Version 2023.50.1/Build 1345844", - "Version 2024.02.0/Build 1368985", - "Version 2024.03.0/Build 1379408", - "Version 2024.04.0/Build 1391236", + "Version 2022.42.0/Build 638508", + "Version 2022.43.0/Build 648277", + "Version 2022.44.0/Build 664348", + "Version 2022.45.0/Build 677985", + "Version 2023.01.0/Build 709875", + "Version 2023.02.0/Build 717912", + "Version 2023.03.0/Build 729220", + "Version 2023.04.0/Build 744681", "Version 2023.05.0/Build 755453", "Version 2023.06.0/Build 775017", "Version 2023.07.0/Build 788827", @@ -56,14 +94,14 @@ pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ "Version 2023.19.0/Build 927681", "Version 2023.20.0/Build 943980", "Version 2023.20.1/Build 946732", - "Version 2023.21.0/Build 956283", - "Version 2023.22.0/Build 968223", - "Version 2023.23.0/Build 983896", - "Version 2023.24.0/Build 998541", - "Version 2023.25.0/Build 1014750", - "Version 2023.25.1/Build 1018737", - "Version 2023.26.0/Build 1019073", - "Version 2023.27.0/Build 1031923", + "Version 2022.20.0/Build 487703", + "Version 2022.21.0/Build 492436", + "Version 2022.22.0/Build 498700", + "Version 2022.23.0/Build 502374", + "Version 2022.23.1/Build 506606", + "Version 2022.24.0/Build 510950", + "Version 2022.24.1/Build 513462", + "Version 2022.25.0/Build 515072", "Version 2022.25.1/Build 516394", "Version 2022.25.2/Build 519915", "Version 2022.26.0/Build 521193", @@ -86,14 +124,14 @@ pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ "Version 2022.40.0/Build 624782", "Version 2022.41.0/Build 630468", "Version 2022.41.1/Build 634168", - "Version 2022.42.0/Build 638508", - "Version 2022.43.0/Build 648277", - "Version 2022.44.0/Build 664348", - "Version 2022.45.0/Build 677985", - "Version 2023.01.0/Build 709875", - "Version 2023.02.0/Build 717912", - "Version 2023.03.0/Build 729220", - "Version 2023.04.0/Build 744681", + "Version 2021.39.1/Build 372418", + "Version 2021.41.0/Build 376052", + "Version 2021.42.0/Build 378193", + "Version 2021.43.0/Build 382019", + "Version 2021.44.0/Build 385129", + "Version 2021.45.0/Build 387663", + "Version 2021.46.0/Build 392043", + "Version 2021.47.0/Build 394342", "Version 2022.10.0/Build 429896", "Version 2022.1.0/Build 402829", "Version 2022.11.0/Build 433004", @@ -106,15 +144,7 @@ pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ "Version 2022.17.0/Build 468480", "Version 2022.18.0/Build 473740", "Version 2022.19.1/Build 482464", - "Version 2022.20.0/Build 487703", "Version 2022.2.0/Build 405543", - "Version 2022.21.0/Build 492436", - "Version 2022.22.0/Build 498700", - "Version 2022.23.0/Build 502374", - "Version 2022.23.1/Build 506606", - "Version 2022.24.0/Build 510950", - "Version 2022.24.1/Build 513462", - "Version 2022.25.0/Build 515072", "Version 2022.3.0/Build 408637", "Version 2022.4.0/Build 411368", "Version 2022.5.0/Build 414731", @@ -124,35 +154,5 @@ pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[ "Version 2022.7.0/Build 420849", "Version 2022.8.0/Build 423906", "Version 2022.9.0/Build 426592", - "Version 2021.20.0/Build 326964", - "Version 2021.21.0/Build 327703", - "Version 2021.21.1/Build 328461", - "Version 2021.22.0/Build 329696", - "Version 2021.23.0/Build 331631", - "Version 2021.24.0/Build 333951", - "Version 2021.25.0/Build 335451", - "Version 2021.26.0/Build 336739", - "Version 2021.27.0/Build 338857", - "Version 2021.28.0/Build 340747", - "Version 2021.29.0/Build 342342", - "Version 2021.30.0/Build 343820", - "Version 2021.31.0/Build 346485", - "Version 2021.32.0/Build 349507", - "Version 2021.33.0/Build 351843", - "Version 2021.34.0/Build 353911", - "Version 2021.35.0/Build 355878", - "Version 2021.36.0/Build 359254", - "Version 2021.36.1/Build 360572", - "Version 2021.37.0/Build 361905", - "Version 2021.38.0/Build 365032", - "Version 2021.39.0/Build 369068", - "Version 2021.39.1/Build 372418", - "Version 2021.41.0/Build 376052", - "Version 2021.42.0/Build 378193", - "Version 2021.43.0/Build 382019", - "Version 2021.44.0/Build 385129", - "Version 2021.45.0/Build 387663", - "Version 2021.46.0/Build 392043", - "Version 2021.47.0/Build 394342", ]; pub static _IOS_OS_VERSION_LIST: &[&str; 1] = &[""]; diff --git a/src/server.rs b/src/server.rs index c7bf45a..f69d200 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use brotli::enc::{BrotliCompress, BrotliEncoderParams}; use cached::proc_macro::cached; use cookie::Cookie; @@ -15,6 +17,7 @@ use libflate::gzip; use route_recognizer::{Params, Router}; use std::{ cmp::Ordering, + fmt::Display, io, pin::Pin, result::Result, @@ -65,12 +68,12 @@ impl CompressionType { } } -impl ToString for CompressionType { - fn to_string(&self) -> String { +impl Display for CompressionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Gzip => "gzip".to_string(), - Self::Brotli => "br".to_string(), - Self::Passthrough => String::new(), + Self::Gzip => write!(f, "gzip"), + Self::Brotli => write!(f, "br"), + Self::Passthrough => Ok(()), } } } diff --git a/src/settings.rs b/src/settings.rs index 6edb059..6c0480c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -19,18 +19,20 @@ struct SettingsTemplate { // CONSTANTS -const PREFS: [&str; 15] = [ +const PREFS: [&str; 17] = [ "theme", "front_page", "layout", "wide", "comment_sort", "post_sort", + "blur_spoiler", "show_nsfw", "blur_nsfw", "use_hls", "hide_hls_notification", "autoplay_videos", + "hide_sidebar_and_summary", "fixed_navbar", "hide_awards", "hide_score", diff --git a/src/subreddit.rs b/src/subreddit.rs index 4f897dc..b6dafca 100644 --- a/src/subreddit.rs +++ b/src/subreddit.rs @@ -8,6 +8,8 @@ use cookie::Cookie; use hyper::header::CONTENT_TYPE; use hyper::{Body, Request, Response}; +use once_cell::sync::Lazy; +use regex::Regex; use time::{Duration, OffsetDateTime}; // STRUCTS @@ -51,10 +53,13 @@ struct WallTemplate { url: String, } +static GEO_FILTER_MATCH: Lazy = Lazy::new(|| Regex::new(r"geo_filter=(?\w+)").unwrap()); + // SERVICES pub async fn community(req: Request) -> Result, String> { // Build Reddit API path let root = req.uri().path() == "/"; + let query = req.uri().query().unwrap_or_default().to_string(); let subscribed = setting(&req, "subscriptions"); let front_page = setting(&req, "front_page"); let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string()); @@ -108,10 +113,14 @@ pub async fn community(req: Request) -> Result, String> { let mut params = String::from("&raw_json=1"); if sub_name == "popular" { - params.push_str("&geo_filter=GLOBAL"); + let geo_filter = match GEO_FILTER_MATCH.captures(&query) { + Some(geo_filter) => geo_filter["region"].to_string(), + None => "GLOBAL".to_owned(), + }; + params.push_str(&format!("&geo_filter={geo_filter}")); } - let path = format!("/r/{sub_name}/{sort}.json?{}{params}", req.uri().query().unwrap_or_default()); + let path = format!("/r/{}/{sort}.json?{}{params}", sub_name.replace('+', "%2B"), req.uri().query().unwrap_or_default()); let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str())); let redirect_url = url[1..].replace('?', "%3F").replace('&', "%26").replace('+', "%2B"); let filters = get_filters(&req); @@ -137,6 +146,10 @@ pub async fn community(req: Request) -> Result, String> { let (_, all_posts_filtered) = filter_posts(&mut posts, &filters); let no_posts = posts.is_empty(); let all_posts_hidden_nsfw = !no_posts && (posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on"); + if sort == "new" { + posts.sort_by(|a, b| b.created_ts.cmp(&a.created_ts)); + posts.sort_by(|a, b| b.flags.stickied.cmp(&a.flags.stickied)); + } Ok(template(&SubredditTemplate { sub, posts, diff --git a/src/utils.rs b/src/utils.rs index 63e679d..b11096f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use crate::config::get_setting; // // CRATES @@ -156,6 +157,7 @@ impl PollOption { // Post flags with nsfw and stickied pub struct Flags { + pub spoiler: bool, pub nsfw: bool, pub stickied: bool, } @@ -167,6 +169,7 @@ pub struct Media { pub width: i64, pub height: i64, pub poster: String, + pub download_name: String, } impl Media { @@ -233,6 +236,15 @@ impl Media { let alt_url = alt_url_val.map_or(String::new(), |val| format_url(val.as_str().unwrap_or_default())); + let download_name = if post_type == "image" || post_type == "gif" || post_type == "video" { + let permalink_base = url_path_basename(data["permalink"].as_str().unwrap_or_default()); + let media_url_base = url_path_basename(url_val.as_str().unwrap_or_default()); + + format!("redlib_{permalink_base}_{media_url_base}") + } else { + String::new() + }; + ( post_type.to_string(), Self { @@ -243,6 +255,7 @@ impl Media { width: source["width"].as_i64().unwrap_or_default(), height: source["height"].as_i64().unwrap_or_default(), poster: format_url(source["url"].as_str().unwrap_or_default()), + download_name, }, gallery, ) @@ -296,6 +309,7 @@ pub struct Post { pub body: String, pub author: Author, pub permalink: String, + pub link_title: String, pub poll: Option, pub score: (String, String), pub upvote_ratio: i64, @@ -307,6 +321,7 @@ pub struct Post { pub domain: String, pub rel_time: String, pub created: String, + pub created_ts: u64, pub num_duplicates: u64, pub comments: (String, String), pub gallery: Vec, @@ -338,6 +353,7 @@ impl Post { let data = &post["data"]; let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default()); + let created_ts = data["created_utc"].as_f64().unwrap_or_default().round() as u64; let score = data["score"].as_i64().unwrap_or_default(); let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0; let title = val(post, "title"); @@ -384,6 +400,7 @@ impl Post { width: data["thumbnail_width"].as_i64().unwrap_or_default(), height: data["thumbnail_height"].as_i64().unwrap_or_default(), poster: String::new(), + download_name: String::new(), }, media, domain: val(post, "domain"), @@ -402,13 +419,16 @@ impl Post { }, }, flags: Flags { + spoiler: data["spoiler"].as_bool().unwrap_or_default(), nsfw: data["over_18"].as_bool().unwrap_or_default(), stickied: data["stickied"].as_bool().unwrap_or_default() || data["pinned"].as_bool().unwrap_or_default(), }, permalink: val(post, "permalink"), + link_title: val(post, "link_title"), poll: Poll::parse(&data["poll_data"]), rel_time, created, + created_ts, num_duplicates: post["data"]["num_duplicates"].as_u64().unwrap_or(0), comments: format_num(data["num_comments"].as_i64().unwrap_or_default()), gallery, @@ -417,7 +437,6 @@ impl Post { ws_url: val(post, "websocket_url"), }); } - Ok((posts, res["data"]["after"].as_str().unwrap_or_default().to_string())) } } @@ -574,9 +593,11 @@ pub struct Preferences { pub front_page: String, pub layout: String, pub wide: String, + pub blur_spoiler: String, pub show_nsfw: String, pub blur_nsfw: String, pub hide_hls_notification: String, + pub hide_sidebar_and_summary: String, pub use_hls: String, pub autoplay_videos: String, pub fixed_navbar: String, @@ -610,7 +631,9 @@ impl Preferences { front_page: setting(req, "front_page"), layout: setting(req, "layout"), wide: setting(req, "wide"), + blur_spoiler: setting(req, "blur_spoiler"), show_nsfw: setting(req, "show_nsfw"), + hide_sidebar_and_summary: setting(req, "hide_sidebar_and_summary"), blur_nsfw: setting(req, "blur_nsfw"), use_hls: setting(req, "use_hls"), hide_hls_notification: setting(req, "hide_hls_notification"), @@ -666,6 +689,8 @@ pub async fn parse_post(post: &Value) -> Post { // Determine the type of media along with the media URL let (post_type, media, gallery) = Media::parse(&post["data"]).await; + let created_ts = post["data"]["created_utc"].as_f64().unwrap_or_default().round() as u64; + let awards: Awards = Awards::parse(&post["data"]["all_awardings"]); let permalink = val(post, "permalink"); @@ -702,6 +727,7 @@ pub async fn parse_post(post: &Value) -> Post { distinguished: val(post, "distinguished"), }, permalink, + link_title: val(post, "link_title"), poll, score: format_num(score), upvote_ratio: ratio as i64, @@ -713,6 +739,7 @@ pub async fn parse_post(post: &Value) -> Post { width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(), height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(), poster: String::new(), + download_name: String::new(), }, flair: Flair { flair_parts: FlairPart::parse( @@ -729,12 +756,14 @@ pub async fn parse_post(post: &Value) -> Post { }, }, flags: Flags { + spoiler: post["data"]["spoiler"].as_bool().unwrap_or_default(), nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(), stickied: post["data"]["stickied"].as_bool().unwrap_or_default() || post["data"]["pinned"].as_bool().unwrap_or(false), }, domain: val(post, "domain"), rel_time, created, + created_ts, num_duplicates: post["data"]["num_duplicates"].as_u64().unwrap_or(0), comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()), gallery, @@ -875,9 +904,9 @@ pub fn format_url(url: &str) -> String { // These are links we want to replace in-body static REDDIT_REGEX: Lazy = Lazy::new(|| Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|new\.|)(reddit\.com|redd\.it)/"#).unwrap()); -static REDDIT_PREVIEW_REGEX: Lazy = Lazy::new(|| Regex::new(r"https?://(external-preview|preview)\.redd\.it(.*)[^?]").unwrap()); +static REDDIT_PREVIEW_REGEX: Lazy = Lazy::new(|| Regex::new(r"https?://(external-preview|preview|i)\.redd\.it(.*)[^?]").unwrap()); static REDDIT_EMOJI_REGEX: Lazy = Lazy::new(|| Regex::new(r"https?://(www|).redditstatic\.com/(.*)").unwrap()); -static REDLIB_PREVIEW_LINK_REGEX: Lazy = Lazy::new(|| Regex::new(r#"/preview/(pre|external-pre)/(.*?)>"#).unwrap()); +static REDLIB_PREVIEW_LINK_REGEX: Lazy = Lazy::new(|| Regex::new(r#"/(img|preview/)(pre|external-pre)?/(.*?)>"#).unwrap()); static REDLIB_PREVIEW_TEXT_REGEX: Lazy = Lazy::new(|| Regex::new(r">(.*?)").unwrap()); // Rewrite Reddit links to Redlib in body of text @@ -901,14 +930,47 @@ pub fn rewrite_urls(input_text: &str) -> String { let formatted_url = format_url(REDDIT_PREVIEW_REGEX.find(&text1).map(|x| x.as_str()).unwrap_or_default()); let image_url = REDLIB_PREVIEW_LINK_REGEX.find(&formatted_url).map_or("", |m| m.as_str()).to_string(); - let image_text = REDLIB_PREVIEW_TEXT_REGEX.find(&formatted_url).map_or("", |m| m.as_str()).to_string(); + let mut image_caption = REDLIB_PREVIEW_TEXT_REGEX.find(&formatted_url).map_or("", |m| m.as_str()).to_string(); - let image_to_replace = format!(">", ">"); - let image_replacement = format!(""); + /* As long as image_caption isn't empty remove first and last four characters of image_text to leave us with just the text in the caption without any HTML. + This makes it possible to enclose it in a
later on without having stray HTML breaking it */ + if !image_caption.is_empty() { + image_caption = image_caption[1..image_caption.len() - 4].to_string(); + } + + // image_url contains > at the end of it, and right above this we remove image_text's front >, leaving us with just a single > between them + let image_to_replace = format!(""); + + // _image_replacement needs to be in scope for the replacement at the bottom of the loop + let mut _image_replacement = String::new(); + + /* We don't want to show a caption that's just the image's link, so we check if we find a Reddit preview link within the image's caption. + If we don't find one we must have actual text, so we include a
block that contains it. + Otherwise we don't include the
block as we don't need it. */ + if REDDIT_PREVIEW_REGEX.find(&image_caption).is_none() { + // Without this " would show as \" instead. "\"" is how the quotes are formatted within image_text beforehand + image_caption = image_caption.replace("\\"", "\""); + + _image_replacement = format!("
{image_caption}
"); + } else { + _image_replacement = format!("
"); + } + + /* In order to know if we're dealing with a normal or external preview we need to take a look at the first capture group of REDDIT_PREVIEW_REGEX + if it's preview we're dealing with something that needs /preview/pre, external-preview is /preview/external-pre, and i is /img */ + let reddit_preview_regex_capture = REDDIT_PREVIEW_REGEX.captures(&text1).unwrap().get(1).map_or("", |m| m.as_str()).to_string(); + let mut _preview_type = String::new(); + if reddit_preview_regex_capture == "preview" { + _preview_type = "/preview/pre".to_string(); + } else if reddit_preview_regex_capture == "external-preview" { + _preview_type = "/preview/external-pre".to_string(); + } else { + _preview_type = "/img".to_string(); + } text1 = REDDIT_PREVIEW_REGEX - .replace(&text1, formatted_url) - .replace(&image_to_replace, &image_replacement) + .replace(&text1, format!("{_preview_type}$2")) + .replace(&image_to_replace, &_image_replacement) .to_string() } } @@ -993,7 +1055,7 @@ pub fn redirect(path: &str) -> Response { /// Renders a generic error landing page. pub async fn error(req: Request, msg: &str) -> Result, String> { - error!("Error page rendered: {msg}"); + error!("Error page rendered: {}", msg.split('|').next().unwrap_or_default()); let url = req.uri().to_string(); let body = ErrorTemplate { msg: msg.to_string(), @@ -1061,6 +1123,20 @@ pub async fn nsfw_landing(req: Request, req_url: String) -> Result String { + let url_result = Url::parse(format!("https://libredd.it/{path}").as_str()); + + if url_result.is_err() { + path.to_string() + } else { + let mut url = url_result.unwrap(); + url.path_segments_mut().unwrap().pop_if_empty(); + + url.path_segments().unwrap().last().unwrap().to_string() + } +} + #[cfg(test)] mod tests { use super::{format_num, format_url, rewrite_urls}; @@ -1164,11 +1240,24 @@ async fn test_fetching_ws() { #[test] fn test_rewriting_image_links() { - let input = r#"

https://preview.redd.it/zq21ggkj2xo31.png?width=2560&format=png&auto=webp&s=539d8050628ec1190cac26468fe99cc66b6071ab

-

https://preview.redd.it/vty9ocij2xo31.png?width=2560&format=png&auto=webp&s=fc7c7ef993a5e9ef656d5f5d9cf8290a0a1df877

-

https://preview.redd.it/bdfdxkjj2xo31.png?width=2560&format=png&auto=webp&s=d0fa420ece27605e882e89cb4711d75d774322ac

-

caption 1

-

caption 2

"#; - let output = r#"

"#; + let input = + r#"

caption 1

"#; + let output = r#"

caption 1
svg { stroke: var(--accent); } } #sort_options + #timeframe:not(#search_sort > #timeframe) { - margin-left: 10px; border-radius: 5px 0px 0px 5px; } @@ -654,24 +661,17 @@ button.submit:hover > svg { stroke: var(--accent); } } #search_sort { - background: var(--highlighted); border-radius: 5px; overflow: auto; } -#search_sort > #search { - border: 0; - background: transparent; +#search_sort > *, .search_widget_divider_box > *, .search_widget_divider_box #sort_options { + background: var(--highlighted); + font-size: 15px; } -#search_sort > *, #searchbox > * { font-size: 15px; } - -#search_sort > :not(:first-child), #search_sort > #sort_options { - margin: 0; - border-radius: 0; - border-right: 0; - border-left: 2px solid var(--background); - box-shadow: none; +#search_sort > #search { + border: 0; background: transparent; } @@ -690,6 +690,66 @@ button.submit:hover > svg { stroke: var(--accent); } margin-bottom: 20px; } +#search_sort > .search_widget_divider_box { + width: 100%; +} + +.search_widget_divider_box > * { + border-right: 2px var(--outside) solid; + margin: 0; +} + +.search_widget_divider_box { + display: flex; + align-items: center; + max-width: 100%; + border: 0; +} + +.search_widget_divider_box > #sort_options { + border-radius: 0; + box-shadow: none; +} + +/* When screen size is smaller than 480px we switch to a design better suited for mobile devices */ +@media screen and (max-width: 480px) { + #search_sort { + align-items: unset; + } + + .search_widget_divider_box > #search { + flex: 1; + min-width: unset; + border-right: 0; + border-bottom: 2px var(--outside) solid; + } + + #search:focus { + outline: 0; + } + + #search_sort > .search_widget_divider_box { + flex-wrap: wrap; + } + + .search_widget_divider_box > * { + width: 100%; + } + + #sort_options { + min-width: fit-content; + } + + .search_widget_divider_box > select:last-child { + border-right: 0; + } + + #sort_submit { + height: unset; + border-left: 2px var(--outside) solid; + } +} + #sort_options, #listing_options, main > * > footer > a { border-radius: 5px; align-items: center; @@ -923,6 +983,15 @@ a.search_subreddit:hover { font-weight: bold; } +.spoiler { + color: var(--spoiler); + margin-left: 5px; + border: 1px solid var(--spoiler); + padding: 3px; + font-size: 12px; + border-radius: 5px; +} + .post_media_content, .post .__NoScript_PlaceHolder__, .gallery { max-width: calc(100% - 40px); grid-area: post_media; @@ -950,11 +1019,25 @@ a.search_subreddit:hover { margin: auto; } -.post_nsfw_blur { +.post_blurred img, +.post_blurred svg, +.post_blurred video { filter: blur(1.5rem); } -.post_nsfw_blur:hover { +.post_blurred .post_body { + filter: blur(0.25rem); +} + +.post_blurred .post_thumbnail svg { + filter: blur(0.3rem); +} + +.post_blurred img:hover, +.post_blurred svg:hover, +.post_blurred video:hover, +.post_blurred .post_body:hover, +.post_blurred .post_thumbnail:hover svg { filter: none; } @@ -979,10 +1062,6 @@ a.search_subreddit:hover { vertical-align: bottom; } -.gallery figcaption { - margin-top: 5px; -} - .gallery .outbound_url { color: var(--accent); text-overflow: ellipsis; @@ -1010,6 +1089,9 @@ a.search_subreddit:hover { .post_body img { max-width: 100%; + display: block; + margin-left: auto; + margin-right: auto; } .post_poll { @@ -1094,12 +1176,12 @@ a.search_subreddit:hover { margin-right: 15px; } -#post_links > li.desktop_item { +.desktop_item { display: auto; } -@media screen and (min-width: 480px) { - #post_links > li.mobile_item { +@media screen and (min-width: 481px) { + .mobile_item { display: none; } } @@ -1131,10 +1213,6 @@ a.search_subreddit:hover { z-index: 0; } -.thumb_nsfw_blur { - filter: blur(0.3rem) -} - .post_thumbnail.no_thumbnail { background-color: var(--highlighted); } @@ -1187,6 +1265,10 @@ a.search_subreddit:hover { } } +.comment figure { + margin: 0; +} + .comment_left, .comment_right { display: flex; flex-direction: column; @@ -1213,10 +1295,6 @@ a.search_subreddit:hover { font-weight: bold; } -.comment_subreddit { - font-weight: bold; -} - .comment_score { color: var(--accent); background: var(--foreground); @@ -1235,10 +1313,28 @@ a.search_subreddit:hover { min-width: 0; } -.comment_data > * { +.comment:has([id]) .comment_data > * { margin-right: 5px; } +.comment:not([id]) .comment_data { + display: inline-flex; + max-width: 100%; +} + +.comment:not([id]) .comment_data > * { + flex: 0 0 auto; +} + +.comment:not([id]) .comment_data > .comment_link { + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + word-break: break-all; + overflow: hidden; + flex: 0 1 auto; +} + .comment_image { max-width: 500px; align-self: center; @@ -1308,6 +1404,15 @@ summary.comment_data { cursor: pointer; } +.user_comment_data_divider { + display: flex; + align-items: center; +} + +.user_comment_data_divider .dot { + display: none; +} + .moderator, .admin { opacity: 1; } .op, .moderator, .admin { font-weight: bold; } @@ -1484,10 +1589,19 @@ input[type="submit"] { width: 100%; } -.md > *:not(:first-child) { +.md > p:not(:first-child):not(:last-child) { margin-top: 20px; } +.md > figure:first-of-type { + margin-top: 5px; + margin-bottom: 0px; +} + +.md > figure:not(:first-of-type) { + margin-top: 10px; +} + .md h1 { font-size: 22px; } .md h2 { font-size: 20px; } .md h3 { font-size: 18px; } @@ -1711,8 +1825,23 @@ td, th { .comment_right { padding: 5px 0 10px 2px; } .comment_author { margin-left: 12px; } .comment_data { margin-left: 12px; } + + .user-comment .comment_data { + flex-direction: column; + flex-wrap: wrap; + row-gap: 5px; + } + .comment_data::marker { font-size: 25px; } - .created { width: 100%; } + .user-comment .comment_data > .comment_link { order: 2 } + .user_comment_data_divider { order: 1; } + + .user_comment_data_divider .dot { + display: unset; + margin-left: 5px; + } + + .created-in { display: none; } .comment_score { min-width: 32px; @@ -1723,10 +1852,11 @@ td, th { } #post_links > li { margin-right: 10px } - #post_links > li.desktop_item { display: none } - #post_links > li.mobile_item { display: auto } .post_footer > p > span#upvoted { display: none } + .desktop_item { display: none } + .mobile_item { display: auto } + .popup { width: auto; } diff --git a/static/themes/icebergDark.css b/static/themes/icebergDark.css new file mode 100644 index 0000000..8732ebb --- /dev/null +++ b/static/themes/icebergDark.css @@ -0,0 +1,14 @@ +/* icebergDark theme setting */ +.icebergDark { + --accent: #85a0c7; + --green: #b5bf82; + --text: #c6c8d1; + --foreground: #454d73; + --background: #161821; + --outside: #1f2233; + --post: #1f2233; + --panel-border: 1px solid #454d73; + --highlighted: #0f1117; + --visited: #0f1117; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.5); +} diff --git a/templates/error.html b/templates/error.html index a16fc74..e831200 100644 --- a/templates/error.html +++ b/templates/error.html @@ -2,10 +2,14 @@ {% block title %}Error: {{ msg }}{% endblock %} {% block sortstyle %}{% endblock %} {% block content %} -
-

{{ msg }}

-

Reddit Status

-
-

Head back home?

-
+
+

{{ msg }}

+

Reddit Status

+
+

Expected something to work? Report + an issue

+
+

Head back home?

+
{% endblock %} \ No newline at end of file diff --git a/templates/search.html b/templates/search.html index 3d91c76..ed72ac5 100644 --- a/templates/search.html +++ b/templates/search.html @@ -10,25 +10,34 @@ {% block content %}
- - {% if sub != "" %} -
- - +
+ +
+ {% if sub != "" %} +
+ + +
+ {% endif %} + {% if params.typed == "sr_user" %}{% endif %} + + {% if params.sort != "new" %} + + {% endif %} +
- {% endif %} - {% if params.typed == "sr_user" %}{% endif %} - {% if params.sort != "new" %}{% endif %} + + {% if !is_filtered %} diff --git a/templates/settings.html b/templates/settings.html index e57d250..434f0d2 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -54,6 +54,11 @@ {% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
+
+ + + +
{% if !crate::utils::sfw_only() %}
@@ -71,11 +76,16 @@
-
+
- - -
+ + +
+
+ + + +
diff --git a/templates/subreddit.html b/templates/subreddit.html index 0c27ed3..66afb87 100644 --- a/templates/subreddit.html +++ b/templates/subreddit.html @@ -82,7 +82,7 @@
{% endif %} - {% if is_filtered || (!sub.name.is_empty() && sub.name != "all" && sub.name != "popular" && !sub.name.contains("+")) %} + {% if is_filtered || (!sub.name.is_empty() && sub.name != "all" && sub.name != "popular" && !sub.name.contains("+")) && prefs.hide_sidebar_and_summary != "on" %}