Merge branch 'spikecodes:master' into master

This commit is contained in:
Matt 2022-06-13 01:43:55 +00:00 committed by GitHub
commit d2fcfe85c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 450 additions and 263 deletions

205
Cargo.lock generated
View File

@ -100,6 +100,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.9.1"
@ -207,6 +216,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]]
name = "darling"
version = "0.13.4"
@ -242,6 +260,15 @@ dependencies = [
"syn",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "fastrand"
version = "1.7.0"
@ -371,6 +398,16 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "h2"
version = "0.3.13"
@ -424,9 +461,9 @@ dependencies = [
[[package]]
name = "http-body"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
@ -447,9 +484,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
version = "0.14.18"
version = "0.14.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
dependencies = [
"bytes",
"futures-channel",
@ -503,9 +540,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.8.1"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [
"autocfg",
"hashbrown 0.11.2",
@ -522,9 +559,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "js-sys"
@ -543,13 +580,13 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.125"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libreddit"
version = "0.22.7"
version = "0.22.9"
dependencies = [
"askama",
"async-recursion",
@ -559,8 +596,10 @@ dependencies = [
"futures-lite",
"hyper",
"hyper-rustls",
"percent-encoding",
"regex",
"route-recognizer",
"rust-embed",
"serde",
"serde_json",
"time",
@ -664,9 +703,15 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.10.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl-probe"
@ -676,9 +721,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "os_str_bytes"
version = "6.0.1"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "parking"
@ -729,11 +774,11 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.38"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -756,9 +801,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.5"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
"aho-corasick",
"memchr",
@ -767,9 +812,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.25"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "ring"
@ -793,10 +838,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
[[package]]
name = "rustls"
version = "0.20.5"
name = "rust-embed"
version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a024a432ae760ab3bff924ad91ce1cfa52cb57ed16e1ef32d0d249cfee1a6c13"
checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rustls"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
dependencies = [
"log",
"ring",
@ -827,18 +906,27 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.19"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"winapi",
"windows-sys",
]
[[package]]
@ -911,6 +999,19 @@ dependencies = [
"serde",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -956,13 +1057,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
@ -1124,6 +1225,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicase"
version = "2.6.0"
@ -1139,6 +1246,12 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
@ -1148,12 +1261,6 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "untrusted"
version = "0.7.1"
@ -1184,6 +1291,17 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.0"
@ -1290,6 +1408,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -3,7 +3,7 @@ name = "libreddit"
description = " Alternative private front-end to Reddit"
license = "AGPL-3.0"
repository = "https://github.com/spikecodes/libreddit"
version = "0.22.7"
version = "0.22.9"
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
edition = "2021"
@ -12,14 +12,16 @@ askama = { version = "0.11.1", default-features = false }
async-recursion = "1.0.0"
cached = "0.34.0"
clap = { version = "3.1.18", default-features = false, features = ["std"] }
regex = "1.5.5"
regex = "1.5.6"
serde = { version = "1.0.137", features = ["derive"] }
cookie = "0.16.0"
futures-lite = "1.12.0"
hyper = { version = "0.14.18", features = ["full"] }
hyper = { version = "0.14.19", features = ["full"] }
hyper-rustls = "0.23.0"
percent-encoding = "2.1.0"
route-recognizer = "0.3.1"
serde_json = "1.0.81"
tokio = { version = "1.18.2", features = ["full"] }
time = "0.3.9"
url = "2.2.2"
rust-embed = "6.4.0"

View File

@ -39,11 +39,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | |
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇩🇪 DE | ✅ |
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | |
| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | |
| [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | |
| [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | |
| [lr.riverside.rocks](https://lr.riverside.rocks) | 🇺🇸 US | |
| [libreddit.silkky.cloud](https://libreddit.silkky.cloud) | 🇫🇮 FI | ✅ |
| [libreddit.strongthany.cc](https://libreddit.strongthany.cc) | 🇺🇸 US | |
| [libreddit.database.red](https://libreddit.database.red) | 🇺🇸 US | ✅ |
| [libreddit.privacy.com.de](https://libreddit.privacy.com.de) | 🇩🇪 DE | |
@ -56,7 +54,6 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [libreddit.some-things.org](https://libreddit.some-things.org) | 🇨🇭 CH | |
| [reddit.stuehieyr.com](https://reddit.stuehieyr.com) | 🇩🇪 DE | |
| [lr.mint.lgbt](https://lr.mint.lgbt) | 🇨🇦 CA | |
| [libreddit.alefvanoon.xyz](https://libreddit.alefvanoon.xyz) | 🇺🇸 US | ✅ |
| [libreddit.igna.rocks](https://libreddit.igna.rocks) | 🇺🇸 US | |
| [libreddit.autarkic.org](https://libreddit.autarkic.org) | 🇺🇸 US | |
| [libreddit.flux.industries](https://libreddit.flux.industries) | 🇩🇪 DE | ✅ |
@ -72,7 +69,7 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [libreddit.hu](https://libreddit.hu) | 🇫🇮 FI | ✅ |
| [libreddit.totaldarkness.net](https://libreddit.totaldarkness.net) | 🇨🇦 CA | |
| [libreddit.esmailelbob.xyz](https://libreddit.esmailelbob.xyz) | 🇨🇦 CA | |
| [lr.vern.cc](https://lr.vern.cc) | 🇵🇱 PL | |
| [lr.vern.cc](https://lr.vern.cc) | 🇨🇦 CA | |
| [libreddit.nl](https://libreddit.nl) | 🇳🇱 NL | |
| [lr.stilic.ml](https://lr.stilic.ml) | 🇫🇷 FR | ✅ |
| [reddi.tk](https://reddi.tk) | 🇺🇸 US | ✅ |
@ -93,6 +90,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [reddit.beparanoid.de](https://reddit.beparanoid.de) | 🇨🇭 CH | |
| [libreddit.dcs0.hu](https://libreddit.dcs0.hu) | 🇭🇺 HU | |
| [reddit.dr460nf1r3.org](https://reddit.dr460nf1r3.org) | 🇩🇪 DE | ✅ |
| [rd.jae.su](https://rd.jae.su) | 🇫🇮 FI | |
| [libreddit.mha.fi](https://libreddit.mha.fi) | 🇫🇮 FI | |
| [libreddit.foss.wtf](https://libreddit.foss.wtf) | 🇩🇪 DE | |
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
| [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | |
| [kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion](http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion) | 🇳🇱 NL | |
@ -106,7 +106,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
| [ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion](http://ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion) | 🇩🇪 DE | |
| [lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion](http://lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion) | 🇨🇦 CA | |
| [libreddit.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion](http://libreddit.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion) | 🇨🇦 CA | |
| [reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion](reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion) | 🇨🇭 CH | |
| [reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion](http://reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion) | 🇨🇭 CH | |
| [inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion](http://inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion) | 🇫🇮 FI | |
| [libreddit.micohauwkjbyw5meacrb4ipicwvwg4xtzl7y7viv53kig2mdcsvwkyyd.onion](http://libreddit.micohauwkjbyw5meacrb4ipicwvwg4xtzl7y7viv53kig2mdcsvwkyyd.onion/)| 🇫🇮 FI | |
A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare.com). The checkmark will not be listed for a site that uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website.
@ -267,8 +269,8 @@ Assign a default value for each setting by passing environment variables to Libr
| `FRONT_PAGE` | `["default", "popular", "all"]` | `default` |
| `LAYOUT` | `["card", "clean", "compact"]` | `card` |
| `WIDE` | `["on", "off"]` | `off` |
| `COMMENT_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` |
| `POST_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` |
| `POST_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` |
| `COMMENT_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` |
| `SHOW_NSFW` | `["on", "off"]` | `off` |
| `USE_HLS` | `["on", "off"]` | `off` |
| `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` |

View File

@ -1,2 +1,2 @@
ADDRESS=localhost
ADDRESS=0.0.0.0
PORT=12345

View File

@ -11,5 +11,27 @@ Environment=PORT=8080
EnvironmentFile=-/etc/libreddit.conf
ExecStart=/usr/bin/libreddit -a ${ADDRESS} -p ${PORT}
# Hardening
DeviceAllow=
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service ~@privileged ~@resources
UMask=0077
[Install]
WantedBy=default.target

View File

@ -1,6 +1,7 @@
use cached::proc_macro::cached;
use futures_lite::{future::Boxed, FutureExt};
use hyper::{body::Buf, client, Body, Request, Response, Uri};
use percent_encoding::{percent_encode, CONTROLS};
use serde_json::Value;
use std::result::Result;
@ -90,7 +91,7 @@ fn request(url: String, quarantine: bool) -> Boxed<Result<Response<Body>, String
.headers()
.get("Location")
.map(|val| {
let new_url = val.to_str().unwrap_or_default();
let new_url = percent_encode(val.as_bytes(), CONTROLS).to_string();
format!("{}{}raw_json=1", new_url, if new_url.contains('?') { "&" } else { "?" })
})
.unwrap_or_default()

View File

@ -11,7 +11,7 @@ mod user;
mod utils;
// Import Crates
use clap::{Command, Arg};
use clap::{Arg, Command};
use futures_lite::FutureExt;
use hyper::{header::HeaderValue, Body, Request, Response};
@ -19,7 +19,7 @@ use hyper::{header::HeaderValue, Body, Request, Response};
mod client;
use client::proxy;
use server::RequestExt;
use utils::{error, redirect};
use utils::{error, redirect, ThemeAssets};
mod server;
@ -85,6 +85,23 @@ async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Respons
Ok(res)
}
async fn style() -> Result<Response<Body>, String> {
let mut res = include_str!("../static/style.css").to_string();
for file in ThemeAssets::iter() {
res.push('\n');
let theme = ThemeAssets::get(file.as_ref()).unwrap();
res.push_str(std::str::from_utf8(theme.data.as_ref()).unwrap());
}
Ok(
Response::builder()
.status(200)
.header("content-type", "text/css")
.header("Cache-Control", "public, max-age=1209600, s-maxage=86400")
.body(res.to_string().into())
.unwrap_or_default(),
)
}
#[tokio::main]
async fn main() {
let matches = Command::new("Libreddit")
@ -152,7 +169,7 @@ async fn main() {
}
// Read static files
app.at("/style.css").get(|_| resource(include_str!("../static/style.css"), "text/css", false).boxed());
app.at("/style.css").get(|_| style().boxed());
app
.at("/manifest.json")
.get(|_| resource(include_str!("../static/manifest.json"), "application/json", false).boxed());

View File

@ -1,6 +1,5 @@
// CRATES
use crate::client::json;
use crate::esc;
use crate::server::RequestExt;
use crate::subreddit::{can_access_quarantine, quarantine};
use crate::utils::{
@ -13,7 +12,7 @@ use std::collections::HashSet;
// STRUCTS
#[derive(Template)]
#[template(path = "post.html", escape = "none")]
#[template(path = "post.html")]
struct PostTemplate {
comments: Vec<Comment>,
post: Post,
@ -100,7 +99,10 @@ async fn parse_post(json: &serde_json::Value) -> Post {
let permalink = val(post, "permalink");
let body = if val(post, "removed_by_category") == "moderator" {
format!("<div class=\"md\"><p>[removed] — <a href=\"https://www.reveddit.com{}\">view removed post</a></p></div>", permalink)
format!(
"<div class=\"md\"><p>[removed] — <a href=\"https://www.reveddit.com{}\">view removed post</a></p></div>",
permalink
)
} else {
rewrite_urls(&val(post, "selftext_html"))
};
@ -108,7 +110,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
// Build a post using data parsed from Reddit post API
Post {
id: val(post, "id"),
title: esc!(post, "title"),
title: val(post, "title"),
community: val(post, "subreddit"),
body,
author: Author {
@ -119,7 +121,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
post["data"]["author_flair_richtext"].as_array(),
post["data"]["author_flair_text"].as_str(),
),
text: esc!(post, "link_flair_text"),
text: val(post, "link_flair_text"),
background_color: val(post, "author_flair_background_color"),
foreground_color: val(post, "author_flair_text_color"),
},
@ -143,7 +145,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
post["data"]["link_flair_richtext"].as_array(),
post["data"]["link_flair_text"].as_str(),
),
text: esc!(post, "link_flair_text"),
text: val(post, "link_flair_text"),
background_color: val(post, "link_flair_background_color"),
foreground_color: if val(post, "link_flair_text_color") == "dark" {
"black".to_string()
@ -199,7 +201,10 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str,
let highlighted = id == highlighted_comment;
let body = if val(&comment, "author") == "[deleted]" && val(&comment, "body") == "[removed]" {
format!("<div class=\"md\"><p>[removed] — <a href=\"https://www.reveddit.com{}{}\">view removed comment</a></p></div>", post_link, id)
format!(
"<div class=\"md\"><p>[removed] — <a href=\"https://www.reveddit.com{}{}\">view removed comment</a></p></div>",
post_link, id
)
} else {
rewrite_urls(&val(&comment, "body_html"))
};
@ -212,7 +217,7 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str,
data["author_flair_richtext"].as_array(),
data["author_flair_text"].as_str(),
),
text: esc!(&comment, "link_flair_text"),
text: val(&comment, "link_flair_text"),
background_color: val(&comment, "author_flair_background_color"),
foreground_color: val(&comment, "author_flair_text_color"),
},

View File

@ -29,7 +29,7 @@ struct Subreddit {
}
#[derive(Template)]
#[template(path = "search.html", escape = "none")]
#[template(path = "search.html")]
struct SearchTemplate {
posts: Vec<Post>,
subreddits: Vec<Subreddit>,

View File

@ -158,8 +158,8 @@ impl Server {
Ok::<_, String>(service_fn(move |req: Request<Body>| {
let headers = default_headers.clone();
// Remove double slashes
let mut path = req.uri().path().replace("//", "/");
// Remove double slashes and decode encoded slashes
let mut path = req.uri().path().replace("//", "/").replace("%2F","/");
// Remove trailing slashes
if path != "/" && path.ends_with('/') {

View File

@ -1,5 +1,4 @@
// CRATES
use crate::esc;
use crate::utils::{
catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
};
@ -11,7 +10,7 @@ use time::{Duration, OffsetDateTime};
// STRUCTS
#[derive(Template)]
#[template(path = "subreddit.html", escape = "none")]
#[template(path = "subreddit.html")]
struct SubredditTemplate {
sub: Subreddit,
posts: Vec<Post>,
@ -30,7 +29,7 @@ struct SubredditTemplate {
}
#[derive(Template)]
#[template(path = "wiki.html", escape = "none")]
#[template(path = "wiki.html")]
struct WikiTemplate {
sub: String,
wiki: String,
@ -40,7 +39,7 @@ struct WikiTemplate {
}
#[derive(Template)]
#[template(path = "wall.html", escape = "none")]
#[template(path = "wall.html")]
struct WallTemplate {
title: String,
sub: String,
@ -412,9 +411,9 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, String> {
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
Ok(Subreddit {
name: esc!(&res, "display_name"),
title: esc!(&res, "title"),
description: esc!(&res, "public_description"),
name: val(&res, "display_name"),
title: val(&res, "title"),
description: val(&res, "public_description"),
info: rewrite_urls(&val(&res, "description_html")),
// moderators: moderators_list(sub, quarantined).await.unwrap_or_default(),
icon: format_url(&icon),

View File

@ -1,6 +1,5 @@
// CRATES
use crate::client::json;
use crate::esc;
use crate::server::RequestExt;
use crate::utils::{error, filter_posts, format_url, get_filters, param, setting, template, Post, Preferences, User};
use askama::Template;
@ -9,7 +8,7 @@ use time::{macros::format_description, OffsetDateTime};
// STRUCTS
#[derive(Template)]
#[template(path = "user.html", escape = "none")]
#[template(path = "user.html")]
struct UserTemplate {
user: User,
posts: Vec<Post>,
@ -106,11 +105,11 @@ async fn user(name: &str) -> Result<User, String> {
// Parse the JSON output into a User struct
User {
name: res["data"]["name"].as_str().unwrap_or(name).to_owned(),
title: esc!(about("title")),
title: about("title"),
icon: format_url(&about("icon_img")),
karma: res["data"]["total_karma"].as_i64().unwrap_or(0),
created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(),
banner: esc!(about("banner_img")),
banner: about("banner_img"),
description: about("public_description"),
}
})

View File

@ -1,15 +1,16 @@
//
// CRATES
//
use crate::{client::json, esc, server::RequestExt};
use crate::{client::json, server::RequestExt};
use askama::Template;
use cookie::Cookie;
use hyper::{Body, Request, Response};
use regex::Regex;
use rust_embed::RustEmbed;
use serde_json::Value;
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use time::{Duration, OffsetDateTime, macros::format_description};
use time::{macros::format_description, Duration, OffsetDateTime};
use url::Url;
// Post flair with content, background color and foreground color
@ -41,7 +42,7 @@ impl FlairPart {
Self {
flair_part_type: value("e").to_string(),
value: match value("e") {
"text" => esc!(value("t")),
"text" => value("t").to_string(),
"emoji" => format_url(value("u")),
_ => String::new(),
},
@ -54,7 +55,7 @@ impl FlairPart {
"text" => match text_flair {
Some(text) => vec![Self {
flair_part_type: "text".to_string(),
value: esc!(text),
value: text.to_string(),
}],
None => Vec::new(),
},
@ -217,24 +218,19 @@ pub struct Post {
impl Post {
// Fetch posts of a user or subreddit and return a vector of posts and the "after" value
pub async fn fetch(path: &str, quarantine: bool) -> Result<(Vec<Self>, String), String> {
let res;
let post_list;
// Send a request to the url
match json(path.to_string(), quarantine).await {
let res = match json(path.to_string(), quarantine).await {
// If success, receive JSON in response
Ok(response) => {
res = response;
}
Ok(response) => response,
// If the Reddit API returns an error, exit this function
Err(msg) => return Err(msg),
}
};
// Fetch the list of posts from the JSON response
match res["data"]["children"].as_array() {
Some(list) => post_list = list,
let post_list = match res["data"]["children"].as_array() {
Some(list) => list,
None => return Err("No posts found".to_string()),
}
};
let mut posts: Vec<Self> = Vec::new();
@ -245,7 +241,7 @@ impl Post {
let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default());
let score = data["score"].as_i64().unwrap_or_default();
let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0;
let title = esc!(post, "title");
let title = val(post, "title");
// Determine the type of media along with the media URL
let (post_type, media, gallery) = Media::parse(data).await;
@ -270,7 +266,7 @@ impl Post {
data["author_flair_richtext"].as_array(),
data["author_flair_text"].as_str(),
),
text: esc!(post, "link_flair_text"),
text: val(post, "link_flair_text"),
background_color: val(post, "author_flair_background_color"),
foreground_color: val(post, "author_flair_text_color"),
},
@ -298,7 +294,7 @@ impl Post {
data["link_flair_richtext"].as_array(),
data["link_flair_text"].as_str(),
),
text: esc!(post, "link_flair_text"),
text: val(post, "link_flair_text"),
background_color: val(post, "link_flair_background_color"),
foreground_color: if val(post, "link_flair_text_color") == "dark" {
"black".to_string()
@ -324,7 +320,7 @@ impl Post {
}
#[derive(Template)]
#[template(path = "comment.html", escape = "none")]
#[template(path = "comment.html")]
// Comment with content, post, score and data/time that it was posted
pub struct Comment {
pub id: String,
@ -400,7 +396,7 @@ impl Awards {
}
#[derive(Template)]
#[template(path = "error.html", escape = "none")]
#[template(path = "error.html")]
pub struct ErrorTemplate {
pub msg: String,
pub prefs: Preferences,
@ -445,6 +441,7 @@ pub struct Params {
#[derive(Default)]
pub struct Preferences {
pub available_themes: Vec<String>,
pub theme: String,
pub front_page: String,
pub layout: String,
@ -459,10 +456,23 @@ pub struct Preferences {
pub filters: Vec<String>,
}
#[derive(RustEmbed)]
#[folder = "static/themes/"]
#[include = "*.css"]
pub struct ThemeAssets;
impl Preferences {
// Build preferences from cookies
pub fn new(req: Request<Body>) -> Self {
// Read available theme names from embedded css files.
// Always make the default "system" theme available.
let mut themes = vec!["system".to_string()];
for file in ThemeAssets::iter() {
let chunks: Vec<&str> = file.as_ref().split(".css").collect();
themes.push(chunks[0].to_owned())
}
Self {
available_themes: themes,
theme: setting(&req, "theme"),
front_page: setting(&req, "front_page"),
layout: setting(&req, "layout"),
@ -607,12 +617,11 @@ pub fn format_url(url: &str) -> String {
// Rewrite Reddit links to Libreddit in body of text
pub fn rewrite_urls(input_text: &str) -> String {
let text1 =
Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|)(reddit\.com|redd\.it)/"#)
.map_or(String::new(), |re| re.replace_all(input_text, r#"href="/"#).to_string())
// Remove (html-encoded) "\" from URLs.
.replace("%5C", "").replace(r"\", "");
let text1 = Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|)(reddit\.com|redd\.it)/"#)
.map_or(String::new(), |re| re.replace_all(input_text, r#"href="/"#).to_string())
// Remove (html-encoded) "\" from URLs.
.replace("%5C", "")
.replace('\\', "");
// Rewrite external media previews to Libreddit
Regex::new(r"https://external-preview\.redd\.it(.*)[^?]").map_or(String::new(), |re| {
@ -656,7 +665,12 @@ pub fn time(created: f64) -> (String, String) {
format!("{}m ago", time_delta.whole_minutes())
};
(rel_time, time.format(format_description!("[month repr:short] [day] [year], [hour]:[minute]:[second] UTC")).unwrap_or_default())
(
rel_time,
time
.format(format_description!("[month repr:short] [day] [year], [hour]:[minute]:[second] UTC"))
.unwrap_or_default(),
)
}
// val() function used to parse JSON from Reddit APIs
@ -664,17 +678,6 @@ pub fn val(j: &Value, k: &str) -> String {
j["data"][k].as_str().unwrap_or_default().to_string()
}
// Escape < and > to accurately render HTML
#[macro_export]
macro_rules! esc {
($f:expr) => {
$f.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;")
};
($j:expr, $k:expr) => {
$j["data"][$k].as_str().unwrap_or_default().to_string().replace('<', "&lt;").replace('>', "&gt;")
};
}
//
// NETWORKING
//
@ -727,7 +730,8 @@ mod tests {
#[test]
fn rewrite_urls_removes_backslashes() {
let comment_body_html = r#"<a href=\"https://www.reddit.com/r/linux%5C_gaming/comments/x/just%5C_a%5C_test%5C/\">https://www.reddit.com/r/linux\\_gaming/comments/x/just\\_a\\_test/</a>"#;
let comment_body_html =
r#"<a href=\"https://www.reddit.com/r/linux%5C_gaming/comments/x/just%5C_a%5C_test%5C/\">https://www.reddit.com/r/linux\\_gaming/comments/x/just\\_a\\_test/</a>"#;
assert_eq!(
rewrite_urls(comment_body_html),
r#"<a href="https://www.reddit.com/r/linux_gaming/comments/x/just_a_test/">https://www.reddit.com/r/linux_gaming/comments/x/just_a_test/</a>"#

View File

@ -45,124 +45,7 @@
}
}
/* Light theme setting */
.light {
--accent: #009a9a;
--green: #00a229;
--text: black;
--foreground: #f5f5f5;
--background: #ddd;
--outside: #ececec;
--post: #eee;
--panel-border: 1px solid #ccc;
--highlighted: white;
--visited: #555;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Black theme setting */
.black {
--accent: #009a9a;
--green: #00a229;
--text: white;
--foreground: #0f0f0f;
--background: black;
--outside: black;
--post: black;
--panel-border: 2px solid #0f0f0f;
--highlighted: #0f0f0f;
--visited: #aaa;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Dracula theme setting */
.dracula {
--accent: #bd93f9;
--green: #50fa7b;
--text: #f8f8f2;
--foreground: #3d4051;
--background: #282a36;
--outside: #393c4d;
--post: #333544;
--panel-border: 2px solid #44475a;
--highlighted: #4e5267;
--visited: #969692;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Nord theme setting */
.nord {
--accent: #8fbcbb;
--green: #a3be8c;
--text: #eceff4;
--foreground: #3b4252;
--background: #2e3440;
--outside: #434c5e;
--post: #434c5e;
--panel-border: 2px solid #4c566a;
--highlighted: #3b4252;
--visited: #a3a5aa;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Laserwave theme setting */
.laserwave {
--accent: #eb64b9;
--green: #74dfc4;
--text: #e0dfe1;
--foreground: #302a36;
--background: #27212e;
--outside: #3e3647;
--post: #3e3647;
--panel-border: 2px solid #2f2738;
--highlighted: #302a36;
--visited: #91889b;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* Violet theme setting */
.violet {
--accent: #7c71dd;
--green: #5cff85;
--text: white;
--foreground: #1F2347;
--background: #12152b;
--outside: #181c3a;
--post: #181c3a;
--panel-border: 1px solid #1F2347;
--highlighted: #1F2347;
--visited: #aaa;
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}
/* Gold theme setting */
.gold {
--accent: #f2aa4c;
--green: #5cff85;
--text: white;
--foreground: #234;
--background: #101820;
--outside: #1b2936;
--post: #1b2936;
--panel-border: 0px solid black;
--highlighted: #234;
--visited: #aaa;
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}
/* Rosebox theme setting */
.rosebox {
--accent: #a57562;
--green: #a3be8c;
--text: white;
--foreground: #222;
--background: #262626;
--outside: #222;
--post: #222;
--panel-border: 1px solid #222;
--highlighted: #262626;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* Other themes are located in the "themes" folder */
/* General */
@ -894,6 +777,7 @@ a.search_subreddit:hover {
padding: 5px 15px 5px 12px;
grid-area: post_body;
width: calc(100% - 30px);
overflow-wrap: anywhere;
}
/* Used only for text post preview */

14
static/themes/black.css Normal file
View File

@ -0,0 +1,14 @@
/* Black theme setting */
.black {
--accent: #009a9a;
--green: #00a229;
--text: white;
--foreground: #0f0f0f;
--background: black;
--outside: black;
--post: black;
--panel-border: 2px solid #0f0f0f;
--highlighted: #0f0f0f;
--visited: #aaa;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

14
static/themes/dark.css Normal file
View File

@ -0,0 +1,14 @@
/* Dark theme setting */
.dark{
--accent: aqua;
--green: #5cff85;
--text: white;
--foreground: #222;
--background: #0f0f0f;
--outside: #1f1f1f;
--post: #161616;
--panel-border: 1px solid #333;
--highlighted: #333;
--visited: #aaa;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

14
static/themes/dracula.css Normal file
View File

@ -0,0 +1,14 @@
/* Dracula theme setting */
.dracula {
--accent: #bd93f9;
--green: #50fa7b;
--text: #f8f8f2;
--foreground: #3d4051;
--background: #282a36;
--outside: #393c4d;
--post: #333544;
--panel-border: 2px solid #44475a;
--highlighted: #4e5267;
--visited: #969692;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

14
static/themes/gold.css Normal file
View File

@ -0,0 +1,14 @@
/* Gold theme setting */
.gold {
--accent: #f2aa4c;
--green: #5cff85;
--text: white;
--foreground: #234;
--background: #101820;
--outside: #1b2936;
--post: #1b2936;
--panel-border: 0px solid black;
--highlighted: #234;
--visited: #aaa;
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}

View File

@ -0,0 +1,14 @@
/* Laserwave theme setting */
.laserwave {
--accent: #eb64b9;
--green: #74dfc4;
--text: #e0dfe1;
--foreground: #302a36;
--background: #27212e;
--outside: #3e3647;
--post: #3e3647;
--panel-border: 2px solid #2f2738;
--highlighted: #302a36;
--visited: #91889b;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

14
static/themes/light.css Normal file
View File

@ -0,0 +1,14 @@
/* Light theme setting */
.light {
--accent: #009a9a;
--green: #00a229;
--text: black;
--foreground: #f5f5f5;
--background: #ddd;
--outside: #ececec;
--post: #eee;
--panel-border: 1px solid #ccc;
--highlighted: white;
--visited: #555;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

14
static/themes/nord.css Normal file
View File

@ -0,0 +1,14 @@
/* Nord theme setting */
.nord {
--accent: #8fbcbb;
--green: #a3be8c;
--text: #eceff4;
--foreground: #3b4252;
--background: #2e3440;
--outside: #434c5e;
--post: #434c5e;
--panel-border: 2px solid #4c566a;
--highlighted: #3b4252;
--visited: #a3a5aa;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

13
static/themes/rosebox.css Normal file
View File

@ -0,0 +1,13 @@
/* Rosebox theme setting */
.rosebox {
--accent: #a57562;
--green: #a3be8c;
--text: white;
--foreground: #222;
--background: #262626;
--outside: #222;
--post: #222;
--panel-border: 1px solid #222;
--highlighted: #262626;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

14
static/themes/violet.css Normal file
View File

@ -0,0 +1,14 @@
/* Violet theme setting */
.violet {
--accent: #7c71dd;
--green: #5cff85;
--text: white;
--foreground: #1F2347;
--background: #12152b;
--outside: #181c3a;
--post: #181c3a;
--panel-border: 1px solid #1F2347;
--highlighted: #1F2347;
--visited: #aaa;
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}

View File

@ -32,9 +32,9 @@
{% if is_filtered %}
<div class="comment_body_filtered {% if highlighted %}highlighted{% endif %}">(Filtered content)</div>
{% else %}
<div class="comment_body {% if highlighted %}highlighted{% endif %}">{{ body }}</div>
<div class="comment_body {% if highlighted %}highlighted{% endif %}">{{ body|safe }}</div>
{% endif %}
<blockquote class="replies">{% for c in replies -%}{{ c.render().unwrap() }}{%- endfor %}
<blockquote class="replies">{% for c in replies -%}{{ c.render().unwrap()|safe }}{%- endfor %}
</blockquote>
</details>
</div>

View File

@ -55,7 +55,7 @@
</span>
{% endif %}
</p>
<p class="post_title">
<h1 class="post_title">
{{ post.title }}
{% if post.flair.flair_parts.len() > 0 %}
<a href="/r/{{ post.community }}/search?q=flair_name%3A%22{{ post.flair.text }}%22&restrict_sr=on"
@ -63,7 +63,7 @@
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call utils::render_flair(post.flair.flair_parts) %}</a>
{% endif %}
{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
</p>
</h1>
<!-- POST MEDIA -->
<!-- post_type: {{ post.post_type }} -->
@ -110,7 +110,7 @@
{% endif %}
<!-- POST BODY -->
<div class="post_body">{{ post.body }}</div>
<div class="post_body">{{ post.body|safe }}</div>
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
<div class="post_footer">
<ul id="post_links">
@ -144,7 +144,7 @@
{% endif %}
{% endif %}
{{ c.render().unwrap() }}
{{ c.render().unwrap()|safe }}
</div>
{%- endfor %}

View File

@ -39,7 +39,7 @@
{% endif %}
{% for subreddit in subreddits %}
<a href="{{ subreddit.url }}" class="search_subreddit">
<div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div>
<div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon|safe }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div>
<div class="search_subreddit_right">
<p class="search_subreddit_header">
<span class="search_subreddit_name">r/{{ subreddit.name }}</span>
@ -97,13 +97,13 @@
{% if params.before != "" %}
<a href="?q={{ params.q }}&restrict_sr={{ params.restrict_sr }}
&sort={{ params.sort }}&t={{ params.t }}
&before={{ params.before }}">PREV</a>
&before={{ params.before }}" accesskey="P">PREV</a>
{% endif %}
{% if params.after != "" %}
<a href="?q={{ params.q }}&restrict_sr={{ params.restrict_sr }}
&sort={{ params.sort }}&t={{ params.t }}
&after={{ params.after }}">NEXT</a>
&after={{ params.after }}" accesskey="N">NEXT</a>
{% endif %}
</footer>
{% endif %}

View File

@ -15,7 +15,7 @@
<div id="theme">
<label for="theme">Theme:</label>
<select name="theme">
{% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold", "rosebox"], "system") %}
{% call utils::options(prefs.theme, prefs.available_themes, "system") %}
</select>
</div>
<legend>Interface</legend>
@ -110,7 +110,7 @@
<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>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 }}&comment_sort={{ prefs.comment_sort }}&show_nsfw={{ prefs.show_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 }}&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>
</div>
</div>

View File

@ -69,11 +69,11 @@
<footer>
{% if !ends.0.is_empty() %}
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&before={{ ends.0 }}">PREV</a>
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&before={{ ends.0 }}" accesskey="P">PREV</a>
{% endif %}
{% if !ends.1.is_empty() %}
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&after={{ ends.1 }}">NEXT</a>
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&after={{ ends.1 }}" accesskey="N">NEXT</a>
{% endif %}
</footer>
</div>
@ -93,7 +93,7 @@
{% endif %}
<div id="sub_meta">
<img loading="lazy" id="sub_icon" src="{{ sub.icon }}" alt="Icon for r/{{ sub.name }}">
<p id="sub_title">{{ sub.title }}</p>
<h1 id="sub_title">{{ sub.title }}</h1>
<p id="sub_name">r/{{ sub.name }}</p>
<p id="sub_description">{{ sub.description }}</p>
<div id="sub_details">
@ -131,7 +131,7 @@
<details class="panel" id="sidebar">
<summary id="sidebar_label">Sidebar</summary>
<div id="sidebar_contents">
{{ sub.info }}
{{ sub.info|safe }}
{# <hr>
<h2>Moderators</h2>
<br>

View File

@ -56,7 +56,7 @@
<a class="comment_link" href="{{ post.permalink }}">COMMENT</a>
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
</summary>
<p class="comment_body">{{ post.body }}</p>
<p class="comment_body">{{ post.body|safe }}</p>
</details>
</div>
{% endif %}
@ -70,11 +70,11 @@
<footer>
{% if ends.0 != "" %}
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&before={{ ends.0 }}">PREV</a>
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&before={{ ends.0 }}" accesskey="P">PREV</a>
{% endif %}
{% if ends.1 != "" %}
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&after={{ ends.1 }}">NEXT</a>
<a href="?sort={{ sort.0 }}&t={{ sort.1 }}&after={{ ends.1 }}" accesskey="N">NEXT</a>
{% endif %}
</footer>
</div>
@ -85,7 +85,7 @@
{% endif %}
<div class="panel" id="user">
<img loading="lazy" id="user_icon" src="{{ user.icon }}" alt="User icon">
<p id="user_title">{{ user.title }}</p>
<h1 id="user_title">{{ user.title }}</h1>
<p id="user_name">u/{{ user.name }}</p>
<div id="user_description">{{ user.description }}</div>
<div id="user_details">

View File

@ -38,7 +38,6 @@
{%- endmacro %}
{% macro sub_list(current) -%}
{% if prefs.subscriptions.len() > 0 %}
<details id="feeds">
<summary>Feeds</summary>
<div id="feed_list">
@ -46,13 +45,14 @@
<a href="/">Home</a>
<a href="/r/popular">Popular</a>
<a href="/r/all">All</a>
<p>REDDIT FEEDS</p>
{% for sub in prefs.subscriptions %}
<a href="/r/{{ sub }}" {% if sub == current %}class="selected"{% endif %}>{{ sub }}</a>
{% endfor %}
{% if prefs.subscriptions.len() > 0 %}
<p>REDDIT FEEDS</p>
{% for sub in prefs.subscriptions %}
<a href="/r/{{ sub }}" {% if sub == current %}class="selected"{% endif %}>{{ sub }}</a>
{% endfor %}
{% endif %}
</div>
</details>
{% endif %}
{%- endmacro %}
{% macro render_hls_notification(redirect_url) -%}
@ -83,7 +83,7 @@
{% endfor %}
{% endif %}
</p>
<p class="post_title">
<h2 class="post_title">
{% if post.flair.flair_parts.len() > 0 %}
<a href="/r/{{ post.community }}/search?q=flair_name%3A%22{{ post.flair.text }}%22&restrict_sr=on"
class="post_flair"
@ -91,7 +91,7 @@
dir="ltr">{% call render_flair(post.flair.flair_parts) %}</a>
{% endif %}
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
</p>
</h2>
<!-- POST MEDIA/THUMBNAIL -->
{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %}
<a href="{{ post.media.url }}" class="post_media_image {% if post.media.height / post.media.width < 2 %}short{% endif %}" >
@ -138,7 +138,7 @@
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
<div class="post_body post_preview">
{{ post.body }}
{{ post.body|safe }}
</div>
<div class="post_footer">
<a href="{{ post.permalink }}" class="post_comments" title="{{ post.comments.1 }} comments">{{ post.comments.0 }} comments</a>

View File

@ -22,8 +22,8 @@
<div>Wiki</div>
</div>
<div id="wiki">
{{ wiki }}
{{ wiki|safe }}
</div>
</div>
</main>
{% endblock %}
{% endblock %}