Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
0ff92cbfe3 | |||
e9891236cd | |||
e2c48c3438 | |||
9a7b3b29f5 | |||
10add895fb | |||
050eaedf15 | |||
5b06a3fc64 | |||
4817f51bc0 | |||
c83a4e0cc8 | |||
c15f305be0 | |||
222d216854 | |||
6a785baa2c | |||
6d8aaba8bb | |||
6cf3748642 | |||
9c938c6210 | |||
a49d399f72 | |||
9178b50b73 | |||
b5d04f1a50 | |||
9e434e7db6 | |||
ab30b8bbec | |||
37d1939dc0 | |||
08a20b89a6 | |||
5d518cfc18 | |||
7e752b3d81 | |||
87729d0daa | |||
dc06ae3b29 | |||
225380b7d9 |
2
.github/workflows/docker-arm.yml
vendored
2
.github/workflows/docker-arm.yml
vendored
@ -33,6 +33,6 @@ jobs:
|
|||||||
file: ./Dockerfile.arm
|
file: ./Dockerfile.arm
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: spikecodes/libreddit:arm
|
tags: libreddit/libreddit:arm
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
2
.github/workflows/docker-armv7.yml
vendored
2
.github/workflows/docker-armv7.yml
vendored
@ -36,6 +36,6 @@ jobs:
|
|||||||
file: ./Dockerfile.armv7
|
file: ./Dockerfile.armv7
|
||||||
platforms: linux/arm/v7
|
platforms: linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
tags: spikecodes/libreddit:armv7
|
tags: libreddit/libreddit:armv7
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
8
.github/workflows/docker.yml
vendored
8
.github/workflows/docker.yml
vendored
@ -26,6 +26,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
- name: Docker Hub Description
|
||||||
|
uses: peter-evans/dockerhub-description@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
repository: libreddit/libreddit
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
@ -33,6 +39,6 @@ jobs:
|
|||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: spikecodes/libreddit:latest
|
tags: libreddit/libreddit:latest
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
4
CREDITS
4
CREDITS
@ -21,11 +21,13 @@ dbrennand <52419383+dbrennand@users.noreply.github.com>
|
|||||||
Diego Magdaleno <38844659+DiegoMagdaleno@users.noreply.github.com>
|
Diego Magdaleno <38844659+DiegoMagdaleno@users.noreply.github.com>
|
||||||
Dyras <jevwmguf@duck.com>
|
Dyras <jevwmguf@duck.com>
|
||||||
Edward <101938856+EdwardLangdon@users.noreply.github.com>
|
Edward <101938856+EdwardLangdon@users.noreply.github.com>
|
||||||
|
elliot <75391956+ellieeet123@users.noreply.github.com>
|
||||||
erdnaxe <erdnaxe@users.noreply.github.com>
|
erdnaxe <erdnaxe@users.noreply.github.com>
|
||||||
Esmail EL BoB <github.defilable@simplelogin.co>
|
Esmail EL BoB <github.defilable@simplelogin.co>
|
||||||
FireMasterK <20838718+FireMasterK@users.noreply.github.com>
|
FireMasterK <20838718+FireMasterK@users.noreply.github.com>
|
||||||
George Roubos <cowkingdom@hotmail.com>
|
George Roubos <cowkingdom@hotmail.com>
|
||||||
git-bruh <e817509a-8ee9-4332-b0ad-3a6bdf9ab63f@aleeas.com>
|
git-bruh <e817509a-8ee9-4332-b0ad-3a6bdf9ab63f@aleeas.com>
|
||||||
|
gmnsii <95436780+gmnsii@users.noreply.github.com>
|
||||||
guaddy <67671414+guaddy@users.noreply.github.com>
|
guaddy <67671414+guaddy@users.noreply.github.com>
|
||||||
Harsh Mishra <erbeusgriffincasper@gmail.com>
|
Harsh Mishra <erbeusgriffincasper@gmail.com>
|
||||||
igna <igna@intent.cool>
|
igna <igna@intent.cool>
|
||||||
@ -62,11 +64,13 @@ robrobinbin <>
|
|||||||
robrobinbin <8597693+robrobinbin@users.noreply.github.com>
|
robrobinbin <8597693+robrobinbin@users.noreply.github.com>
|
||||||
robrobinbin <robindepril@gmail.com>
|
robrobinbin <robindepril@gmail.com>
|
||||||
Ruben Elshof <15641671+rubenelshof@users.noreply.github.com>
|
Ruben Elshof <15641671+rubenelshof@users.noreply.github.com>
|
||||||
|
Rupert Angermeier <rangermeier@users.noreply.github.com>
|
||||||
Scoder12 <34356756+Scoder12@users.noreply.github.com>
|
Scoder12 <34356756+Scoder12@users.noreply.github.com>
|
||||||
Slayer <51095261+GhostSlayer@users.noreply.github.com>
|
Slayer <51095261+GhostSlayer@users.noreply.github.com>
|
||||||
Soheb <somoso@users.noreply.github.com>
|
Soheb <somoso@users.noreply.github.com>
|
||||||
somini <somini@users.noreply.github.com>
|
somini <somini@users.noreply.github.com>
|
||||||
somoso <github@soheb.anonaddy.com>
|
somoso <github@soheb.anonaddy.com>
|
||||||
|
Spenser Black <spenserblack01@gmail.com>
|
||||||
Spike <19519553+spikecodes@users.noreply.github.com>
|
Spike <19519553+spikecodes@users.noreply.github.com>
|
||||||
spikecodes <19519553+spikecodes@users.noreply.github.com>
|
spikecodes <19519553+spikecodes@users.noreply.github.com>
|
||||||
sybenx <syb@duck.com>
|
sybenx <syb@duck.com>
|
||||||
|
178
Cargo.lock
generated
178
Cargo.lock
generated
@ -10,9 +10,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.19"
|
version = "0.7.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -75,22 +75,11 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-recursion"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.58"
|
version = "0.1.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
|
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -168,9 +157,9 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.2.1"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cached"
|
name = "cached"
|
||||||
@ -211,9 +200,9 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.76"
|
version = "1.0.77"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -223,9 +212,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.0.24"
|
version = "4.0.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60494cedb60cb47462c0ff7be53de32c0e42a6fc2c772184554fa12bd9489c03"
|
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
@ -331,9 +320,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.5"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
@ -363,6 +352,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.25"
|
version = "0.3.25"
|
||||||
@ -567,9 +562,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.23.0"
|
version = "0.23.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
|
checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
@ -598,9 +593,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.1"
|
version = "1.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -638,9 +633,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.137"
|
version = "0.2.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libflate"
|
name = "libflate"
|
||||||
@ -664,10 +659,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libreddit"
|
name = "libreddit"
|
||||||
version = "0.25.0"
|
version = "0.27.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"async-recursion",
|
|
||||||
"brotli",
|
"brotli",
|
||||||
"cached",
|
"cached",
|
||||||
"clap",
|
"clap",
|
||||||
@ -677,14 +671,17 @@ dependencies = [
|
|||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"libflate",
|
"libflate",
|
||||||
"lipsum",
|
"lipsum",
|
||||||
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"regex",
|
"regex",
|
||||||
"route-recognizer",
|
"route-recognizer",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
|
"sealed_test",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -791,9 +788,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.4.0"
|
version = "6.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
@ -813,9 +810,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.4"
|
version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
|
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@ -857,6 +854,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@ -922,6 +925,15 @@ version = "0.6.28"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.16.20"
|
version = "0.16.20"
|
||||||
@ -1017,6 +1029,18 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusty-forkfork"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ce85af4dfa2fb0c0143121ab5e424c71ea693867357c9159b8777b59984c218"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"quick-error",
|
||||||
|
"tempfile",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@ -1058,6 +1082,28 @@ dependencies = [
|
|||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sealed_test"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a608d94641cc17fe203b102db2ae86d47a236630192f0244ddbbbb0044c0272"
|
||||||
|
dependencies = [
|
||||||
|
"fs_extra",
|
||||||
|
"rusty-forkfork",
|
||||||
|
"sealed_test_derive",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sealed_test_derive"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b672e005ae58fef5da619d90b9f1c5b44b061890f4a371b3c96257a8a15e697"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
@ -1083,18 +1129,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.147"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
|
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.147"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
|
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1103,9 +1149,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.87"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -1171,15 +1217,29 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.103"
|
version = "1.0.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
|
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"remove_dir_all",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
@ -1244,9 +1304,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.21.2"
|
version = "1.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
|
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1259,14 +1319,14 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"winapi",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "1.8.0"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
|
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1298,6 +1358,15 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -1332,9 +1401,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
@ -1389,6 +1458,15 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "waker-fn"
|
name = "waker-fn"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
11
Cargo.toml
11
Cargo.toml
@ -3,13 +3,12 @@ name = "libreddit"
|
|||||||
description = " Alternative private front-end to Reddit"
|
description = " Alternative private front-end to Reddit"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
repository = "https://github.com/spikecodes/libreddit"
|
repository = "https://github.com/spikecodes/libreddit"
|
||||||
version = "0.25.0"
|
version = "0.27.0"
|
||||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
askama = { version = "0.11.1", default-features = false }
|
askama = { version = "0.11.1", default-features = false }
|
||||||
async-recursion = "1.0.0"
|
|
||||||
cached = "0.40.0"
|
cached = "0.40.0"
|
||||||
clap = { version = "4.0.24", default-features = false, features = ["std"] }
|
clap = { version = "4.0.24", default-features = false, features = ["std"] }
|
||||||
regex = "1.7.0"
|
regex = "1.7.0"
|
||||||
@ -27,6 +26,14 @@ url = "2.3.1"
|
|||||||
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
|
rust-embed = { version = "6.4.2", features = ["include-exclude"] }
|
||||||
libflate = "1.2.0"
|
libflate = "1.2.0"
|
||||||
brotli = { version = "3.3.4", features = ["std"] }
|
brotli = { version = "3.3.4", features = ["std"] }
|
||||||
|
toml = "0.5.9"
|
||||||
|
once_cell = "1.16.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lipsum = "0.8.2"
|
lipsum = "0.8.2"
|
||||||
|
sealed_test = "1.0.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
strip = true
|
||||||
|
35
README.md
35
README.md
@ -39,7 +39,7 @@ Both files are part of the [libreddit-instances](https://github.com/libreddit/li
|
|||||||
|
|
||||||
# About
|
# About
|
||||||
|
|
||||||
Find Libreddit on 💬 [Matrix](https://matrix.to/#/#libreddit:kde.org), 🐋 [Docker](https://hub.docker.com/r/spikecodes/libreddit), :octocat: [GitHub](https://github.com/libreddit/libreddit), and 🦊 [GitLab](https://gitlab.com/spikecodes/libreddit).
|
Find Libreddit on 💬 [Matrix](https://matrix.to/#/#libreddit:kde.org), 🐋 [Docker](https://hub.docker.com/r/libreddit/libreddit), :octocat: [GitHub](https://github.com/libreddit/libreddit), and 🦊 [GitLab](https://gitlab.com/libreddit/libreddit).
|
||||||
|
|
||||||
## Built with
|
## Built with
|
||||||
|
|
||||||
@ -136,21 +136,21 @@ cargo install libreddit
|
|||||||
|
|
||||||
## 2) Docker
|
## 2) Docker
|
||||||
|
|
||||||
Deploy the [Docker image](https://hub.docker.com/r/spikecodes/libreddit) of Libreddit:
|
Deploy the [Docker image](https://hub.docker.com/r/libreddit/libreddit) of Libreddit:
|
||||||
```
|
```
|
||||||
docker pull spikecodes/libreddit
|
docker pull libreddit/libreddit
|
||||||
docker run -d --name libreddit -p 8080:8080 spikecodes/libreddit
|
docker run -d --name libreddit -p 8080:8080 libreddit/libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
Deploy using a different port (in this case, port 80):
|
Deploy using a different port (in this case, port 80):
|
||||||
```
|
```
|
||||||
docker pull spikecodes/libreddit
|
docker pull libreddit/libreddit
|
||||||
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
|
docker run -d --name libreddit -p 80:8080 libreddit/libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
To deploy on `arm64` platforms, simply replace `spikecodes/libreddit` in the commands above with `spikecodes/libreddit:arm`.
|
To deploy on `arm64` platforms, simply replace `libreddit/libreddit` in the commands above with `libreddit/libreddit:arm`.
|
||||||
|
|
||||||
To deploy on `armv7` platforms, simply replace `spikecodes/libreddit` in the commands above with `spikecodes/libreddit:armv7`.
|
To deploy on `armv7` platforms, simply replace `libreddit/libreddit` in the commands above with `libreddit/libreddit:armv7`.
|
||||||
|
|
||||||
## 3) AUR
|
## 3) AUR
|
||||||
|
|
||||||
@ -182,9 +182,17 @@ Once installed, deploy Libreddit to `0.0.0.0:8080` by running:
|
|||||||
libreddit
|
libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
## Change Default Settings
|
## Instance settings
|
||||||
|
|
||||||
Assign a default value for each setting by passing environment variables to Libreddit in the format `LIBREDDIT_DEFAULT_{X}`. Replace `{X}` with the setting name (see list below) in capital letters.
|
Assign a default value for each instance-specific setting by passing environment variables to Libreddit in the format `LIBREDDIT_{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. |
|
||||||
|
|
||||||
|
## Default User Settings
|
||||||
|
|
||||||
|
Assign a default value for each user-modifiable setting by passing environment variables to Libreddit in the format `LIBREDDIT_DEFAULT_{Y}`. Replace `{Y}` with the setting name (see list below) in capital letters.
|
||||||
|
|
||||||
| Name | Possible values | Default value |
|
| Name | Possible values | Default value |
|
||||||
|-------------------------|-----------------------------------------------------------------------------------------------------|---------------|
|
|-------------------------|-----------------------------------------------------------------------------------------------------|---------------|
|
||||||
@ -200,6 +208,13 @@ Assign a default value for each setting by passing environment variables to Libr
|
|||||||
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |
|
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |
|
||||||
| `AUTOPLAY_VIDEOS` | `["on", "off"]` | `off` |
|
| `AUTOPLAY_VIDEOS` | `["on", "off"]` | `off` |
|
||||||
|
|
||||||
|
You can also configure Libreddit with a configuration file. An example `libreddit.toml` can be found below:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
LIBREDDIT_DEFAULT_WIDE = "on"
|
||||||
|
LIBREDDIT_DEFAULT_USE_HLS = "on"
|
||||||
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
3
app.json
3
app.json
@ -40,6 +40,9 @@
|
|||||||
},
|
},
|
||||||
"LIBREDDIT_HIDE_HLS_NOTIFICATION": {
|
"LIBREDDIT_HIDE_HLS_NOTIFICATION": {
|
||||||
"required": false
|
"required": false
|
||||||
|
},
|
||||||
|
"LIBREDDIT_SFW_ONLY": {
|
||||||
|
"required": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
130
src/config.rs
Normal file
130
src/config.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::{env::var, fs::read_to_string};
|
||||||
|
|
||||||
|
// Waiting for https://github.com/rust-lang/rust/issues/74465 to land, so we
|
||||||
|
// can reduce reliance on once_cell.
|
||||||
|
//
|
||||||
|
// This is the local static that is initialized at runtime (technically at
|
||||||
|
// first request) and contains the instance settings.
|
||||||
|
static CONFIG: Lazy<Config> = Lazy::new(Config::load);
|
||||||
|
|
||||||
|
/// Stores the configuration parsed from the environment variables and the
|
||||||
|
/// config file. `Config::Default()` contains None for each setting.
|
||||||
|
#[derive(Default, serde::Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(rename = "LIBREDDIT_SFW_ONLY")]
|
||||||
|
sfw_only: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_THEME")]
|
||||||
|
default_theme: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_FRONT_PAGE")]
|
||||||
|
default_front_page: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_LAYOUT")]
|
||||||
|
default_layout: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_WIDE")]
|
||||||
|
default_wide: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_COMMENT_SORT")]
|
||||||
|
default_comment_sort: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_POST_SORT")]
|
||||||
|
default_post_sort: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_SHOW_NSFW")]
|
||||||
|
default_show_nsfw: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_BLUR_NSFW")]
|
||||||
|
default_blur_nsfw: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_USE_HLS")]
|
||||||
|
default_use_hls: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION")]
|
||||||
|
default_hide_hls_notification: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Load the configuration from the environment variables and the config file.
|
||||||
|
/// In the case that there are no environment variables set and there is no
|
||||||
|
/// config file, this function returns a Config that contains all None values.
|
||||||
|
pub fn load() -> Self {
|
||||||
|
// Read from libreddit.toml config file. If for any reason, it fails, the
|
||||||
|
// default `Config` is used (all None values)
|
||||||
|
let config: Config = toml::from_str(&read_to_string("libreddit.toml").unwrap_or_default()).unwrap_or_default();
|
||||||
|
// This function defines the order of preference - first check for
|
||||||
|
// environment variables with "LIBREDDIT", then check the config, then if
|
||||||
|
// both are `None`, return a `None` via the `map_or_else` function
|
||||||
|
let parse = |key: &str| -> Option<String> { var(key).ok().map_or_else(|| get_setting_from_config(key, &config), Some) };
|
||||||
|
Self {
|
||||||
|
sfw_only: parse("LIBREDDIT_SFW_ONLY"),
|
||||||
|
default_theme: parse("LIBREDDIT_DEFAULT_THEME"),
|
||||||
|
default_front_page: parse("LIBREDDIT_DEFAULT_FRONT_PAGE"),
|
||||||
|
default_layout: parse("LIBREDDIT_DEFAULT_LAYOUT"),
|
||||||
|
default_post_sort: parse("LIBREDDIT_DEFAULT_POST_SORT"),
|
||||||
|
default_wide: parse("LIBREDDIT_DEFAULT_WIDE"),
|
||||||
|
default_comment_sort: parse("LIBREDDIT_DEFAULT_COMMENT_SORT"),
|
||||||
|
default_show_nsfw: parse("LIBREDDIT_DEFAULT_SHOW_NSFW"),
|
||||||
|
default_blur_nsfw: parse("LIBREDDIT_DEFAULT_BLUR_NSFW"),
|
||||||
|
default_use_hls: parse("LIBREDDIT_DEFAULT_USE_HLS"),
|
||||||
|
default_hide_hls_notification: parse("LIBREDDIT_DEFAULT_HIDE_HLS"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_setting_from_config(name: &str, config: &Config) -> Option<String> {
|
||||||
|
match name {
|
||||||
|
"LIBREDDIT_SFW_ONLY" => config.sfw_only.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_THEME" => config.default_theme.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_FRONT_PAGE" => config.default_front_page.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_LAYOUT" => config.default_layout.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_POST_SORT" => config.default_post_sort.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
|
||||||
|
"LIBREDDIT_DEFAULT_WIDE" => config.default_wide.clone(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves setting from environment variable or config file.
|
||||||
|
pub(crate) fn get_setting(name: &str) -> Option<String> {
|
||||||
|
get_setting_from_config(name, &CONFIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use {sealed_test::prelude::*, std::fs::write};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[sealed_test(env = [("LIBREDDIT_SFW_ONLY", "1")])]
|
||||||
|
fn test_env_var() {
|
||||||
|
assert!(crate::utils::sfw_only())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[sealed_test]
|
||||||
|
fn test_config() {
|
||||||
|
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
|
||||||
|
write("libreddit.toml", config_to_write).unwrap();
|
||||||
|
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("best".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[sealed_test(env = [("LIBREDDIT_DEFAULT_COMMENT_SORT", "top")])]
|
||||||
|
fn test_env_config_precedence() {
|
||||||
|
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
|
||||||
|
write("libreddit.toml", config_to_write).unwrap();
|
||||||
|
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("top".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[sealed_test(env = [("LIBREDDIT_DEFAULT_COMMENT_SORT", "top")])]
|
||||||
|
fn test_alt_env_config_precedence() {
|
||||||
|
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
|
||||||
|
write("libreddit.toml", config_to_write).unwrap();
|
||||||
|
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("top".into()))
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
use crate::client::json;
|
use crate::client::json;
|
||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
use crate::subreddit::{can_access_quarantine, quarantine};
|
use crate::subreddit::{can_access_quarantine, quarantine};
|
||||||
use crate::utils::{error, filter_posts, get_filters, parse_post, template, Post, Preferences};
|
use crate::utils::{error, filter_posts, get_filters, nsfw_landing, parse_post, setting, template, Post, Preferences};
|
||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
@ -65,8 +65,16 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
match json(path, quarantined).await {
|
match json(path, quarantined).await {
|
||||||
// Process response JSON.
|
// Process response JSON.
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let filters = get_filters(&req);
|
|
||||||
let post = parse_post(&response[0]["data"]["children"][0]).await;
|
let post = parse_post(&response[0]["data"]["children"][0]).await;
|
||||||
|
|
||||||
|
// Return landing page if this post if this Reddit deems this post
|
||||||
|
// NSFW, but we have also disabled the display of NSFW content
|
||||||
|
// or if the instance is SFW-only.
|
||||||
|
if post.nsfw && (setting(&req, "show_nsfw") != "on" || crate::utils::sfw_only()) {
|
||||||
|
return Ok(nsfw_landing(req).await.unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let filters = get_filters(&req);
|
||||||
let (duplicates, num_posts_filtered, all_posts_filtered) = parse_duplicates(&response[1], &filters).await;
|
let (duplicates, num_posts_filtered, all_posts_filtered) = parse_duplicates(&response[1], &filters).await;
|
||||||
|
|
||||||
// These are the values for the "before=", "after=", and "sort="
|
// These are the values for the "before=", "after=", and "sort="
|
||||||
@ -193,7 +201,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
params: DuplicatesParams { before, after, sort },
|
params: DuplicatesParams { before, after, sort },
|
||||||
post,
|
post,
|
||||||
duplicates,
|
duplicates,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
num_posts_filtered,
|
num_posts_filtered,
|
||||||
all_posts_filtered,
|
all_posts_filtered,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![allow(clippy::cmp_owned)]
|
#![allow(clippy::cmp_owned)]
|
||||||
|
|
||||||
// Reference local files
|
// Reference local files
|
||||||
|
mod config;
|
||||||
mod duplicates;
|
mod duplicates;
|
||||||
mod post;
|
mod post;
|
||||||
mod search;
|
mod search;
|
||||||
@ -289,7 +290,7 @@ async fn main() {
|
|||||||
Some("best" | "hot" | "new" | "top" | "rising" | "controversial") => subreddit::community(req).await,
|
Some("best" | "hot" | "new" | "top" | "rising" | "controversial") => subreddit::community(req).await,
|
||||||
|
|
||||||
// Short link for post
|
// Short link for post
|
||||||
Some(id) if (5..7).contains(&id.len()) => match canonical_path(format!("/{}", id)).await {
|
Some(id) if (5..8).contains(&id.len()) => match canonical_path(format!("/{}", id)).await {
|
||||||
Ok(path_opt) => match path_opt {
|
Ok(path_opt) => match path_opt {
|
||||||
Some(path) => Ok(redirect(path)),
|
Some(path) => Ok(redirect(path)),
|
||||||
None => error(req, "Post ID is invalid. It may point to a post on a community that has been banned.").await,
|
None => error(req, "Post ID is invalid. It may point to a post on a community that has been banned.").await,
|
||||||
|
19
src/post.rs
19
src/post.rs
@ -3,7 +3,7 @@ use crate::client::json;
|
|||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
use crate::subreddit::{can_access_quarantine, quarantine};
|
use crate::subreddit::{can_access_quarantine, quarantine};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
error, format_num, get_filters, param, parse_post, rewrite_urls, setting, template, time, val, Author, Awards, Comment, Flair, FlairPart, Post, Preferences,
|
error, format_num, get_filters, nsfw_landing, param, parse_post, rewrite_urls, setting, template, time, val, Author, Awards, Comment, Flair, FlairPart, Post, Preferences,
|
||||||
};
|
};
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
|
|
||||||
@ -55,7 +55,15 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
// Parse the JSON into Post and Comment structs
|
// Parse the JSON into Post and Comment structs
|
||||||
let post = parse_post(&response[0]["data"]["children"][0]).await;
|
let post = parse_post(&response[0]["data"]["children"][0]).await;
|
||||||
let comments = parse_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &get_filters(&req));
|
|
||||||
|
// Return landing page if this post if this Reddit deems this post
|
||||||
|
// NSFW, but we have also disabled the display of NSFW content
|
||||||
|
// or if the instance is SFW-only.
|
||||||
|
if post.nsfw && (setting(&req, "show_nsfw") != "on" || crate::utils::sfw_only()) {
|
||||||
|
return Ok(nsfw_landing(req).await.unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let comments = parse_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &get_filters(&req), &req);
|
||||||
let url = req.uri().to_string();
|
let url = req.uri().to_string();
|
||||||
|
|
||||||
// Use the Post and Comment structs to generate a website to show users
|
// Use the Post and Comment structs to generate a website to show users
|
||||||
@ -63,7 +71,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
comments,
|
comments,
|
||||||
post,
|
post,
|
||||||
sort,
|
sort,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
single_thread,
|
single_thread,
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
@ -81,7 +89,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// COMMENTS
|
// COMMENTS
|
||||||
fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, highlighted_comment: &str, filters: &HashSet<String>) -> Vec<Comment> {
|
fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, highlighted_comment: &str, filters: &HashSet<String>, req: &Request<Body>) -> Vec<Comment> {
|
||||||
// Parse the comment JSON into a Vector of Comments
|
// Parse the comment JSON into a Vector of Comments
|
||||||
let comments = json["data"]["children"].as_array().map_or(Vec::new(), std::borrow::ToOwned::to_owned);
|
let comments = json["data"]["children"].as_array().map_or(Vec::new(), std::borrow::ToOwned::to_owned);
|
||||||
|
|
||||||
@ -101,7 +109,7 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str,
|
|||||||
|
|
||||||
// If this comment contains replies, handle those too
|
// If this comment contains replies, handle those too
|
||||||
let replies: Vec<Comment> = if data["replies"].is_object() {
|
let replies: Vec<Comment> = if data["replies"].is_object() {
|
||||||
parse_comments(&data["replies"], post_link, post_author, highlighted_comment, filters)
|
parse_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, req)
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
@ -169,6 +177,7 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str,
|
|||||||
awards,
|
awards,
|
||||||
collapsed,
|
collapsed,
|
||||||
is_filtered,
|
is_filtered,
|
||||||
|
prefs: Preferences::new(req),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::{catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, setting, template, val, Post, Preferences};
|
use crate::utils::{self, catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, setting, template, val, Post, Preferences};
|
||||||
use crate::{
|
use crate::{
|
||||||
client::json,
|
client::json,
|
||||||
subreddit::{can_access_quarantine, quarantine},
|
subreddit::{can_access_quarantine, quarantine},
|
||||||
@ -7,6 +7,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
struct SearchParams {
|
struct SearchParams {
|
||||||
@ -44,13 +46,23 @@ struct SearchTemplate {
|
|||||||
all_posts_filtered: bool,
|
all_posts_filtered: bool,
|
||||||
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
||||||
all_posts_hidden_nsfw: bool,
|
all_posts_hidden_nsfw: bool,
|
||||||
|
no_posts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regex matched against search queries to determine if they are reddit urls.
|
||||||
|
static REDDIT_URL_MATCH: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://([^\./]+\.)*reddit.com/").unwrap());
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
let nsfw_results = if setting(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
// This ensures that during a search, no NSFW posts are fetched at all
|
||||||
|
let nsfw_results = if setting(&req, "show_nsfw") == "on" && !utils::sfw_only() {
|
||||||
|
"&include_over_18=on"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
let path = format!("{}.json?{}{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
let path = format!("{}.json?{}{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
||||||
let query = param(&path, "q").unwrap_or_default();
|
let mut query = param(&path, "q").unwrap_or_default();
|
||||||
|
query = REDDIT_URL_MATCH.replace(&query, "").to_string();
|
||||||
|
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
return Ok(redirect("/".to_string()));
|
return Ok(redirect("/".to_string()));
|
||||||
@ -98,17 +110,19 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
||||||
typed,
|
typed,
|
||||||
},
|
},
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
is_filtered: true,
|
is_filtered: true,
|
||||||
all_posts_filtered: false,
|
all_posts_filtered: false,
|
||||||
all_posts_hidden_nsfw: false,
|
all_posts_hidden_nsfw: false,
|
||||||
|
no_posts: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
match Post::fetch(&path, quarantined).await {
|
match Post::fetch(&path, quarantined).await {
|
||||||
Ok((mut posts, after)) => {
|
Ok((mut posts, after)) => {
|
||||||
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
||||||
let all_posts_hidden_nsfw = posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on";
|
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");
|
||||||
template(SearchTemplate {
|
template(SearchTemplate {
|
||||||
posts,
|
posts,
|
||||||
subreddits,
|
subreddits,
|
||||||
@ -122,11 +136,12 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
||||||
typed,
|
typed,
|
||||||
},
|
},
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
is_filtered: false,
|
is_filtered: false,
|
||||||
all_posts_filtered,
|
all_posts_filtered,
|
||||||
all_posts_hidden_nsfw,
|
all_posts_hidden_nsfw,
|
||||||
|
no_posts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
|
@ -243,7 +243,7 @@ impl Server {
|
|||||||
match func.await {
|
match func.await {
|
||||||
Ok(mut res) => {
|
Ok(mut res) => {
|
||||||
res.headers_mut().extend(def_headers);
|
res.headers_mut().extend(def_headers);
|
||||||
let _ = compress_response(req_headers, &mut res).await;
|
let _ = compress_response(&req_headers, &mut res).await;
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ async fn new_boilerplate(
|
|||||||
) -> Result<Response<Body>, String> {
|
) -> Result<Response<Body>, String> {
|
||||||
match Response::builder().status(status).body(body) {
|
match Response::builder().status(status).body(body) {
|
||||||
Ok(mut res) => {
|
Ok(mut res) => {
|
||||||
let _ = compress_response(req_headers, &mut res).await;
|
let _ = compress_response(&req_headers, &mut res).await;
|
||||||
|
|
||||||
res.headers_mut().extend(default_headers.clone());
|
res.headers_mut().extend(default_headers.clone());
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@ -306,7 +306,8 @@ async fn new_boilerplate(
|
|||||||
/// Accept-Encoding: gzip, compress, br
|
/// Accept-Encoding: gzip, compress, br
|
||||||
/// Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1
|
/// Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1
|
||||||
/// ```
|
/// ```
|
||||||
fn determine_compressor(accept_encoding: &str) -> Option<CompressionType> {
|
#[cached]
|
||||||
|
fn determine_compressor(accept_encoding: String) -> Option<CompressionType> {
|
||||||
if accept_encoding.is_empty() {
|
if accept_encoding.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
@ -473,7 +474,7 @@ fn determine_compressor(accept_encoding: &str) -> Option<CompressionType> {
|
|||||||
///
|
///
|
||||||
/// This function logs errors to stderr, but only in debug mode. No information
|
/// This function logs errors to stderr, but only in debug mode. No information
|
||||||
/// is logged in release builds.
|
/// is logged in release builds.
|
||||||
async fn compress_response(req_headers: HeaderMap<header::HeaderValue>, res: &mut Response<Body>) -> Result<(), String> {
|
async fn compress_response(req_headers: &HeaderMap<header::HeaderValue>, res: &mut Response<Body>) -> Result<(), String> {
|
||||||
// Check if the data is eligible for compression.
|
// Check if the data is eligible for compression.
|
||||||
if let Some(hdr) = res.headers().get(header::CONTENT_TYPE) {
|
if let Some(hdr) = res.headers().get(header::CONTENT_TYPE) {
|
||||||
match from_utf8(hdr.as_bytes()) {
|
match from_utf8(hdr.as_bytes()) {
|
||||||
@ -503,30 +504,22 @@ async fn compress_response(req_headers: HeaderMap<header::HeaderValue>, res: &mu
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Quick and dirty closure for extracting a header from the request and
|
|
||||||
// returning it as a &str.
|
|
||||||
let get_req_header = |k: header::HeaderName| -> Option<&str> {
|
|
||||||
match req_headers.get(k) {
|
|
||||||
Some(hdr) => match from_utf8(hdr.as_bytes()) {
|
|
||||||
Ok(val) => Some(val),
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
Err(e) => {
|
|
||||||
dbg_msg!(e);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check to see which compressor is requested, and if we can use it.
|
// Check to see which compressor is requested, and if we can use it.
|
||||||
let accept_encoding: &str = match get_req_header(header::ACCEPT_ENCODING) {
|
let accept_encoding: String = match req_headers.get(header::ACCEPT_ENCODING) {
|
||||||
Some(val) => val,
|
|
||||||
None => return Ok(()), // Client requested no compression.
|
None => return Ok(()), // Client requested no compression.
|
||||||
|
|
||||||
|
Some(hdr) => match String::from_utf8(hdr.as_bytes().into()) {
|
||||||
|
Ok(val) => val,
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
Err(e) => {
|
||||||
|
dbg_msg!(e);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
Err(_) => return Ok(()),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let compressor: CompressionType = match determine_compressor(accept_encoding) {
|
let compressor: CompressionType = match determine_compressor(accept_encoding) {
|
||||||
@ -636,18 +629,18 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_determine_compressor() {
|
fn test_determine_compressor() {
|
||||||
// Single compressor given.
|
// Single compressor given.
|
||||||
assert_eq!(determine_compressor("unsupported"), None);
|
assert_eq!(determine_compressor("unsupported".to_string()), None);
|
||||||
assert_eq!(determine_compressor("gzip"), Some(CompressionType::Gzip));
|
assert_eq!(determine_compressor("gzip".to_string()), Some(CompressionType::Gzip));
|
||||||
assert_eq!(determine_compressor("*"), Some(DEFAULT_COMPRESSOR));
|
assert_eq!(determine_compressor("*".to_string()), Some(DEFAULT_COMPRESSOR));
|
||||||
|
|
||||||
// Multiple compressors.
|
// Multiple compressors.
|
||||||
assert_eq!(determine_compressor("gzip, br"), Some(CompressionType::Brotli));
|
assert_eq!(determine_compressor("gzip, br".to_string()), Some(CompressionType::Brotli));
|
||||||
assert_eq!(determine_compressor("gzip;q=0.8, br;q=0.3"), Some(CompressionType::Gzip));
|
assert_eq!(determine_compressor("gzip;q=0.8, br;q=0.3".to_string()), Some(CompressionType::Gzip));
|
||||||
assert_eq!(determine_compressor("br, gzip"), Some(CompressionType::Brotli));
|
assert_eq!(determine_compressor("br, gzip".to_string()), Some(CompressionType::Brotli));
|
||||||
assert_eq!(determine_compressor("br;q=0.3, gzip;q=0.4"), Some(CompressionType::Gzip));
|
assert_eq!(determine_compressor("br;q=0.3, gzip;q=0.4".to_string()), Some(CompressionType::Gzip));
|
||||||
|
|
||||||
// Invalid q-values.
|
// Invalid q-values.
|
||||||
assert_eq!(determine_compressor("gzip;q=NAN"), None);
|
assert_eq!(determine_compressor("gzip;q=NAN".to_string()), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -672,9 +665,9 @@ mod tests {
|
|||||||
] {
|
] {
|
||||||
// Determine what the expected encoding should be based on both the
|
// Determine what the expected encoding should be based on both the
|
||||||
// specific encodings we accept.
|
// specific encodings we accept.
|
||||||
let expected_encoding: CompressionType = match determine_compressor(accept_encoding) {
|
let expected_encoding: CompressionType = match determine_compressor(accept_encoding.to_string()) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => panic!("determine_compressor(accept_encoding) => None"),
|
None => panic!("determine_compressor(accept_encoding.to_string()) => None"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build headers with our Accept-Encoding.
|
// Build headers with our Accept-Encoding.
|
||||||
@ -691,8 +684,8 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Perform the compression.
|
// Perform the compression.
|
||||||
if let Err(e) = block_on(compress_response(req_headers, &mut res)) {
|
if let Err(e) = block_on(compress_response(&req_headers, &mut res)) {
|
||||||
panic!("compress_response(req_headers, &mut res) => Err(\"{}\")", e);
|
panic!("compress_response(&req_headers, &mut res) => Err(\"{}\")", e);
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the content was compressed, we expect the Content-Encoding
|
// If the content was compressed, we expect the Content-Encoding
|
||||||
@ -739,9 +732,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut decompressed = Vec::<u8>::new();
|
let mut decompressed = Vec::<u8>::new();
|
||||||
match io::copy(&mut decoder, &mut decompressed) {
|
if let Err(e) = io::copy(&mut decoder, &mut decompressed) {
|
||||||
Ok(_) => {}
|
panic!("{}", e);
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(decompressed.eq(&expected_lorem_ipsum));
|
assert!(decompressed.eq(&expected_lorem_ipsum));
|
||||||
|
@ -19,7 +19,7 @@ struct SettingsTemplate {
|
|||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
|
|
||||||
const PREFS: [&str; 11] = [
|
const PREFS: [&str; 12] = [
|
||||||
"theme",
|
"theme",
|
||||||
"front_page",
|
"front_page",
|
||||||
"layout",
|
"layout",
|
||||||
@ -31,6 +31,7 @@ const PREFS: [&str; 11] = [
|
|||||||
"use_hls",
|
"use_hls",
|
||||||
"hide_hls_notification",
|
"hide_hls_notification",
|
||||||
"autoplay_videos",
|
"autoplay_videos",
|
||||||
|
"hide_awards",
|
||||||
];
|
];
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
@ -39,7 +40,7 @@ const PREFS: [&str; 11] = [
|
|||||||
pub async fn get(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn get(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
let url = req.uri().to_string();
|
let url = req.uri().to_string();
|
||||||
template(SettingsTemplate {
|
template(SettingsTemplate {
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
|
catch_random, error, filter_posts, format_num, format_url, get_filters, nsfw_landing, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
|
||||||
};
|
};
|
||||||
use crate::{client::json, server::ResponseExt, RequestExt};
|
use crate::{client::json, server::ResponseExt, RequestExt};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
@ -26,6 +26,7 @@ struct SubredditTemplate {
|
|||||||
all_posts_filtered: bool,
|
all_posts_filtered: bool,
|
||||||
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
||||||
all_posts_hidden_nsfw: bool,
|
all_posts_hidden_nsfw: bool,
|
||||||
|
no_posts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -96,6 +97,12 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return landing page if this post if this is NSFW community but the user
|
||||||
|
// has disabled the display of NSFW content or if the instance is SFW-only.
|
||||||
|
if sub.nsfw && (setting(&req, "show_nsfw") != "on" || crate::utils::sfw_only()) {
|
||||||
|
return Ok(nsfw_landing(req).await.unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub_name.clone(), sort, req.uri().query().unwrap_or_default());
|
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub_name.clone(), sort, req.uri().query().unwrap_or_default());
|
||||||
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
|
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 redirect_url = url[1..].replace('?', "%3F").replace('&', "%26").replace('+', "%2B");
|
||||||
@ -108,29 +115,32 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
posts: Vec::new(),
|
posts: Vec::new(),
|
||||||
sort: (sort, param(&path, "t").unwrap_or_default()),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after").unwrap_or_default(), "".to_string()),
|
ends: (param(&path, "after").unwrap_or_default(), "".to_string()),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
is_filtered: true,
|
is_filtered: true,
|
||||||
all_posts_filtered: false,
|
all_posts_filtered: false,
|
||||||
all_posts_hidden_nsfw: false,
|
all_posts_hidden_nsfw: false,
|
||||||
|
no_posts: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
match Post::fetch(&path, quarantined).await {
|
match Post::fetch(&path, quarantined).await {
|
||||||
Ok((mut posts, after)) => {
|
Ok((mut posts, after)) => {
|
||||||
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
||||||
let all_posts_hidden_nsfw = posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on";
|
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");
|
||||||
template(SubredditTemplate {
|
template(SubredditTemplate {
|
||||||
sub,
|
sub,
|
||||||
posts,
|
posts,
|
||||||
sort: (sort, param(&path, "t").unwrap_or_default()),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after").unwrap_or_default(), after),
|
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
is_filtered: false,
|
is_filtered: false,
|
||||||
all_posts_filtered,
|
all_posts_filtered,
|
||||||
all_posts_hidden_nsfw,
|
all_posts_hidden_nsfw,
|
||||||
|
no_posts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(msg) => match msg.as_str() {
|
Err(msg) => match msg.as_str() {
|
||||||
@ -149,7 +159,7 @@ pub fn quarantine(req: Request<Body>, sub: String) -> Result<Response<Body>, Str
|
|||||||
msg: "Please click the button below to continue to this subreddit.".to_string(),
|
msg: "Please click the button below to continue to this subreddit.".to_string(),
|
||||||
url: req.uri().to_string(),
|
url: req.uri().to_string(),
|
||||||
sub,
|
sub,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
@ -196,7 +206,7 @@ pub async fn subscriptions_filters(req: Request<Body>) -> Result<Response<Body>,
|
|||||||
|
|
||||||
let query = req.uri().query().unwrap_or_default().to_string();
|
let query = req.uri().query().unwrap_or_default().to_string();
|
||||||
|
|
||||||
let preferences = Preferences::new(req);
|
let preferences = Preferences::new(&req);
|
||||||
let mut sub_list = preferences.subscriptions;
|
let mut sub_list = preferences.subscriptions;
|
||||||
let mut filters = preferences.filters;
|
let mut filters = preferences.filters;
|
||||||
|
|
||||||
@ -309,7 +319,7 @@ pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
sub,
|
sub,
|
||||||
wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or("<h3>Wiki not found</h3>")),
|
wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or("<h3>Wiki not found</h3>")),
|
||||||
page,
|
page,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
}),
|
}),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
@ -347,7 +357,7 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
// ),
|
// ),
|
||||||
sub,
|
sub,
|
||||||
page: "Sidebar".to_string(),
|
page: "Sidebar".to_string(),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
}),
|
}),
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
@ -420,5 +430,6 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, String> {
|
|||||||
members: format_num(members),
|
members: format_num(members),
|
||||||
active: format_num(active),
|
active: format_num(active),
|
||||||
wiki: res["data"]["wiki_enabled"].as_bool().unwrap_or_default(),
|
wiki: res["data"]["wiki_enabled"].as_bool().unwrap_or_default(),
|
||||||
|
nsfw: res["data"]["over18"].as_bool().unwrap_or_default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
22
src/user.rs
22
src/user.rs
@ -1,7 +1,7 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::client::json;
|
use crate::client::json;
|
||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
use crate::utils::{error, filter_posts, format_url, get_filters, param, setting, template, Post, Preferences, User};
|
use crate::utils::{error, filter_posts, format_url, get_filters, nsfw_landing, param, setting, template, Post, Preferences, User};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
use time::{macros::format_description, OffsetDateTime};
|
use time::{macros::format_description, OffsetDateTime};
|
||||||
@ -26,6 +26,7 @@ struct UserTemplate {
|
|||||||
all_posts_filtered: bool,
|
all_posts_filtered: bool,
|
||||||
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
/// Whether all posts were hidden because they are NSFW (and user has disabled show NSFW)
|
||||||
all_posts_hidden_nsfw: bool,
|
all_posts_hidden_nsfw: bool,
|
||||||
|
no_posts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
@ -45,8 +46,17 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
// Retrieve other variables from Libreddit request
|
// Retrieve other variables from Libreddit request
|
||||||
let sort = param(&path, "sort").unwrap_or_default();
|
let sort = param(&path, "sort").unwrap_or_default();
|
||||||
let username = req.param("name").unwrap_or_default();
|
let username = req.param("name").unwrap_or_default();
|
||||||
|
|
||||||
|
// Retrieve info from user about page.
|
||||||
let user = user(&username).await.unwrap_or_default();
|
let user = user(&username).await.unwrap_or_default();
|
||||||
|
|
||||||
|
// Return landing page if this post if this Reddit deems this user NSFW,
|
||||||
|
// but we have also disabled the display of NSFW content or if the instance
|
||||||
|
// is SFW-only.
|
||||||
|
if user.nsfw && (setting(&req, "show_nsfw") != "on" || crate::utils::sfw_only()) {
|
||||||
|
return Ok(nsfw_landing(req).await.unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
let filters = get_filters(&req);
|
let filters = get_filters(&req);
|
||||||
if filters.contains(&["u_", &username].concat()) {
|
if filters.contains(&["u_", &username].concat()) {
|
||||||
template(UserTemplate {
|
template(UserTemplate {
|
||||||
@ -55,31 +65,34 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
sort: (sort, param(&path, "t").unwrap_or_default()),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after").unwrap_or_default(), "".to_string()),
|
ends: (param(&path, "after").unwrap_or_default(), "".to_string()),
|
||||||
listing,
|
listing,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
is_filtered: true,
|
is_filtered: true,
|
||||||
all_posts_filtered: false,
|
all_posts_filtered: false,
|
||||||
all_posts_hidden_nsfw: false,
|
all_posts_hidden_nsfw: false,
|
||||||
|
no_posts: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Request user posts/comments from Reddit
|
// Request user posts/comments from Reddit
|
||||||
match Post::fetch(&path, false).await {
|
match Post::fetch(&path, false).await {
|
||||||
Ok((mut posts, after)) => {
|
Ok((mut posts, after)) => {
|
||||||
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
let (_, all_posts_filtered) = filter_posts(&mut posts, &filters);
|
||||||
let all_posts_hidden_nsfw = posts.iter().all(|p| p.flags.nsfw) && setting(&req, "show_nsfw") != "on";
|
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");
|
||||||
template(UserTemplate {
|
template(UserTemplate {
|
||||||
user,
|
user,
|
||||||
posts,
|
posts,
|
||||||
sort: (sort, param(&path, "t").unwrap_or_default()),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after").unwrap_or_default(), after),
|
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||||
listing,
|
listing,
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
is_filtered: false,
|
is_filtered: false,
|
||||||
all_posts_filtered,
|
all_posts_filtered,
|
||||||
all_posts_hidden_nsfw,
|
all_posts_hidden_nsfw,
|
||||||
|
no_posts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// If there is an error show error page
|
// If there is an error show error page
|
||||||
@ -111,6 +124,7 @@ async fn user(name: &str) -> Result<User, String> {
|
|||||||
created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(),
|
created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(),
|
||||||
banner: about("banner_img"),
|
banner: about("banner_img"),
|
||||||
description: about("public_description"),
|
description: about("public_description"),
|
||||||
|
nsfw: res["data"]["subreddit"]["over_18"].as_bool().unwrap_or_default(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
93
src/utils.rs
93
src/utils.rs
@ -9,6 +9,7 @@ use regex::Regex;
|
|||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::env;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use time::{macros::format_description, Duration, OffsetDateTime};
|
use time::{macros::format_description, Duration, OffsetDateTime};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -28,6 +29,16 @@ macro_rules! dbg_msg {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identifies whether or not the page is a subreddit, a user page, or a post.
|
||||||
|
/// This is used by the NSFW landing template to determine the mesage to convey
|
||||||
|
/// to the user.
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub enum ResourceType {
|
||||||
|
Subreddit,
|
||||||
|
User,
|
||||||
|
Post,
|
||||||
|
}
|
||||||
|
|
||||||
// Post flair with content, background color and foreground color
|
// Post flair with content, background color and foreground color
|
||||||
pub struct Flair {
|
pub struct Flair {
|
||||||
pub flair_parts: Vec<FlairPart>,
|
pub flair_parts: Vec<FlairPart>,
|
||||||
@ -229,6 +240,7 @@ pub struct Post {
|
|||||||
pub comments: (String, String),
|
pub comments: (String, String),
|
||||||
pub gallery: Vec<GalleryMedia>,
|
pub gallery: Vec<GalleryMedia>,
|
||||||
pub awards: Awards,
|
pub awards: Awards,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Post {
|
impl Post {
|
||||||
@ -329,6 +341,7 @@ impl Post {
|
|||||||
comments: format_num(data["num_comments"].as_i64().unwrap_or_default()),
|
comments: format_num(data["num_comments"].as_i64().unwrap_or_default()),
|
||||||
gallery,
|
gallery,
|
||||||
awards,
|
awards,
|
||||||
|
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +370,7 @@ pub struct Comment {
|
|||||||
pub awards: Awards,
|
pub awards: Awards,
|
||||||
pub collapsed: bool,
|
pub collapsed: bool,
|
||||||
pub is_filtered: bool,
|
pub is_filtered: bool,
|
||||||
|
pub prefs: Preferences,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
@ -420,6 +434,27 @@ pub struct ErrorTemplate {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Template for NSFW landing page. The landing page is displayed when a page's
|
||||||
|
/// content is wholly NSFW, but a user has not enabled the option to view NSFW
|
||||||
|
/// posts.
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "nsfwlanding.html")]
|
||||||
|
pub struct NSFWLandingTemplate {
|
||||||
|
/// Identifier for the resource. This is either a subreddit name or a
|
||||||
|
/// username. (In the case of the latter, set is_user to true.)
|
||||||
|
pub res: String,
|
||||||
|
|
||||||
|
/// Identifies whether or not the resource is a subreddit, a user page,
|
||||||
|
/// or a post.
|
||||||
|
pub res_type: ResourceType,
|
||||||
|
|
||||||
|
/// User preferences.
|
||||||
|
pub prefs: Preferences,
|
||||||
|
|
||||||
|
/// Request URL.
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
// User struct containing metadata about user
|
// User struct containing metadata about user
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -430,6 +465,7 @@ pub struct User {
|
|||||||
pub created: String,
|
pub created: String,
|
||||||
pub banner: String,
|
pub banner: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -444,6 +480,7 @@ pub struct Subreddit {
|
|||||||
pub members: (String, String),
|
pub members: (String, String),
|
||||||
pub active: (String, String),
|
pub active: (String, String),
|
||||||
pub wiki: bool,
|
pub wiki: bool,
|
||||||
|
pub nsfw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser for query params, used in sorting (eg. /r/rust/?sort=hot)
|
// Parser for query params, used in sorting (eg. /r/rust/?sort=hot)
|
||||||
@ -472,6 +509,7 @@ pub struct Preferences {
|
|||||||
pub post_sort: String,
|
pub post_sort: String,
|
||||||
pub subscriptions: Vec<String>,
|
pub subscriptions: Vec<String>,
|
||||||
pub filters: Vec<String>,
|
pub filters: Vec<String>,
|
||||||
|
pub hide_awards: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
@ -481,7 +519,7 @@ pub struct ThemeAssets;
|
|||||||
|
|
||||||
impl Preferences {
|
impl Preferences {
|
||||||
// Build preferences from cookies
|
// Build preferences from cookies
|
||||||
pub fn new(req: Request<Body>) -> Self {
|
pub fn new(req: &Request<Body>) -> Self {
|
||||||
// Read available theme names from embedded css files.
|
// Read available theme names from embedded css files.
|
||||||
// Always make the default "system" theme available.
|
// Always make the default "system" theme available.
|
||||||
let mut themes = vec!["system".to_string()];
|
let mut themes = vec!["system".to_string()];
|
||||||
@ -504,6 +542,7 @@ impl Preferences {
|
|||||||
post_sort: setting(&req, "post_sort"),
|
post_sort: setting(&req, "post_sort"),
|
||||||
subscriptions: setting(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
subscriptions: setting(&req, "subscriptions").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||||
filters: setting(&req, "filters").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
filters: setting(&req, "filters").split('+').map(String::from).filter(|s| !s.is_empty()).collect(),
|
||||||
|
hide_awards: setting(&req, "hide_awards"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -617,6 +656,7 @@ pub async fn parse_post(post: &serde_json::Value) -> Post {
|
|||||||
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
|
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
|
||||||
gallery,
|
gallery,
|
||||||
awards,
|
awards,
|
||||||
|
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,8 +683,8 @@ pub fn setting(req: &Request<Body>, name: &str) -> String {
|
|||||||
req
|
req
|
||||||
.cookie(name)
|
.cookie(name)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
// If there is no cookie for this setting, try receiving a default from an environment variable
|
// If there is no cookie for this setting, try receiving a default from the config
|
||||||
if let Ok(default) = std::env::var(format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())) {
|
if let Some(default) = crate::config::get_setting(&format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())) {
|
||||||
Cookie::new(name, default)
|
Cookie::new(name, default)
|
||||||
} else {
|
} else {
|
||||||
Cookie::named(name)
|
Cookie::named(name)
|
||||||
@ -820,7 +860,7 @@ pub async fn error(req: Request<Body>, msg: impl ToString) -> Result<Response<Bo
|
|||||||
let url = req.uri().to_string();
|
let url = req.uri().to_string();
|
||||||
let body = ErrorTemplate {
|
let body = ErrorTemplate {
|
||||||
msg: msg.to_string(),
|
msg: msg.to_string(),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(&req),
|
||||||
url,
|
url,
|
||||||
}
|
}
|
||||||
.render()
|
.render()
|
||||||
@ -829,6 +869,51 @@ pub async fn error(req: Request<Body>, msg: impl ToString) -> Result<Response<Bo
|
|||||||
Ok(Response::builder().status(404).header("content-type", "text/html").body(body.into()).unwrap_or_default())
|
Ok(Response::builder().status(404).header("content-type", "text/html").body(body.into()).unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the config/env variable `LIBREDDIT_SFW_ONLY` carries the
|
||||||
|
/// value `on`.
|
||||||
|
///
|
||||||
|
/// If this variable is set as such, the instance will operate in SFW-only
|
||||||
|
/// mode; all NSFW content will be filtered. Attempts to access NSFW
|
||||||
|
/// subreddits or posts or userpages for users Reddit has deemed NSFW will
|
||||||
|
/// be denied.
|
||||||
|
pub fn sfw_only() -> bool {
|
||||||
|
match crate::config::get_setting("LIBREDDIT_SFW_ONLY") {
|
||||||
|
Some(val) => val == "on",
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders the landing page for NSFW content when the user has not enabled
|
||||||
|
/// "show NSFW posts" in settings.
|
||||||
|
pub async fn nsfw_landing(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
|
let res_type: ResourceType;
|
||||||
|
let url = req.uri().to_string();
|
||||||
|
|
||||||
|
// Determine from the request URL if the resource is a subreddit, a user
|
||||||
|
// page, or a post.
|
||||||
|
let res: String = if !req.param("name").unwrap_or_default().is_empty() {
|
||||||
|
res_type = ResourceType::User;
|
||||||
|
req.param("name").unwrap_or_default()
|
||||||
|
} else if !req.param("id").unwrap_or_default().is_empty() {
|
||||||
|
res_type = ResourceType::Post;
|
||||||
|
req.param("id").unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
res_type = ResourceType::Subreddit;
|
||||||
|
req.param("sub").unwrap_or_default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = NSFWLandingTemplate {
|
||||||
|
res,
|
||||||
|
res_type,
|
||||||
|
prefs: Preferences::new(&req),
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(Response::builder().status(403).header("content-type", "text/html").body(body.into()).unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{format_num, format_url, rewrite_urls};
|
use super::{format_num, format_url, rewrite_urls};
|
||||||
|
@ -160,16 +160,35 @@ main {
|
|||||||
overflow: inherit;
|
overflow: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
/* Body footer. */
|
||||||
|
body > footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > footer > div#sfw-only {
|
||||||
|
color: var(--green);
|
||||||
|
border: 1px solid var(--green);
|
||||||
|
padding: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
/* / Body footer. */
|
||||||
|
|
||||||
|
/* Footer in content block. */
|
||||||
|
main > * > footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer > a {
|
main > * > footer > a {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* / Footer in content block. */
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@ -485,7 +504,7 @@ button.submit:hover > svg { stroke: var(--accent); }
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sort_options, #listing_options, footer > a {
|
#sort_options, #listing_options, main > * > footer > a {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
@ -494,7 +513,7 @@ button.submit:hover > svg { stroke: var(--accent); }
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sort_options > a, #listing_options > a, footer > a {
|
#sort_options > a, #listing_options > a, main > * > footer > a {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -1118,22 +1137,16 @@ summary.comment_data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.prefs {
|
.prefs {
|
||||||
display: flex;
|
padding: 10px 20px 20px;
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--post);
|
background: var(--post);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefs > div {
|
.prefs fieldset {
|
||||||
display: flex;
|
border: 0;
|
||||||
justify-content: space-between;
|
padding: 10px 0;
|
||||||
width: 100%;
|
margin: 0 0 5px;
|
||||||
height: 35px;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefs legend {
|
.prefs legend {
|
||||||
@ -1141,11 +1154,25 @@ summary.comment_data {
|
|||||||
border-bottom: 1px solid var(--highlighted);
|
border-bottom: 1px solid var(--highlighted);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
width: 100%;
|
||||||
|
float: left; /* places the legend inside the (invisible) border, instead of vertically centered on top border*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefs legend:not(:first-child) {
|
.prefs-group {
|
||||||
padding-top: 10px;
|
display: flex;
|
||||||
margin-top: 15px;
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefs-group > *:not(:last-child) {
|
||||||
|
margin-right: 1ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefs-group > *:last-child {
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefs select {
|
.prefs select {
|
||||||
@ -1163,7 +1190,8 @@ aside.prefs {
|
|||||||
background: var(--highlighted);
|
background: var(--highlighted);
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-top: 20px;
|
margin-top: 5px;
|
||||||
|
width: 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="submit"] {
|
input[type="submit"] {
|
||||||
@ -1306,6 +1334,31 @@ td, th {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NSFW Landing Page */
|
||||||
|
|
||||||
|
#nsfw_landing {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nsfw_landing h1 {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nsfw_landing p {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nsfw_landing a {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<a id="code" href="https://github.com/spikecodes/libreddit">
|
<a id="code" href="https://github.com/libreddit/libreddit" target="_blank" rel="noopener noreferrer">
|
||||||
<span>code</span>
|
<span>code</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<title>code</title>
|
<title>code</title>
|
||||||
@ -65,5 +65,10 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block footer %}
|
||||||
|
{% if crate::utils::sfw_only() %}
|
||||||
|
<footer><div id="sfw-only">This instance of Libreddit is SFW-only.</div></footer>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post_link }}{{ id }}/?context=3" class="created" title="{{ created }}">{{ rel_time }}</a>
|
<a href="{{ post_link }}{{ id }}/?context=3" class="created" title="{{ created }}">{{ rel_time }}</a>
|
||||||
{% if edited.0 != "".to_string() %}<span class="edited" title="{{ edited.1 }}">edited {{ edited.0 }}</span>{% endif %}
|
{% if edited.0 != "".to_string() %}<span class="edited" title="{{ edited.1 }}">edited {{ edited.0 }}</span>{% endif %}
|
||||||
{% if !awards.is_empty() %}
|
{% if !awards.is_empty() && prefs.hide_awards != "on" %}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
{% for award in awards.clone() %}
|
{% for award in awards.clone() %}
|
||||||
<span class="award" title="{{ award.name }}">
|
<span class="award" title="{{ award.name }}">
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
<a class="post_author {{ post.author.distinguished }}" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
<a class="post_author {{ post.author.distinguished }}" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
{% if !post.awards.is_empty() %}
|
{% if !post.awards.is_empty() && prefs.hide_awards != "on" %}
|
||||||
{% for award in post.awards.clone() %}
|
{% for award in post.awards.clone() %}
|
||||||
<span class="award" title="{{ award.name }}">
|
<span class="award" title="{{ award.name }}">
|
||||||
<img alt="{{ award.name }}" src="{{ award.icon_url }}" width="16" height="16"/>
|
<img alt="{{ award.name }}" src="{{ award.icon_url }}" width="16" height="16"/>
|
||||||
@ -104,4 +104,4 @@
|
|||||||
</footer>
|
</footer>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
28
templates/nsfwlanding.html
Normal file
28
templates/nsfwlanding.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}NSFW content gated{% endblock %}
|
||||||
|
{% block sortstyle %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div id="nsfw_landing">
|
||||||
|
<h1>
|
||||||
|
😱
|
||||||
|
{% if res_type == crate::utils::ResourceType::Subreddit %}
|
||||||
|
r/{{ res }} is a NSFW community!
|
||||||
|
{% else if res_type == crate::utils::ResourceType::User %}
|
||||||
|
u/{{ res }}'s content is NSFW!
|
||||||
|
{% else if res_type == crate::utils::ResourceType::Post %}
|
||||||
|
This post is NSFW!
|
||||||
|
{% endif %}
|
||||||
|
</h1>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% if crate::utils::sfw_only() %}
|
||||||
|
This instance of Libreddit is SFW-only.</p>
|
||||||
|
{% else %}
|
||||||
|
Enable "Show NSFW posts" in <a href="/settings">settings</a> to view this {% if res_type == crate::utils::ResourceType::Subreddit %}subreddit{% else if res_type == crate::utils::ResourceType::User %}user's posts or comments{% else if res_type == crate::utils::ResourceType::Post %}post{% endif %}.
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block footer %}
|
||||||
|
{% endblock %}
|
@ -61,6 +61,10 @@
|
|||||||
<span class="listing_warn">All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</span>
|
<span class="listing_warn">All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if no_posts %}
|
||||||
|
<center>No posts were found.</center>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if all_posts_filtered %}
|
{% if all_posts_filtered %}
|
||||||
<span class="listing_warn">(All content on this page has been filtered)</span>
|
<span class="listing_warn">(All content on this page has been filtered)</span>
|
||||||
{% else if is_filtered %}
|
{% else if is_filtered %}
|
||||||
|
@ -11,74 +11,86 @@
|
|||||||
<div id="settings">
|
<div id="settings">
|
||||||
<form action="/settings" method="POST">
|
<form action="/settings" method="POST">
|
||||||
<div class="prefs">
|
<div class="prefs">
|
||||||
<legend>Appearance</legend>
|
<fieldset>
|
||||||
<div id="theme">
|
<legend>Appearance</legend>
|
||||||
<label for="theme">Theme:</label>
|
<div class="prefs-group">
|
||||||
<select name="theme">
|
<label for="theme">Theme:</label>
|
||||||
{% call utils::options(prefs.theme, prefs.available_themes, "system") %}
|
<select name="theme" id="theme">
|
||||||
</select>
|
{% call utils::options(prefs.theme, prefs.available_themes, "system") %}
|
||||||
</div>
|
</select>
|
||||||
<legend>Interface</legend>
|
</div>
|
||||||
<div id="front_page">
|
</fieldset>
|
||||||
<label for="front_page">Front page:</label>
|
<fieldset>
|
||||||
<select name="front_page">
|
<legend>Interface</legend>
|
||||||
{% call utils::options(prefs.front_page, ["default", "popular", "all"], "default") %}
|
<div class="prefs-group">
|
||||||
</select>
|
<label for="front_page">Front page:</label>
|
||||||
</div>
|
<select name="front_page" id="front_page">
|
||||||
<div id="layout">
|
{% call utils::options(prefs.front_page, ["default", "popular", "all"], "default") %}
|
||||||
<label for="layout">Layout:</label>
|
</select>
|
||||||
<select name="layout">
|
</div>
|
||||||
{% call utils::options(prefs.layout, ["card", "clean", "compact"], "card") %}
|
<div class="prefs-group">
|
||||||
</select>
|
<label for="layout">Layout:</label>
|
||||||
</div>
|
<select name="layout" id="layout">
|
||||||
<div id="wide">
|
{% call utils::options(prefs.layout, ["card", "clean", "compact"], "card") %}
|
||||||
<label for="wide">Wide UI:</label>
|
</select>
|
||||||
<input type="hidden" value="off" name="wide">
|
</div>
|
||||||
<input type="checkbox" name="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
<div class="prefs-group">
|
||||||
</div>
|
<label for="wide">Wide UI:</label>
|
||||||
<legend>Content</legend>
|
<input type="hidden" value="off" name="wide">
|
||||||
<div id="post_sort">
|
<input type="checkbox" name="wide" id="wide" {% if prefs.wide == "on" %}checked{% endif %}>
|
||||||
<label for="post_sort" title="Applies only to subreddit feeds">Default subreddit post sort:</label>
|
</div>
|
||||||
<select name="post_sort">
|
</fieldset>
|
||||||
{% call utils::options(prefs.post_sort, ["hot", "new", "top", "rising", "controversial"], "hot") %}
|
<fieldset>
|
||||||
</select>
|
<legend>Content</legend>
|
||||||
</div>
|
<div class="prefs-group">
|
||||||
<div id="comment_sort">
|
<label for="post_sort" title="Applies only to subreddit feeds">Default subreddit post sort:</label>
|
||||||
<label for="comment_sort">Default comment sort:</label>
|
<select name="post_sort">
|
||||||
<select name="comment_sort">
|
{% call utils::options(prefs.post_sort, ["hot", "new", "top", "rising", "controversial"], "hot") %}
|
||||||
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
</select>
|
||||||
</select>
|
</div>
|
||||||
</div>
|
<div class="prefs-group">
|
||||||
<div id="show_nsfw">
|
<label for="comment_sort">Default comment sort:</label>
|
||||||
<label for="show_nsfw">Show NSFW posts:</label>
|
<select name="comment_sort" id="comment_sort">
|
||||||
<input type="hidden" value="off" name="show_nsfw">
|
{% call utils::options(prefs.comment_sort, ["confidence", "top", "new", "controversial", "old"], "confidence") %}
|
||||||
<input type="checkbox" name="show_nsfw" {% if prefs.show_nsfw == "on" %}checked{% endif %}>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="blur_nsfw">
|
{% if !crate::utils::sfw_only() %}
|
||||||
<label for="blur_nsfw">Blur NSFW previews:</label>
|
<div class="prefs-group">
|
||||||
<input type="hidden" value="off" name="blur_nsfw">
|
<label for="show_nsfw">Show NSFW posts:</label>
|
||||||
<input type="checkbox" name="blur_nsfw" {% if prefs.blur_nsfw == "on" %}checked{% endif %}>
|
<input type="hidden" value="off" name="show_nsfw">
|
||||||
</div>
|
<input type="checkbox" name="show_nsfw" id="show_nsfw" {% if prefs.show_nsfw == "on" %}checked{% endif %}>
|
||||||
<div id="autoplay_videos">
|
</div>
|
||||||
<label for="autoplay_videos">Autoplay videos</label>
|
<div class="prefs-group">
|
||||||
<input type="hidden" value="off" name="autoplay_videos">
|
<label for="blur_nsfw">Blur NSFW previews:</label>
|
||||||
<input type="checkbox" name="autoplay_videos" {% if prefs.autoplay_videos == "on" %}checked{% endif %}>
|
<input type="hidden" value="off" name="blur_nsfw">
|
||||||
</div>
|
<input type="checkbox" name="blur_nsfw" id="blur_nsfw" {% if prefs.blur_nsfw == "on" %}checked{% endif %}>
|
||||||
<div id="use_hls">
|
</div>
|
||||||
<label for="use_hls">Use HLS for videos
|
{% endif %}
|
||||||
|
<div class="prefs-group">
|
||||||
|
<label for="autoplay_videos">Autoplay videos</label>
|
||||||
|
<input type="hidden" value="off" name="autoplay_videos">
|
||||||
|
<input type="checkbox" name="autoplay_videos" id="autoplay_videos" {% if prefs.autoplay_videos == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
|
<div class="prefs-group">
|
||||||
|
<label for="use_hls">Use HLS for videos</label>
|
||||||
<details id="feeds">
|
<details id="feeds">
|
||||||
<summary>Why?</summary>
|
<summary>Why?</summary>
|
||||||
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Libreddit JS-free or utilize this feature.</div>
|
<div id="feed_list" class="helper">Reddit videos require JavaScript (via HLS.js) to be enabled to be played with audio. Therefore, this toggle lets you either use Libreddit JS-free or utilize this feature.</div>
|
||||||
</details>
|
</details>
|
||||||
</label>
|
<input type="hidden" value="off" name="use_hls">
|
||||||
<input type="hidden" value="off" name="use_hls">
|
<input type="checkbox" name="use_hls" id="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
|
||||||
<input type="checkbox" name="use_hls" {% if prefs.use_hls == "on" %}checked{% endif %}>
|
</div>
|
||||||
</div>
|
<div class="prefs-group">
|
||||||
<div id="hide_hls_notification">
|
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
||||||
<label for="hide_hls_notification">Hide notification about possible HLS usage</label>
|
<input type="hidden" value="off" name="hide_hls_notification">
|
||||||
<input type="hidden" value="off" name="hide_hls_notification">
|
<input type="checkbox" name="hide_hls_notification" id="hide_hls_notification" {% if prefs.hide_hls_notification == "on" %}checked{% endif %}>
|
||||||
<input type="checkbox" name="hide_hls_notification" {% if prefs.hide_hls_notification == "on" %}checked{% endif %}>
|
</div>
|
||||||
</div>
|
<div class="prefs-group">
|
||||||
|
<label for="hide_awards">Hide awards</label>
|
||||||
|
<input type="hidden" value="off" name="hide_awards">
|
||||||
|
<input type="checkbox" name="hide_awards" id="hide_awards" {% if prefs.hide_awards == "on" %}checked{% endif %}>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
<input id="save" type="submit" value="Save">
|
<input id="save" type="submit" value="Save">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -115,7 +127,7 @@
|
|||||||
|
|
||||||
<div id="settings_note">
|
<div id="settings_note">
|
||||||
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.</p><br>
|
<p><b>Note:</b> settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.</p><br>
|
||||||
<p>You can restore your current settings and subscriptions after clearing your cookies using <a href="/settings/restore/?theme={{ prefs.theme }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&post_sort={{ prefs.post_sort }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&blur_nsfw={{ prefs.blur_nsfw }}&use_hls={{ prefs.use_hls }}&hide_hls_notification={{ prefs.hide_hls_notification }}&subscriptions={{ prefs.subscriptions.join("%2B") }}&filters={{ prefs.filters.join("%2B") }}">this link</a>.</p>
|
<p>You can restore your current settings and subscriptions after clearing your cookies using <a href="/settings/restore/?theme={{ prefs.theme }}&front_page={{ prefs.front_page }}&layout={{ prefs.layout }}&wide={{ prefs.wide }}&post_sort={{ prefs.post_sort }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_nsfw }}&blur_nsfw={{ prefs.blur_nsfw }}&use_hls={{ prefs.use_hls }}&hide_hls_notification={{ prefs.hide_hls_notification }}&hide_awards={{ prefs.hide_awards }}&subscriptions={{ prefs.subscriptions.join("%2B") }}&filters={{ prefs.filters.join("%2B") }}">this link</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@
|
|||||||
<center>All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</center>
|
<center>All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</center>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if no_posts %}
|
||||||
|
<center>No posts were found.</center>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if all_posts_filtered %}
|
{% if all_posts_filtered %}
|
||||||
<center>(All content on this page has been filtered)</center>
|
<center>(All content on this page has been filtered)</center>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -36,6 +36,10 @@
|
|||||||
<center>All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</center>
|
<center>All posts are hidden because they are NSFW. Enable "Show NSFW posts" in settings to view.</center>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if no_posts %}
|
||||||
|
<center>No posts were found.</center>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if all_posts_filtered %}
|
{% if all_posts_filtered %}
|
||||||
<center>(All content on this page has been filtered)</center>
|
<center>(All content on this page has been filtered)</center>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -73,7 +73,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
{% if !post.awards.is_empty() %}
|
{% if !post.awards.is_empty() && prefs.hide_awards != "on" %}
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
<span class="awards">
|
<span class="awards">
|
||||||
{% for award in post.awards.clone() %}
|
{% for award in post.awards.clone() %}
|
||||||
@ -115,7 +115,7 @@
|
|||||||
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
||||||
<script src="/hls.min.js"></script>
|
<script src="/hls.min.js"></script>
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls>
|
<video class="post_media_video short {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls>
|
||||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
@ -178,7 +178,7 @@
|
|||||||
<a class="post_author {{ post.author.distinguished }}" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
<a class="post_author {{ post.author.distinguished }}" href="/u/{{ post.author.name }}">u/{{ post.author.name }}</a>
|
||||||
<span class="dot">•</span>
|
<span class="dot">•</span>
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
{% if !post.awards.is_empty() %}
|
{% if !post.awards.is_empty() && prefs.hide_awards != "on" %}
|
||||||
{% for award in post.awards.clone() %}
|
{% for award in post.awards.clone() %}
|
||||||
<span class="award" title="{{ award.name }}">
|
<span class="award" title="{{ award.name }}">
|
||||||
<img alt="{{ award.name }}" src="{{ award.icon_url }}" width="16" height="16"/>
|
<img alt="{{ award.name }}" src="{{ award.icon_url }}" width="16" height="16"/>
|
||||||
@ -213,19 +213,19 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "gif" %}
|
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "gif" %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls loop {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
||||||
</div>
|
</div>
|
||||||
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %}
|
{% else if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "video" %}
|
||||||
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
{% if prefs.use_hls == "on" && !post.media.alt_url.is_empty() %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %} {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" controls preload="none">
|
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %} {% if prefs.autoplay_videos == "on" %}hls_autoplay{% endif %}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" controls preload="none">
|
||||||
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
<source src="{{ post.media.alt_url }}" type="application/vnd.apple.mpegurl" />
|
||||||
<source src="{{ post.media.url }}" type="video/mp4" />
|
<source src="{{ post.media.url }}" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="post_media_content">
|
<div class="post_media_content">
|
||||||
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" width="{{ post.media.width }}" height="{{ post.media.height }}" poster="{{ post.media.poster }}" preload="none" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
<video class="post_media_video short {%if post.flags.nsfw && prefs.blur_nsfw=="on" %}post_nsfw_blur{% endif %}" src="{{ post.media.url }}" {% if post.media.width > 0 && post.media.height > 0 %}width="{{ post.media.width }}" height="{{ post.media.height }}"{% endif %} poster="{{ post.media.poster }}" preload="none" controls {% if prefs.autoplay_videos == "on" %}autoplay{% endif %}><a href={{ post.media.url }}>Video</a></video>
|
||||||
</div>
|
</div>
|
||||||
{% call render_hls_notification(format!("{}%23{}", &self.url[1..].replace("&", "%26").replace("+", "%2B"), post.id)) %}
|
{% call render_hls_notification(format!("{}%23{}", &self.url[1..].replace("&", "%26").replace("+", "%2B"), post.id)) %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Reference in New Issue
Block a user