Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
e34329cfee | |||
97a0680bd0 | |||
c1560f4eba | |||
242ffab0da | |||
1211d781d0 | |||
9e4066658c | |||
560de4e91f | |||
bd1c890961 | |||
6f799b2617 | |||
38e176f59f | |||
8248eca95c | |||
ffc3bfe72d | |||
d713746407 | |||
21b45760eb | |||
e3fb93946a | |||
b6134a39d0 | |||
c844655c98 | |||
cac83493da | |||
b47cfd1ba5 | |||
28ca3589ed | |||
3cf787cf98 | |||
46e22cf74e | |||
5c2e134924 | |||
c6244585fa | |||
9f1ba274eb | |||
93ed1c6f0c | |||
6ce82c36fb | |||
2974d92e30 | |||
34dfcb2512 | |||
6b42e97bda | |||
49bfe4d27c | |||
c8965ae51b | |||
0b64a52a63 | |||
a18db1e2b7 |
@ -1,4 +1,4 @@
|
|||||||
name: Docker Multi-Architecture Build
|
name: Docker ARM Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -30,7 +30,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile.arm
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: spikecodes/libreddit:latest
|
tags: spikecodes/libreddit:arm
|
4
.github/workflows/rust.yml
vendored
4
.github/workflows/rust.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=version::$(cargo metadata --format-version 1 --no-deps | jq .packages[0].version -r | sed 's/^/v/')"
|
echo "::set-output name=version::$(cargo metadata --format-version 1 --no-deps | jq .packages[0].version -r | sed 's/^/v/')"
|
||||||
echo "::set-output name=tag::${GITHUB_REF#refs/*/}"
|
echo "::set-output name=tag::$(git describe --tags)"
|
||||||
|
|
||||||
- name: Calculate SHA512 checksum
|
- name: Calculate SHA512 checksum
|
||||||
run: sha512sum target/release/libreddit > libreddit.sha512
|
run: sha512sum target/release/libreddit > libreddit.sha512
|
||||||
@ -47,7 +47,7 @@ jobs:
|
|||||||
target/release/libreddit
|
target/release/libreddit
|
||||||
libreddit.sha512
|
libreddit.sha512
|
||||||
body: |
|
body: |
|
||||||
- CHANGES
|
- ${{ github.event.head_commit.message }} ${{ github.sha }}
|
||||||
|
|
||||||
See full list of changes [here](https://github.com/spikecodes/libreddit/compare/${{ steps.version.outputs.tag }}...${{ steps.version.outputs.version }}).
|
See full list of changes [here](https://github.com/spikecodes/libreddit/compare/${{ steps.version.outputs.tag }}...${{ steps.version.outputs.version }}).
|
||||||
env:
|
env:
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
|
1462
Cargo.lock
generated
Normal file
1462
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@ -3,7 +3,7 @@ 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.10.0"
|
version = "0.11.1"
|
||||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -12,14 +12,14 @@ askama = { version = "0.10.5", default-features = false }
|
|||||||
async-recursion = "0.3.2"
|
async-recursion = "0.3.2"
|
||||||
cached = "0.23.0"
|
cached = "0.23.0"
|
||||||
clap = { version = "2.33.3", default-features = false }
|
clap = { version = "2.33.3", default-features = false }
|
||||||
regex = "1.4.5"
|
regex = "1.5.4"
|
||||||
serde = { version = "1.0.125", features = ["derive"] }
|
serde = { version = "1.0.125", features = ["derive"] }
|
||||||
cookie = "0.15.0"
|
cookie = "0.15.0"
|
||||||
futures-lite = "1.11.3"
|
futures-lite = "1.11.3"
|
||||||
hyper = { version = "0.14.5", features = ["full"] }
|
hyper = { version = "0.14.7", features = ["full"] }
|
||||||
hyper-rustls = "0.22.1"
|
hyper-rustls = "0.22.1"
|
||||||
route-recognizer = "0.3.0"
|
route-recognizer = "0.3.0"
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.64"
|
||||||
tokio = { version = "1.4.0", features = ["full"] }
|
tokio = { version = "1.5.0", features = ["full"] }
|
||||||
time = "0.2.26"
|
time = "0.2.26"
|
||||||
url = "2.2.1"
|
url = "2.2.2"
|
||||||
|
39
Dockerfile
39
Dockerfile
@ -1,15 +1,36 @@
|
|||||||
FROM rust:alpine as builder
|
####################################################################################################
|
||||||
WORKDIR /usr/src/libreddit
|
## Builder
|
||||||
COPY . .
|
####################################################################################################
|
||||||
RUN apk add --no-cache g++
|
FROM rust:alpine AS builder
|
||||||
RUN cargo install --path .
|
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev
|
||||||
|
|
||||||
|
WORKDIR /libreddit
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cargo build --target x86_64-unknown-linux-musl --release
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
## Final image
|
||||||
|
####################################################################################################
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk add --no-cache curl
|
|
||||||
COPY --from=builder /usr/local/cargo/bin/libreddit /usr/local/bin/libreddit
|
# Import ca-certificates from builder
|
||||||
RUN adduser --system --home /nonexistent --no-create-home --disabled-password libreddit
|
COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
|
||||||
|
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
||||||
|
# Copy our build
|
||||||
|
COPY --from=builder /libreddit/target/x86_64-unknown-linux-musl/release/libreddit /usr/local/bin/libreddit
|
||||||
|
|
||||||
|
# Use an unprivileged user.
|
||||||
|
RUN adduser --home /nonexistent --no-create-home --disabled-password libreddit
|
||||||
USER libreddit
|
USER libreddit
|
||||||
|
|
||||||
|
# Tell Docker to expose port 8080
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost:8080/settings || exit 1
|
|
||||||
|
# Run a healthcheck every minute to make sure Libreddit is functional
|
||||||
|
HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider --q http://localhost:8080/settings || exit 1
|
||||||
|
|
||||||
CMD ["libreddit"]
|
CMD ["libreddit"]
|
36
Dockerfile.arm
Normal file
36
Dockerfile.arm
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
####################################################################################################
|
||||||
|
## Builder
|
||||||
|
####################################################################################################
|
||||||
|
FROM rust:alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache g++
|
||||||
|
|
||||||
|
WORKDIR /usr/src/libreddit
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cargo install --path .
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
## Final image
|
||||||
|
####################################################################################################
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Import ca-certificates from builder
|
||||||
|
COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
|
||||||
|
COPY --from=builder /etc/ssl/certs /etc/ssl/certs
|
||||||
|
|
||||||
|
# Copy our build
|
||||||
|
COPY --from=builder /usr/local/cargo/bin/libreddit /usr/local/bin/libreddit
|
||||||
|
|
||||||
|
# Use an unprivileged user.
|
||||||
|
RUN adduser --home /nonexistent --no-create-home --disabled-password libreddit
|
||||||
|
USER libreddit
|
||||||
|
|
||||||
|
# Tell Docker to expose port 8080
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Run a healthcheck every minute to make sure Libreddit is functional
|
||||||
|
HEALTHCHECK --interval=1m --timeout=3s CMD wget --spider --q http://localhost:8080/settings || exit 1
|
||||||
|
|
||||||
|
CMD ["libreddit"]
|
11
README.md
11
README.md
@ -31,16 +31,19 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new)
|
|||||||
| [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | |
|
| [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | |
|
||||||
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | |
|
| [libreddit.dothq.co](https://libreddit.dothq.co) | 🇺🇸 US | |
|
||||||
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | ✅ |
|
| [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | ✅ |
|
||||||
| [libreddit.himiko.cloud](https://libreddit.himiko.cloud) | 🇫🇮 FI | |
|
|
||||||
| [libreddit.bcow.xyz](https://libreddit.bcow.xyz) | 🇺🇸 US | |
|
| [libreddit.bcow.xyz](https://libreddit.bcow.xyz) | 🇺🇸 US | |
|
||||||
| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | |
|
| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | |
|
||||||
| [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | |
|
| [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | |
|
||||||
| [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | |
|
| [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.silkky.cloud](https://libreddit.silkky.cloud) | 🇫🇮 FI | |
|
||||||
|
| [libreddit.database.red](https://libreddit.database.red) | 🇺🇸 US | ✅ |
|
||||||
|
| [libreddit.exonip.de](https://libreddit.exonip.de) | 🇩🇪 DE | |
|
||||||
|
| [libreddit.domain.glass](https://libreddit.domain.glass) | 🇺🇸 US | ✅ |
|
||||||
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
|
| [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | |
|
||||||
| [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | |
|
| [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | |
|
||||||
| [libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion](http://libreddit.himiko7xl2skojc6odi7hykl626gt4qki3vxdbv33u2u3af76d6k32ad.onion) | 🇫🇮 FI | |
|
|
||||||
| [dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion](http://dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion/) | 🇺🇸 US | |
|
| [dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion](http://dflv6yjt7il3n3tggf4qhcmkzbti2ppytqx3o7pjrzwgntutpewscyid.onion/) | 🇺🇸 US | |
|
||||||
|
| [kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion](http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion/) | 🇳🇱 NL | |
|
||||||
|
|
||||||
A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare). The checkmark will not be listed for a site which uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website.
|
A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare). The checkmark will not be listed for a site which uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website.
|
||||||
|
|
||||||
@ -68,7 +71,7 @@ Teddit is another awesome open source project designed to provide an alternative
|
|||||||
|
|
||||||
If you are looking to compare, the biggest differences I have noticed are:
|
If you are looking to compare, the biggest differences I have noticed are:
|
||||||
- Libreddit is themed around Reddit's redesign whereas Teddit appears to stick much closer to Reddit's old design. This may suit some users better as design is always subjective.
|
- Libreddit is themed around Reddit's redesign whereas Teddit appears to stick much closer to Reddit's old design. This may suit some users better as design is always subjective.
|
||||||
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Actix Web](https://actix.rs), which was [benchmarked as the fastest web server for single queries](https://www.techempower.com/benchmarks/#hw=ph&test=db).
|
- Libreddit is written in [Rust](https://www.rust-lang.org) for speed and memory safety. It uses [Hyper](https://hyper.rs), a speedy and lightweight HTTP server/client implementation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -157,6 +160,8 @@ docker pull spikecodes/libreddit
|
|||||||
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
|
docker run -d --name libreddit -p 80:8080 spikecodes/libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To deploy on `arm64` platforms, simply replace `spikecodes/libreddit` in the commands above with `spikecodes/libreddit:arm`.
|
||||||
|
|
||||||
## 3) AUR
|
## 3) AUR
|
||||||
|
|
||||||
For ArchLinux users, Libreddit is available from the AUR as [`libreddit-git`](https://aur.archlinux.org/packages/libreddit-git).
|
For ArchLinux users, Libreddit is available from the AUR as [`libreddit-git`](https://aur.archlinux.org/packages/libreddit-git).
|
||||||
|
@ -7,7 +7,7 @@ use std::{result::Result, str::FromStr};
|
|||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
|
|
||||||
pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, String> {
|
pub async fn proxy(req: Request<Body>, format: &str) -> Result<Response<Body>, String> {
|
||||||
let mut url = format.to_string();
|
let mut url = format!("{}?{}", format, req.uri().query().unwrap_or_default());
|
||||||
|
|
||||||
for (name, value) in req.params().iter() {
|
for (name, value) in req.params().iter() {
|
||||||
url = url.replace(&format!("{{{}}}", name), value);
|
url = url.replace(&format!("{{{}}}", name), value);
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -164,7 +164,7 @@ async fn main() {
|
|||||||
app.at("/img/:id").get(|r| proxy(r, "https://i.redd.it/{id}").boxed());
|
app.at("/img/:id").get(|r| proxy(r, "https://i.redd.it/{id}").boxed());
|
||||||
app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed());
|
app.at("/thumb/:point/:id").get(|r| proxy(r, "https://{point}.thumbs.redditmedia.com/{id}").boxed());
|
||||||
app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed());
|
app.at("/emoji/:id/:name").get(|r| proxy(r, "https://emoji.redditmedia.com/{id}/{name}").boxed());
|
||||||
app.at("/preview/:loc/:id/:query").get(|r| proxy(r, "https://{loc}view.redd.it/{id}?{query}").boxed());
|
app.at("/preview/:loc/:id").get(|r| proxy(r, "https://{loc}view.redd.it/{id}").boxed());
|
||||||
app.at("/style/*path").get(|r| proxy(r, "https://styles.redditmedia.com/{path}").boxed());
|
app.at("/style/*path").get(|r| proxy(r, "https://styles.redditmedia.com/{path}").boxed());
|
||||||
app.at("/static/*path").get(|r| proxy(r, "https://www.redditstatic.com/{path}").boxed());
|
app.at("/static/*path").get(|r| proxy(r, "https://www.redditstatic.com/{path}").boxed());
|
||||||
|
|
||||||
@ -205,10 +205,10 @@ async fn main() {
|
|||||||
.at("/r/:sub/w")
|
.at("/r/:sub/w")
|
||||||
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed());
|
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki", r.param("sub").unwrap_or_default()))) }.boxed());
|
||||||
app
|
app
|
||||||
.at("/r/:sub/w/:page")
|
.at("/r/:sub/w/*page")
|
||||||
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed());
|
.get(|r| async move { Ok(redirect(format!("/r/{}/wiki/{}", r.param("sub").unwrap_or_default(), r.param("wiki").unwrap_or_default()))) }.boxed());
|
||||||
app.at("/r/:sub/wiki").get(|r| subreddit::wiki(r).boxed());
|
app.at("/r/:sub/wiki").get(|r| subreddit::wiki(r).boxed());
|
||||||
app.at("/r/:sub/wiki/:page").get(|r| subreddit::wiki(r).boxed());
|
app.at("/r/:sub/wiki/*page").get(|r| subreddit::wiki(r).boxed());
|
||||||
|
|
||||||
app.at("/r/:sub/about/sidebar").get(|r| subreddit::sidebar(r).boxed());
|
app.at("/r/:sub/about/sidebar").get(|r| subreddit::sidebar(r).boxed());
|
||||||
|
|
||||||
@ -223,10 +223,10 @@ async fn main() {
|
|||||||
// View Reddit wiki
|
// View Reddit wiki
|
||||||
app.at("/w").get(|_| async { Ok(redirect("/wiki".to_string())) }.boxed());
|
app.at("/w").get(|_| async { Ok(redirect("/wiki".to_string())) }.boxed());
|
||||||
app
|
app
|
||||||
.at("/w/:page")
|
.at("/w/*page")
|
||||||
.get(|r| async move { Ok(redirect(format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed());
|
.get(|r| async move { Ok(redirect(format!("/wiki/{}", r.param("page").unwrap_or_default()))) }.boxed());
|
||||||
app.at("/wiki").get(|r| subreddit::wiki(r).boxed());
|
app.at("/wiki").get(|r| subreddit::wiki(r).boxed());
|
||||||
app.at("/wiki/:page").get(|r| subreddit::wiki(r).boxed());
|
app.at("/wiki/*page").get(|r| subreddit::wiki(r).boxed());
|
||||||
|
|
||||||
// Search all of Reddit
|
// Search all of Reddit
|
||||||
app.at("/search").get(|r| search::find(r).boxed());
|
app.at("/search").get(|r| search::find(r).boxed());
|
||||||
|
@ -99,8 +99,32 @@ pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String>
|
|||||||
|
|
||||||
let mut sub_list = Preferences::new(req).subscriptions;
|
let mut sub_list = Preferences::new(req).subscriptions;
|
||||||
|
|
||||||
|
// Retrieve list of posts for these subreddits to extract display names
|
||||||
|
let display = json(format!("/r/{}/hot.json?raw_json=1", sub)).await?;
|
||||||
|
let display_lookup: Vec<(String, &str)> = display["data"]["children"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|post| {
|
||||||
|
let display_name = post["data"]["subreddit"].as_str().unwrap();
|
||||||
|
(display_name.to_lowercase(), display_name)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Find each subreddit name (separated by '+') in sub parameter
|
// Find each subreddit name (separated by '+') in sub parameter
|
||||||
for part in sub.split('+') {
|
for part in sub.split('+') {
|
||||||
|
// Retrieve display name for the subreddit
|
||||||
|
let display;
|
||||||
|
let part = if let Some(&(_, display)) = display_lookup.iter().find(|x| x.0 == part.to_lowercase()) {
|
||||||
|
// This is already known, doesn't require seperate request
|
||||||
|
display
|
||||||
|
} else {
|
||||||
|
// This subreddit display name isn't known, retrieve it
|
||||||
|
let path: String = format!("/r/{}/about.json?raw_json=1", part);
|
||||||
|
display = json(path).await?;
|
||||||
|
display["data"]["display_name"].as_str().ok_or_else(|| "Failed to query subreddit name".to_string())?
|
||||||
|
};
|
||||||
|
|
||||||
// Modify sub list based on action
|
// Modify sub list based on action
|
||||||
if action.contains(&"subscribe".to_string()) && !sub_list.contains(&part.to_owned()) {
|
if action.contains(&"subscribe".to_string()) && !sub_list.contains(&part.to_owned()) {
|
||||||
// Add each sub name to the subscribed list
|
// Add each sub name to the subscribed list
|
||||||
@ -148,7 +172,7 @@ pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
match json(path).await {
|
match json(path).await {
|
||||||
Ok(response) => template(WikiTemplate {
|
Ok(response) => template(WikiTemplate {
|
||||||
sub,
|
sub,
|
||||||
wiki: rewrite_urls(response["data"]["content_html"].as_str().unwrap_or_default()),
|
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),
|
||||||
}),
|
}),
|
||||||
@ -166,8 +190,12 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
match json(path).await {
|
match json(path).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => template(WikiTemplate {
|
Ok(response) => template(WikiTemplate {
|
||||||
|
wiki: format!(
|
||||||
|
"{}<hr><h1>Moderators</h1><br><ul>{}</ul>",
|
||||||
|
rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
||||||
|
moderators(&sub).await?.join(""),
|
||||||
|
),
|
||||||
sub,
|
sub,
|
||||||
wiki: rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
|
||||||
page: "Sidebar".to_string(),
|
page: "Sidebar".to_string(),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(req),
|
||||||
}),
|
}),
|
||||||
@ -175,6 +203,36 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn moderators(sub: &str) -> Result<Vec<String>, String> {
|
||||||
|
// Retrieve and format the html for the moderators list
|
||||||
|
Ok(
|
||||||
|
moderators_list(sub)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|m| format!("<li><a style=\"color: var(--accent)\" href=\"/u/{name}\">{name}</a></li>", name = m))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn moderators_list(sub: &str) -> Result<Vec<String>, String> {
|
||||||
|
// Build the moderator list URL
|
||||||
|
let path: String = format!("/r/{}/about/moderators.json?raw_json=1", sub);
|
||||||
|
|
||||||
|
// Retrieve response
|
||||||
|
let response = json(path).await?["data"]["children"].clone();
|
||||||
|
Ok(if let Some(response) = response.as_array() {
|
||||||
|
// Traverse json tree and format into list of strings
|
||||||
|
response
|
||||||
|
.iter()
|
||||||
|
.map(|m| m["name"].as_str().unwrap_or(""))
|
||||||
|
.filter(|m| !m.is_empty())
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// SUBREDDIT
|
// SUBREDDIT
|
||||||
async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
@ -197,6 +255,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
|||||||
title: esc!(&res, "title"),
|
title: esc!(&res, "title"),
|
||||||
description: esc!(&res, "public_description"),
|
description: esc!(&res, "public_description"),
|
||||||
info: rewrite_urls(&val(&res, "description_html").replace("\\", "")),
|
info: rewrite_urls(&val(&res, "description_html").replace("\\", "")),
|
||||||
|
moderators: moderators_list(sub).await?,
|
||||||
icon: format_url(&icon),
|
icon: format_url(&icon),
|
||||||
members: format_num(members),
|
members: format_num(members),
|
||||||
active: format_num(active),
|
active: format_num(active),
|
||||||
|
23
src/utils.rs
23
src/utils.rs
@ -341,6 +341,7 @@ pub struct Subreddit {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub info: String,
|
pub info: String,
|
||||||
|
pub moderators: Vec<String>,
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub members: (String, String),
|
pub members: (String, String),
|
||||||
pub active: (String, String),
|
pub active: (String, String),
|
||||||
@ -431,8 +432,8 @@ pub fn format_url(url: &str) -> String {
|
|||||||
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
|
"a.thumbs.redditmedia.com" => capture(r"https://a\.thumbs\.redditmedia\.com/(.*)", "/thumb/a/", 1),
|
||||||
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
|
"b.thumbs.redditmedia.com" => capture(r"https://b\.thumbs\.redditmedia\.com/(.*)", "/thumb/b/", 1),
|
||||||
"emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2),
|
"emoji.redditmedia.com" => capture(r"https://emoji\.redditmedia\.com/(.*)/(.*)", "/emoji/", 2),
|
||||||
"preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)\?(.*)", "/preview/pre/", 2),
|
"preview.redd.it" => capture(r"https://preview\.redd\.it/(.*)", "/preview/pre/", 1),
|
||||||
"external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)\?(.*)", "/preview/external-pre/", 2),
|
"external-preview.redd.it" => capture(r"https://external\-preview\.redd\.it/(.*)", "/preview/external-pre/", 1),
|
||||||
"styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1),
|
"styles.redditmedia.com" => capture(r"https://styles\.redditmedia\.com/(.*)", "/style/", 1),
|
||||||
"www.redditstatic.com" => capture(r"https://www\.redditstatic\.com/(.*)", "/static/", 1),
|
"www.redditstatic.com" => capture(r"https://www\.redditstatic\.com/(.*)", "/static/", 1),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
@ -444,9 +445,21 @@ pub fn format_url(url: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite Reddit links to Libreddit in body of text
|
// Rewrite Reddit links to Libreddit in body of text
|
||||||
pub fn rewrite_urls(text: &str) -> String {
|
pub fn rewrite_urls(input_text: &str) -> String {
|
||||||
match Regex::new(r#"href="(https|http|)://(www.|old.|np.|amp.|)(reddit).(com)/"#) {
|
let text1 = match Regex::new(r#"href="(https|http|)://(www.|old.|np.|amp.|)(reddit).(com)/"#) {
|
||||||
Ok(re) => re.replace_all(text, r#"href="/"#).to_string(),
|
Ok(re) => re.replace_all(input_text, r#"href="/"#).to_string(),
|
||||||
|
Err(_) => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rewrite external media previews to Libreddit
|
||||||
|
match Regex::new(r"https://external-preview\.redd\.it(.*)[^?]") {
|
||||||
|
Ok(re) => {
|
||||||
|
if re.is_match(&text1) {
|
||||||
|
re.replace_all(&text1, format_url(re.find(&text1).unwrap().as_str())).to_string()
|
||||||
|
} else {
|
||||||
|
text1
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(_) => String::new(),
|
Err(_) => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,49 @@
|
|||||||
--highlighted: #3b4252;
|
--highlighted: #3b4252;
|
||||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
--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;
|
||||||
|
--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;
|
||||||
|
--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;
|
||||||
|
--shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
/* General */
|
/* General */
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
@ -1129,6 +1172,8 @@ input[type="submit"] {
|
|||||||
|
|
||||||
.md table {
|
.md table {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md code {
|
.md code {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<div id="theme">
|
<div id="theme">
|
||||||
<label for="theme">Theme:</label>
|
<label for="theme">Theme:</label>
|
||||||
<select name="theme">
|
<select name="theme">
|
||||||
{% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord"], "system") %}
|
{% call utils::options(prefs.theme, ["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold"], "system") %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p>Interface</p>
|
<p>Interface</p>
|
||||||
|
@ -99,7 +99,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<details class="panel" id="sidebar">
|
<details class="panel" id="sidebar">
|
||||||
<summary id="sidebar_label">Sidebar</summary>
|
<summary id="sidebar_label">Sidebar</summary>
|
||||||
<div id="sidebar_contents">{{ sub.info }}</div>
|
<div id="sidebar_contents">
|
||||||
|
{{ sub.info }}
|
||||||
|
<hr>
|
||||||
|
<h2>Moderators</h2>
|
||||||
|
<br>
|
||||||
|
<ul>
|
||||||
|
{% for moderator in sub.moderators %}
|
||||||
|
<li><a style="color: var(--accent)" href="/u/{{ moderator }}">{{ moderator }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</aside>
|
</aside>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -74,7 +74,8 @@
|
|||||||
{% if post.flair.flair_parts.len() > 0 %}
|
{% 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"
|
<a href="/r/{{ post.community }}/search?q=flair_name%3A%22{{ post.flair.text }}%22&restrict_sr=on"
|
||||||
class="post_flair"
|
class="post_flair"
|
||||||
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};">{% call render_flair(post.flair.flair_parts) %}</a>
|
style="color:{{ post.flair.foreground_color }}; background:{{ post.flair.background_color }};"
|
||||||
|
dir="ltr">{% call render_flair(post.flair.flair_parts) %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
<a href="{{ post.permalink }}">{{ post.title }}</a>{% if post.flags.nsfw %} <small class="nsfw">NSFW</small>{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
Reference in New Issue
Block a user