Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
ba42fc066f | |||
2cd35fb3b6 | |||
b9af6f47f3 | |||
73732a2a44 | |||
43ed9756dc | |||
8bb247af3b | |||
ed05f5a092 | |||
4f09333cd7 | |||
31bf8c802e |
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -196,9 +196,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_fn"
|
name = "const_fn"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "402da840495de3f976eaefc3485b7f5eb5b0bf9761f9a47be27fe975b3b8c2ec"
|
checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
@ -609,7 +609,7 @@ checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libreddit"
|
name = "libreddit"
|
||||||
version = "0.13.0"
|
version = "0.14.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
|
@ -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.13.0"
|
version = "0.14.3"
|
||||||
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
26
README.md
26
README.md
@ -194,6 +194,32 @@ Once installed, deploy Libreddit to `0.0.0.0:8080` by running:
|
|||||||
libreddit
|
libreddit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Change Default 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.
|
||||||
|
|
||||||
|
| Name | Possible values | Default value |
|
||||||
|
|-----------------------|----------------------------------------------------------------------------------------|---------------|
|
||||||
|
| theme | ["system", "light", "dark", "black", "dracula", "nord", "laserwave", "violet", "gold"] | system |
|
||||||
|
| 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 |
|
||||||
|
| show_nsfw | ["on", "off"] | off |
|
||||||
|
| use_hls | ["on", "off"] | off |
|
||||||
|
| hide_hls_notification | ["on", "off"] | off |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LIBREDDIT_DEFAULT_SHOW_NSFW=on libreddit
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
LIBREDDIT_DEFAULT_WIDE=on LIBREDDIT_DEFAULT_THEME=dark libreddit -r
|
||||||
|
```
|
||||||
|
|
||||||
## Proxying using NGINX
|
## Proxying using NGINX
|
||||||
|
|
||||||
**NOTE** If you're [proxying Libreddit through a NGINX Reverse Proxy](https://github.com/spikecodes/libreddit/issues/122#issuecomment-782226853), add
|
**NOTE** If you're [proxying Libreddit through a NGINX Reverse Proxy](https://github.com/spikecodes/libreddit/issues/122#issuecomment-782226853), add
|
||||||
|
@ -60,7 +60,7 @@ async fn stream(url: &str, req: &Request<Body>) -> Result<Response<Body>, String
|
|||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request(url: String) -> Boxed<Result<Response<Body>, String>> {
|
fn request(url: String, quarantine: bool) -> Boxed<Result<Response<Body>, String>> {
|
||||||
// Prepare the HTTPS connector.
|
// Prepare the HTTPS connector.
|
||||||
let https = hyper_rustls::HttpsConnector::with_native_roots();
|
let https = hyper_rustls::HttpsConnector::with_native_roots();
|
||||||
|
|
||||||
@ -75,6 +75,7 @@ fn request(url: String) -> Boxed<Result<Response<Body>, String>> {
|
|||||||
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
|
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
|
||||||
.header("Accept-Language", "en-US,en;q=0.5")
|
.header("Accept-Language", "en-US,en;q=0.5")
|
||||||
.header("Connection", "keep-alive")
|
.header("Connection", "keep-alive")
|
||||||
|
.header("Cookie", if quarantine { "_options=%7B%22pref_quarantine_optin%22%3A%20true%7D" } else { "" })
|
||||||
.body(Body::empty());
|
.body(Body::empty());
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
@ -89,6 +90,7 @@ fn request(url: String) -> Boxed<Result<Response<Body>, String>> {
|
|||||||
.map(|val| val.to_str().unwrap_or_default())
|
.map(|val| val.to_str().unwrap_or_default())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
quarantine,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
@ -105,7 +107,7 @@ fn request(url: String) -> Boxed<Result<Response<Body>, String>> {
|
|||||||
|
|
||||||
// Make a request to a Reddit API and parse the JSON response
|
// Make a request to a Reddit API and parse the JSON response
|
||||||
#[cached(size = 100, time = 30, result = true)]
|
#[cached(size = 100, time = 30, result = true)]
|
||||||
pub async fn json(path: String) -> Result<Value, String> {
|
pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
||||||
// Build Reddit url from path
|
// Build Reddit url from path
|
||||||
let url = format!("https://www.reddit.com{}", path);
|
let url = format!("https://www.reddit.com{}", path);
|
||||||
|
|
||||||
@ -116,7 +118,7 @@ pub async fn json(path: String) -> Result<Value, String> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Fetch the url...
|
// Fetch the url...
|
||||||
match request(url.clone()).await {
|
match request(url.clone(), quarantine).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
// asynchronously aggregate the chunks of the body
|
// asynchronously aggregate the chunks of the body
|
||||||
match hyper::body::aggregate(response).await {
|
match hyper::body::aggregate(response).await {
|
||||||
|
18
src/main.rs
18
src/main.rs
@ -66,6 +66,16 @@ async fn favicon() -> Result<Response<Body>, String> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn font() -> Result<Response<Body>, String> {
|
||||||
|
Ok(
|
||||||
|
Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header("content-type", "font/woff2")
|
||||||
|
.body(include_bytes!("../static/Inter.var.woff2").as_ref().into())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Response<Body>, String> {
|
async fn resource(body: &str, content_type: &str, cache: bool) -> Result<Response<Body>, String> {
|
||||||
let mut res = Response::builder()
|
let mut res = Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
@ -139,7 +149,7 @@ async fn main() {
|
|||||||
"Referrer-Policy" => "no-referrer",
|
"Referrer-Policy" => "no-referrer",
|
||||||
"X-Content-Type-Options" => "nosniff",
|
"X-Content-Type-Options" => "nosniff",
|
||||||
"X-Frame-Options" => "DENY",
|
"X-Frame-Options" => "DENY",
|
||||||
"Content-Security-Policy" => "default-src 'none'; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:;"
|
"Content-Security-Policy" => "default-src 'none'; font-src 'self'; script-src 'self' blob:; manifest-src 'self'; media-src 'self' data: blob: about:; style-src 'self' 'unsafe-inline'; base-uri 'none'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; connect-src 'self'; worker-src blob:;"
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(expire_time) = hsts {
|
if let Some(expire_time) = hsts {
|
||||||
@ -156,6 +166,7 @@ async fn main() {
|
|||||||
app.at("/robots.txt").get(|_| resource("User-agent: *\nAllow: /", "text/plain", true).boxed());
|
app.at("/robots.txt").get(|_| resource("User-agent: *\nAllow: /", "text/plain", true).boxed());
|
||||||
app.at("/favicon.ico").get(|_| favicon().boxed());
|
app.at("/favicon.ico").get(|_| favicon().boxed());
|
||||||
app.at("/logo.png").get(|_| pwa_logo().boxed());
|
app.at("/logo.png").get(|_| pwa_logo().boxed());
|
||||||
|
app.at("/Inter.var.woff2").get(|_| font().boxed());
|
||||||
app.at("/touch-icon-iphone.png").get(|_| iphone_logo().boxed());
|
app.at("/touch-icon-iphone.png").get(|_| iphone_logo().boxed());
|
||||||
app.at("/apple-touch-icon.png").get(|_| iphone_logo().boxed());
|
app.at("/apple-touch-icon.png").get(|_| iphone_logo().boxed());
|
||||||
app
|
app
|
||||||
@ -194,7 +205,10 @@ async fn main() {
|
|||||||
app.at("/settings/update").get(|r| settings::update(r).boxed());
|
app.at("/settings/update").get(|r| settings::update(r).boxed());
|
||||||
|
|
||||||
// Subreddit services
|
// Subreddit services
|
||||||
app.at("/r/:sub").get(|r| subreddit::community(r).boxed());
|
app
|
||||||
|
.at("/r/:sub")
|
||||||
|
.get(|r| subreddit::community(r).boxed())
|
||||||
|
.post(|r| subreddit::add_quarantine_exception(r).boxed());
|
||||||
|
|
||||||
app
|
app
|
||||||
.at("/r/u_:name")
|
.at("/r/u_:name")
|
||||||
|
35
src/post.rs
35
src/post.rs
@ -2,7 +2,9 @@
|
|||||||
use crate::client::json;
|
use crate::client::json;
|
||||||
use crate::esc;
|
use crate::esc;
|
||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
|
use crate::subreddit::{can_access_quarantine, quarantine};
|
||||||
use crate::utils::{error, format_num, format_url, param, rewrite_urls, setting, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences};
|
use crate::utils::{error, format_num, format_url, param, rewrite_urls, setting, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences};
|
||||||
|
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
@ -23,18 +25,22 @@ struct PostTemplate {
|
|||||||
pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
// Build Reddit API path
|
// Build Reddit API path
|
||||||
let mut path: String = format!("{}.json?{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default());
|
let mut path: String = format!("{}.json?{}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default());
|
||||||
|
let sub = req.param("sub").unwrap_or_default();
|
||||||
|
let quarantined = can_access_quarantine(&req, &sub);
|
||||||
|
|
||||||
// Set sort to sort query parameter
|
// Set sort to sort query parameter
|
||||||
let mut sort: String = param(&path, "sort");
|
let sort = param(&path, "sort").unwrap_or_else(|| {
|
||||||
|
// Grab default comment sort method from Cookies
|
||||||
|
let default_sort = setting(&req, "comment_sort");
|
||||||
|
|
||||||
// Grab default comment sort method from Cookies
|
// If there's no sort query but there's a default sort, set sort to default_sort
|
||||||
let default_sort = setting(&req, "comment_sort");
|
if default_sort.is_empty() {
|
||||||
|
String::new()
|
||||||
// If there's no sort query but there's a default sort, set sort to default_sort
|
} else {
|
||||||
if sort.is_empty() && !default_sort.is_empty() {
|
path = format!("{}.json?{}&sort={}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), default_sort);
|
||||||
sort = default_sort;
|
default_sort
|
||||||
path = format!("{}.json?{}&sort={}&raw_json=1", req.uri().path(), req.uri().query().unwrap_or_default(), sort);
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
// Log the post ID being fetched in debug mode
|
// Log the post ID being fetched in debug mode
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@ -44,7 +50,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
let highlighted_comment = &req.param("comment_id").unwrap_or_default();
|
let highlighted_comment = &req.param("comment_id").unwrap_or_default();
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
match json(path).await {
|
match json(path, quarantined).await {
|
||||||
// Otherwise, grab the JSON output from the request
|
// Otherwise, grab the JSON output from the request
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Parse the JSON into Post and Comment structs
|
// Parse the JSON into Post and Comment structs
|
||||||
@ -61,7 +67,14 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// If the Reddit API returns an error, exit and send error page to user
|
// If the Reddit API returns an error, exit and send error page to user
|
||||||
Err(msg) => error(req, msg).await,
|
Err(msg) => {
|
||||||
|
if msg == "quarantined" {
|
||||||
|
let sub = req.param("sub").unwrap_or_default();
|
||||||
|
quarantine(req, sub)
|
||||||
|
} else {
|
||||||
|
error(req, msg).await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::utils::{catch_random, error, format_num, format_url, param, setting, template, val, Post, Preferences};
|
use crate::utils::{catch_random, error, format_num, format_url, param, setting, template, val, Post, Preferences};
|
||||||
use crate::{client::json, RequestExt};
|
use crate::{
|
||||||
|
client::json,
|
||||||
|
subreddit::{can_access_quarantine, quarantine},
|
||||||
|
RequestExt,
|
||||||
|
};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
|
|
||||||
@ -39,27 +43,23 @@ 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 { "" };
|
let nsfw_results = if setting(&req, "show_nsfw") == "on" { "&include_over_18=on" } else { "" };
|
||||||
let path = format!("{}.json?{}{}", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
let path = format!("{}.json?{}{}", req.uri().path(), req.uri().query().unwrap_or_default(), nsfw_results);
|
||||||
let sub = req.param("sub").unwrap_or_default();
|
let sub = req.param("sub").unwrap_or_default();
|
||||||
|
let quarantined = can_access_quarantine(&req, &sub);
|
||||||
// Handle random subreddits
|
// Handle random subreddits
|
||||||
if let Ok(random) = catch_random(&sub, "/find").await {
|
if let Ok(random) = catch_random(&sub, "/find").await {
|
||||||
return Ok(random);
|
return Ok(random);
|
||||||
}
|
}
|
||||||
let query = param(&path, "q");
|
let query = param(&path, "q").unwrap_or_default();
|
||||||
|
|
||||||
let sort = if param(&path, "sort").is_empty() {
|
let sort = param(&path, "sort").unwrap_or_else(|| "relevance".to_string());
|
||||||
"relevance".to_string()
|
|
||||||
} else {
|
|
||||||
param(&path, "sort")
|
|
||||||
};
|
|
||||||
|
|
||||||
let subreddits = if param(&path, "restrict_sr").is_empty() {
|
let subreddits = match param(&path, "restrict_sr") {
|
||||||
search_subreddits(&query).await
|
None => search_subreddits(&query).await,
|
||||||
} else {
|
Some(_) => Vec::new(),
|
||||||
Vec::new()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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()));
|
||||||
|
|
||||||
match Post::fetch(&path, String::new()).await {
|
match Post::fetch(&path, String::new(), quarantined).await {
|
||||||
Ok((posts, after)) => template(SearchTemplate {
|
Ok((posts, after)) => template(SearchTemplate {
|
||||||
posts,
|
posts,
|
||||||
subreddits,
|
subreddits,
|
||||||
@ -67,15 +67,22 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
params: SearchParams {
|
params: SearchParams {
|
||||||
q: query.replace('"', """),
|
q: query.replace('"', """),
|
||||||
sort,
|
sort,
|
||||||
t: param(&path, "t"),
|
t: param(&path, "t").unwrap_or_default(),
|
||||||
before: param(&path, "after"),
|
before: param(&path, "after").unwrap_or_default(),
|
||||||
after,
|
after,
|
||||||
restrict_sr: param(&path, "restrict_sr"),
|
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
|
||||||
},
|
},
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(req),
|
||||||
url,
|
url,
|
||||||
}),
|
}),
|
||||||
Err(msg) => error(req, msg).await,
|
Err(msg) => {
|
||||||
|
if msg == "quarantined" {
|
||||||
|
let sub = req.param("sub").unwrap_or_default();
|
||||||
|
quarantine(req, sub)
|
||||||
|
} else {
|
||||||
|
error(req, msg).await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +90,7 @@ async fn search_subreddits(q: &str) -> Vec<Subreddit> {
|
|||||||
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit=3", q.replace(' ', "+"));
|
let subreddit_search_path = format!("/subreddits/search.json?q={}&limit=3", q.replace(' ', "+"));
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match json(subreddit_search_path).await {
|
match json(subreddit_search_path, false).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
match response["data"]["children"].as_array() {
|
match response["data"]["children"].as_array() {
|
||||||
|
@ -18,7 +18,7 @@ struct SettingsTemplate {
|
|||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
|
|
||||||
const PREFS: [&str; 10] = [
|
const PREFS: [&str; 9] = [
|
||||||
"theme",
|
"theme",
|
||||||
"front_page",
|
"front_page",
|
||||||
"layout",
|
"layout",
|
||||||
@ -28,7 +28,6 @@ const PREFS: [&str; 10] = [
|
|||||||
"show_nsfw",
|
"show_nsfw",
|
||||||
"use_hls",
|
"use_hls",
|
||||||
"hide_hls_notification",
|
"hide_hls_notification",
|
||||||
"subscriptions",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
@ -104,7 +103,7 @@ fn set_cookies_method(req: Request<Body>, remove_cookies: bool) -> Response<Body
|
|||||||
|
|
||||||
let mut res = redirect(path);
|
let mut res = redirect(path);
|
||||||
|
|
||||||
for &name in &PREFS {
|
for name in [PREFS.to_vec(), vec!["subscriptions"]].concat() {
|
||||||
match form.get(name) {
|
match form.get(name) {
|
||||||
Some(value) => res.insert_cookie(
|
Some(value) => res.insert_cookie(
|
||||||
Cookie::build(name.to_owned(), value.to_owned())
|
Cookie::build(name.to_owned(), value.to_owned())
|
||||||
|
131
src/subreddit.rs
131
src/subreddit.rs
@ -28,9 +28,20 @@ struct WikiTemplate {
|
|||||||
prefs: Preferences,
|
prefs: Preferences,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "wall.html", escape = "none")]
|
||||||
|
struct WallTemplate {
|
||||||
|
title: String,
|
||||||
|
sub: String,
|
||||||
|
msg: String,
|
||||||
|
prefs: Preferences,
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
// Build Reddit API path
|
// Build Reddit API path
|
||||||
|
let root = req.uri().path() == "/";
|
||||||
let subscribed = setting(&req, "subscriptions");
|
let subscribed = setting(&req, "subscriptions");
|
||||||
let front_page = setting(&req, "front_page");
|
let front_page = setting(&req, "front_page");
|
||||||
let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string());
|
let post_sort = req.cookie("post_sort").map_or_else(|| "hot".to_string(), |c| c.value().to_string());
|
||||||
@ -45,6 +56,7 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
} else {
|
} else {
|
||||||
front_page.to_owned()
|
front_page.to_owned()
|
||||||
});
|
});
|
||||||
|
let quarantined = can_access_quarantine(&req, &sub) || root;
|
||||||
|
|
||||||
// Handle random subreddits
|
// Handle random subreddits
|
||||||
if let Ok(random) = catch_random(&sub, "").await {
|
if let Ok(random) = catch_random(&sub, "").await {
|
||||||
@ -57,16 +69,16 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
|
|
||||||
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub, sort, req.uri().query().unwrap_or_default());
|
let path = format!("/r/{}/{}.json?{}&raw_json=1", sub, sort, req.uri().query().unwrap_or_default());
|
||||||
|
|
||||||
match Post::fetch(&path, String::new()).await {
|
match Post::fetch(&path, String::new(), quarantined).await {
|
||||||
Ok((posts, after)) => {
|
Ok((posts, after)) => {
|
||||||
// If you can get subreddit posts, also request subreddit metadata
|
// If you can get subreddit posts, also request subreddit metadata
|
||||||
let sub = if !sub.contains('+') && sub != subscribed && sub != "popular" && sub != "all" {
|
let sub = if !sub.contains('+') && sub != subscribed && sub != "popular" && sub != "all" {
|
||||||
// Regular subreddit
|
// Regular subreddit
|
||||||
subreddit(&sub).await.unwrap_or_default()
|
subreddit(&sub, quarantined).await.unwrap_or_default()
|
||||||
} else if sub == subscribed {
|
} else if sub == subscribed {
|
||||||
// Subscription feed
|
// Subscription feed
|
||||||
if req.uri().path().starts_with("/r/") {
|
if req.uri().path().starts_with("/r/") {
|
||||||
subreddit(&sub).await.unwrap_or_default()
|
subreddit(&sub, quarantined).await.unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
Subreddit::default()
|
Subreddit::default()
|
||||||
}
|
}
|
||||||
@ -85,14 +97,14 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
template(SubredditTemplate {
|
template(SubredditTemplate {
|
||||||
sub,
|
sub,
|
||||||
posts,
|
posts,
|
||||||
sort: (sort, param(&path, "t")),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after"), after),
|
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(req),
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(msg) => match msg.as_str() {
|
Err(msg) => match msg.as_str() {
|
||||||
"quarantined" => error(req, format!("r/{} has been quarantined by Reddit", sub)).await,
|
"quarantined" => quarantine(req, sub),
|
||||||
"private" => error(req, format!("r/{} is a private community", sub)).await,
|
"private" => error(req, format!("r/{} is a private community", sub)).await,
|
||||||
"banned" => error(req, format!("r/{} has been banned from Reddit", sub)).await,
|
"banned" => error(req, format!("r/{} has been banned from Reddit", sub)).await,
|
||||||
_ => error(req, msg).await,
|
_ => error(req, msg).await,
|
||||||
@ -100,6 +112,43 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn quarantine(req: Request<Body>, sub: String) -> Result<Response<Body>, String> {
|
||||||
|
let wall = WallTemplate {
|
||||||
|
title: format!("r/{} is quarantined", sub),
|
||||||
|
msg: "Please click the button below to continue to this subreddit.".to_string(),
|
||||||
|
url: req.uri().to_string(),
|
||||||
|
sub,
|
||||||
|
prefs: Preferences::new(req),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Response::builder()
|
||||||
|
.status(403)
|
||||||
|
.header("content-type", "text/html")
|
||||||
|
.body(wall.render().unwrap_or_default().into())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_quarantine_exception(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
|
let subreddit = req.param("sub").ok_or("Invalid URL")?;
|
||||||
|
let redir = param(&format!("?{}", req.uri().query().unwrap_or_default()), "redir").ok_or("Invalid URL")?;
|
||||||
|
let mut res = redirect(redir);
|
||||||
|
res.insert_cookie(
|
||||||
|
Cookie::build(&format!("allow_quaran_{}", subreddit.to_lowercase()), "true")
|
||||||
|
.path("/")
|
||||||
|
.http_only(true)
|
||||||
|
.expires(cookie::Expiration::Session)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_access_quarantine(req: &Request<Body>, sub: &str) -> bool {
|
||||||
|
// Determine if the subreddit can be accessed
|
||||||
|
setting(&req, &format!("allow_quaran_{}", sub.to_lowercase())).parse().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
// Sub or unsub by setting subscription cookie using response "Set-Cookie" header
|
// Sub or unsub by setting subscription cookie using response "Set-Cookie" header
|
||||||
pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
let sub = req.param("sub").unwrap_or_default();
|
let sub = req.param("sub").unwrap_or_default();
|
||||||
@ -114,16 +163,19 @@ 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
|
// Retrieve list of posts for these subreddits to extract display names
|
||||||
let display = json(format!("/r/{}/hot.json?raw_json=1", sub)).await?;
|
let posts = json(format!("/r/{}/hot.json?raw_json=1", sub), true).await?;
|
||||||
let display_lookup: Vec<(String, &str)> = display["data"]["children"]
|
let display_lookup: Vec<(String, &str)> = posts["data"]["children"]
|
||||||
.as_array()
|
.as_array()
|
||||||
.unwrap()
|
.map(|list| {
|
||||||
.iter()
|
list
|
||||||
.map(|post| {
|
.iter()
|
||||||
let display_name = post["data"]["subreddit"].as_str().unwrap();
|
.map(|post| {
|
||||||
(display_name.to_lowercase(), display_name)
|
let display_name = post["data"]["subreddit"].as_str().unwrap_or_default();
|
||||||
|
(display_name.to_lowercase(), display_name)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// 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('+') {
|
||||||
@ -135,7 +187,7 @@ pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String>
|
|||||||
} else {
|
} else {
|
||||||
// This subreddit display name isn't known, retrieve it
|
// This subreddit display name isn't known, retrieve it
|
||||||
let path: String = format!("/r/{}/about.json?raw_json=1", part);
|
let path: String = format!("/r/{}/about.json?raw_json=1", part);
|
||||||
display = json(path).await?;
|
display = json(path, true).await?;
|
||||||
display["data"]["display_name"].as_str().ok_or_else(|| "Failed to query subreddit name".to_string())?
|
display["data"]["display_name"].as_str().ok_or_else(|| "Failed to query subreddit name".to_string())?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,17 +199,16 @@ pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String>
|
|||||||
sub_list.sort_by_key(|a| a.to_lowercase())
|
sub_list.sort_by_key(|a| a.to_lowercase())
|
||||||
} else if action.contains(&"unsubscribe".to_string()) {
|
} else if action.contains(&"unsubscribe".to_string()) {
|
||||||
// Remove sub name from subscribed list
|
// Remove sub name from subscribed list
|
||||||
sub_list.retain(|s| s != part);
|
sub_list.retain(|s| s.to_lowercase() != part.to_lowercase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect back to subreddit
|
// Redirect back to subreddit
|
||||||
// check for redirect parameter if unsubscribing from outside sidebar
|
// check for redirect parameter if unsubscribing from outside sidebar
|
||||||
let redirect_path = param(&format!("/?{}", query), "redirect");
|
let path = if let Some(redirect_path) = param(&format!("?{}", query), "redirect") {
|
||||||
let path = if redirect_path.is_empty() {
|
|
||||||
format!("/r/{}", sub)
|
|
||||||
} else {
|
|
||||||
format!("/{}/", redirect_path)
|
format!("/{}/", redirect_path)
|
||||||
|
} else {
|
||||||
|
format!("/r/{}", sub)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = redirect(path);
|
let mut res = redirect(path);
|
||||||
@ -180,6 +231,7 @@ pub async fn subscriptions(req: Request<Body>) -> Result<Response<Body>, String>
|
|||||||
|
|
||||||
pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
||||||
|
let quarantined = can_access_quarantine(&req, &sub);
|
||||||
// Handle random subreddits
|
// Handle random subreddits
|
||||||
if let Ok(random) = catch_random(&sub, "/wiki").await {
|
if let Ok(random) = catch_random(&sub, "/wiki").await {
|
||||||
return Ok(random);
|
return Ok(random);
|
||||||
@ -188,19 +240,26 @@ pub async fn wiki(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
let page = req.param("page").unwrap_or_else(|| "index".to_string());
|
let page = req.param("page").unwrap_or_else(|| "index".to_string());
|
||||||
let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page);
|
let path: String = format!("/r/{}/wiki/{}.json?raw_json=1", sub, page);
|
||||||
|
|
||||||
match json(path).await {
|
match json(path, quarantined).await {
|
||||||
Ok(response) => template(WikiTemplate {
|
Ok(response) => template(WikiTemplate {
|
||||||
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),
|
||||||
}),
|
}),
|
||||||
Err(msg) => error(req, msg).await,
|
Err(msg) => {
|
||||||
|
if msg == "quarantined" {
|
||||||
|
quarantine(req, sub)
|
||||||
|
} else {
|
||||||
|
error(req, msg).await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
let sub = req.param("sub").unwrap_or_else(|| "reddit.com".to_string());
|
||||||
|
let quarantined = can_access_quarantine(&req, &sub);
|
||||||
// Handle random subreddits
|
// Handle random subreddits
|
||||||
if let Ok(random) = catch_random(&sub, "/about/sidebar").await {
|
if let Ok(random) = catch_random(&sub, "/about/sidebar").await {
|
||||||
return Ok(random);
|
return Ok(random);
|
||||||
@ -210,26 +269,32 @@ pub async fn sidebar(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match json(path).await {
|
match json(path, quarantined).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => template(WikiTemplate {
|
Ok(response) => template(WikiTemplate {
|
||||||
wiki: format!(
|
wiki: format!(
|
||||||
"{}<hr><h1>Moderators</h1><br><ul>{}</ul>",
|
"{}<hr><h1>Moderators</h1><br><ul>{}</ul>",
|
||||||
rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
rewrite_urls(&val(&response, "description_html").replace("\\", "")),
|
||||||
moderators(&sub).await?.join(""),
|
moderators(&sub, quarantined).await?.join(""),
|
||||||
),
|
),
|
||||||
sub,
|
sub,
|
||||||
page: "Sidebar".to_string(),
|
page: "Sidebar".to_string(),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(req),
|
||||||
}),
|
}),
|
||||||
Err(msg) => error(req, msg).await,
|
Err(msg) => {
|
||||||
|
if msg == "quarantined" {
|
||||||
|
quarantine(req, sub)
|
||||||
|
} else {
|
||||||
|
error(req, msg).await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn moderators(sub: &str) -> Result<Vec<String>, String> {
|
pub async fn moderators(sub: &str, quarantined: bool) -> Result<Vec<String>, String> {
|
||||||
// Retrieve and format the html for the moderators list
|
// Retrieve and format the html for the moderators list
|
||||||
Ok(
|
Ok(
|
||||||
moderators_list(sub)
|
moderators_list(sub, quarantined)
|
||||||
.await?
|
.await?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| format!("<li><a style=\"color: var(--accent)\" href=\"/u/{name}\">{name}</a></li>", name = m))
|
.map(|m| format!("<li><a style=\"color: var(--accent)\" href=\"/u/{name}\">{name}</a></li>", name = m))
|
||||||
@ -237,12 +302,12 @@ pub async fn moderators(sub: &str) -> Result<Vec<String>, String> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn moderators_list(sub: &str) -> Result<Vec<String>, String> {
|
async fn moderators_list(sub: &str, quarantined: bool) -> Result<Vec<String>, String> {
|
||||||
// Build the moderator list URL
|
// Build the moderator list URL
|
||||||
let path: String = format!("/r/{}/about/moderators.json?raw_json=1", sub);
|
let path: String = format!("/r/{}/about/moderators.json?raw_json=1", sub);
|
||||||
|
|
||||||
// Retrieve response
|
// Retrieve response
|
||||||
let response = json(path).await?["data"]["children"].clone();
|
let response = json(path, quarantined).await?["data"]["children"].clone();
|
||||||
Ok(
|
Ok(
|
||||||
// Traverse json tree and format into list of strings
|
// Traverse json tree and format into list of strings
|
||||||
response
|
response
|
||||||
@ -262,12 +327,12 @@ async fn moderators_list(sub: &str) -> Result<Vec<String>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SUBREDDIT
|
// SUBREDDIT
|
||||||
async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, String> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
let path: String = format!("/r/{}/about.json?raw_json=1", sub);
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match json(path).await {
|
match json(path, quarantined).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Metadata regarding the subreddit
|
// Metadata regarding the subreddit
|
||||||
@ -275,7 +340,7 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
|
|||||||
let active: i64 = res["data"]["accounts_active"].as_u64().unwrap_or_default() as i64;
|
let active: i64 = res["data"]["accounts_active"].as_u64().unwrap_or_default() as i64;
|
||||||
|
|
||||||
// Fetch subreddit icon either from the community_icon or icon_img value
|
// Fetch subreddit icon either from the community_icon or icon_img value
|
||||||
let community_icon: &str = res["data"]["community_icon"].as_str().unwrap();
|
let community_icon: &str = res["data"]["community_icon"].as_str().unwrap_or_default();
|
||||||
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
||||||
|
|
||||||
let sub = Subreddit {
|
let sub = Subreddit {
|
||||||
@ -283,7 +348,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?,
|
moderators: moderators_list(sub, quarantined).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),
|
||||||
|
10
src/user.rs
10
src/user.rs
@ -29,11 +29,11 @@ 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");
|
let sort = param(&path, "sort").unwrap_or_default();
|
||||||
let username = req.param("name").unwrap_or_default();
|
let username = req.param("name").unwrap_or_default();
|
||||||
|
|
||||||
// Request user posts/comments from Reddit
|
// Request user posts/comments from Reddit
|
||||||
let posts = Post::fetch(&path, "Comment".to_string()).await;
|
let posts = Post::fetch(&path, "Comment".to_string(), false).await;
|
||||||
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()));
|
||||||
|
|
||||||
match posts {
|
match posts {
|
||||||
@ -44,8 +44,8 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
|
|||||||
template(UserTemplate {
|
template(UserTemplate {
|
||||||
user,
|
user,
|
||||||
posts,
|
posts,
|
||||||
sort: (sort, param(&path, "t")),
|
sort: (sort, param(&path, "t").unwrap_or_default()),
|
||||||
ends: (param(&path, "after"), after),
|
ends: (param(&path, "after").unwrap_or_default(), after),
|
||||||
prefs: Preferences::new(req),
|
prefs: Preferences::new(req),
|
||||||
url,
|
url,
|
||||||
})
|
})
|
||||||
@ -61,7 +61,7 @@ async fn user(name: &str) -> Result<User, String> {
|
|||||||
let path: String = format!("/user/{}/about.json?raw_json=1", name);
|
let path: String = format!("/user/{}/about.json?raw_json=1", name);
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match json(path).await {
|
match json(path, false).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
// Grab creation date as unix timestamp
|
// Grab creation date as unix timestamp
|
||||||
|
23
src/utils.rs
23
src/utils.rs
@ -217,12 +217,12 @@ pub struct Post {
|
|||||||
|
|
||||||
impl Post {
|
impl Post {
|
||||||
// Fetch posts of a user or subreddit and return a vector of posts and the "after" value
|
// Fetch posts of a user or subreddit and return a vector of posts and the "after" value
|
||||||
pub async fn fetch(path: &str, fallback_title: String) -> Result<(Vec<Self>, String), String> {
|
pub async fn fetch(path: &str, fallback_title: String, quarantine: bool) -> Result<(Vec<Self>, String), String> {
|
||||||
let res;
|
let res;
|
||||||
let post_list;
|
let post_list;
|
||||||
|
|
||||||
// Send a request to the url
|
// Send a request to the url
|
||||||
match json(path.to_string()).await {
|
match json(path.to_string(), quarantine).await {
|
||||||
// If success, receive JSON in response
|
// If success, receive JSON in response
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
res = response;
|
res = response;
|
||||||
@ -416,11 +416,16 @@ impl Preferences {
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Grab a query parameter from a url
|
// Grab a query parameter from a url
|
||||||
pub fn param(path: &str, value: &str) -> String {
|
pub fn param(path: &str, value: &str) -> Option<String> {
|
||||||
match Url::parse(format!("https://libredd.it/{}", path).as_str()) {
|
Some(
|
||||||
Ok(url) => url.query_pairs().into_owned().collect::<HashMap<_, _>>().get(value).unwrap_or(&String::new()).to_owned(),
|
Url::parse(format!("https://libredd.it/{}", path).as_str())
|
||||||
_ => String::new(),
|
.ok()?
|
||||||
}
|
.query_pairs()
|
||||||
|
.into_owned()
|
||||||
|
.collect::<HashMap<_, _>>()
|
||||||
|
.get(value)?
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the value of a setting by name
|
// Retrieve the value of a setting by name
|
||||||
@ -443,7 +448,7 @@ pub fn setting(req: &Request<Body>, name: &str) -> String {
|
|||||||
// Detect and redirect in the event of a random subreddit
|
// Detect and redirect in the event of a random subreddit
|
||||||
pub async fn catch_random(sub: &str, additional: &str) -> Result<Response<Body>, String> {
|
pub async fn catch_random(sub: &str, additional: &str) -> Result<Response<Body>, String> {
|
||||||
if (sub == "random" || sub == "randnsfw") && !sub.contains('+') {
|
if (sub == "random" || sub == "randnsfw") && !sub.contains('+') {
|
||||||
let new_sub = json(format!("/r/{}/about.json?raw_json=1", sub)).await?["data"]["display_name"]
|
let new_sub = json(format!("/r/{}/about.json?raw_json=1", sub), false).await?["data"]["display_name"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string();
|
.to_string();
|
||||||
@ -528,7 +533,7 @@ pub fn rewrite_urls(input_text: &str) -> String {
|
|||||||
match Regex::new(r"https://external-preview\.redd\.it(.*)[^?]") {
|
match Regex::new(r"https://external-preview\.redd\.it(.*)[^?]") {
|
||||||
Ok(re) => {
|
Ok(re) => {
|
||||||
if re.is_match(&text1) {
|
if re.is_match(&text1) {
|
||||||
re.replace_all(&text1, format_url(re.find(&text1).unwrap().as_str())).to_string()
|
re.replace_all(&text1, format_url(re.find(&text1).map(|x| x.as_str()).unwrap_or_default())).to_string()
|
||||||
} else {
|
} else {
|
||||||
text1
|
text1
|
||||||
}
|
}
|
||||||
|
BIN
static/Inter.var.woff2
Normal file
BIN
static/Inter.var.woff2
Normal file
Binary file not shown.
@ -6,6 +6,12 @@
|
|||||||
--admin: #ea0027;
|
--admin: #ea0027;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
src: url('/Inter.var.woff2') format('woff2-variations');
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
/* Automatic theme selection */
|
/* Automatic theme selection */
|
||||||
:root, .dark{
|
:root, .dark{
|
||||||
/* Default & fallback theme (dark) */
|
/* Default & fallback theme (dark) */
|
||||||
@ -150,7 +156,7 @@ html, body, div, h1, h2, h3, h4, h5, h6, ul, ol, dl, li, dt, dd, p, blockquote,
|
|||||||
pre, form, fieldset, table, th, td, select, input {
|
pre, form, fieldset, table, th, td, select, input {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-family: sans-serif;
|
font-family: "Inter";
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
{% macro render_hls_notification(redirect_url) -%}
|
{% macro render_hls_notification(redirect_url) -%}
|
||||||
{% if post.post_type == "video" && !post.media.alt_url.is_empty() && prefs.hide_hls_notification != "on" %}
|
{% if post.post_type == "video" && !post.media.alt_url.is_empty() && prefs.hide_hls_notification != "on" %}
|
||||||
<div class="post_notification"><p><a href="/settings/update/?use_hls=on&redirect={{ redirect_url }}">Enable HSL</a> to view with audio, or <a href="/settings/update/?hide_hls_notification=on&redirect={{ redirect_url }}">disable this notification</a></p></div>
|
<div class="post_notification"><p><a href="/settings/update/?use_hls=on&redirect={{ redirect_url }}">Enable HLS</a> to view with audio, or <a href="/settings/update/?hide_hls_notification=on&redirect={{ redirect_url }}">disable this notification</a></p></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
13
templates/wall.html
Normal file
13
templates/wall.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{ msg }}{% endblock %}
|
||||||
|
{% block sortstyle %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div id="wall">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<br>
|
||||||
|
<p>{{ msg }}</p>
|
||||||
|
<form action="/r/{{ sub }}?redir={{ url }}" method="POST">
|
||||||
|
<input id="save" type="submit" value="Continue">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Reference in New Issue
Block a user