Libreddit -> Redlib

This commit is contained in:
Matthew Esposito 2023-12-26 18:25:52 -05:00
parent dac059573d
commit b0f985c687
No known key found for this signature in database
38 changed files with 276 additions and 275 deletions

View file

@ -137,7 +137,7 @@ fn reddit_head(path: String, quarantine: bool) -> Boxed<Result<Response<Body>, S
/// in its response.
fn request(method: &'static Method, path: String, redirect: bool, quarantine: bool) -> Boxed<Result<Response<Body>, String>> {
// Increment reddit request count. This will include head requests.
if config::get_setting("LIBREDDIT_DISABLE_STATS_COLLECTION").is_none() {
if config::get_setting("REDLIB_DISABLE_STATS_COLLECTION").is_none() {
INSTANCE_INFO.reddit_requests.fetch_add(1, SeqCst);
}
// Build Reddit URL from path.

View file

@ -20,61 +20,61 @@ pub(crate) const DEFAULT_PUSHSHIFT_FRONTEND: &str = "www.unddit.com";
/// instance_info::InstanceInfo.to_string(), README.md and app.json.
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
pub struct Config {
#[serde(rename = "LIBREDDIT_SFW_ONLY")]
#[serde(rename = "REDLIB_SFW_ONLY")]
pub(crate) sfw_only: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_THEME")]
#[serde(rename = "REDLIB_DEFAULT_THEME")]
pub(crate) default_theme: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_FRONT_PAGE")]
#[serde(rename = "REDLIB_DEFAULT_FRONT_PAGE")]
pub(crate) default_front_page: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_LAYOUT")]
#[serde(rename = "REDLIB_DEFAULT_LAYOUT")]
pub(crate) default_layout: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_WIDE")]
#[serde(rename = "REDLIB_DEFAULT_WIDE")]
pub(crate) default_wide: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_COMMENT_SORT")]
#[serde(rename = "REDLIB_DEFAULT_COMMENT_SORT")]
pub(crate) default_comment_sort: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_POST_SORT")]
#[serde(rename = "REDLIB_DEFAULT_POST_SORT")]
pub(crate) default_post_sort: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_SHOW_NSFW")]
#[serde(rename = "REDLIB_DEFAULT_SHOW_NSFW")]
pub(crate) default_show_nsfw: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_BLUR_NSFW")]
#[serde(rename = "REDLIB_DEFAULT_BLUR_NSFW")]
pub(crate) default_blur_nsfw: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_USE_HLS")]
#[serde(rename = "REDLIB_DEFAULT_USE_HLS")]
pub(crate) default_use_hls: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION")]
#[serde(rename = "REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION")]
pub(crate) default_hide_hls_notification: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_HIDE_AWARDS")]
#[serde(rename = "REDLIB_DEFAULT_HIDE_AWARDS")]
pub(crate) default_hide_awards: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_HIDE_SCORE")]
#[serde(rename = "REDLIB_DEFAULT_HIDE_SCORE")]
pub(crate) default_hide_score: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_SUBSCRIPTIONS")]
#[serde(rename = "REDLIB_DEFAULT_SUBSCRIPTIONS")]
pub(crate) default_subscriptions: Option<String>,
#[serde(rename = "LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
#[serde(rename = "REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION")]
pub(crate) default_disable_visit_reddit_confirmation: Option<String>,
#[serde(rename = "LIBREDDIT_BANNER")]
#[serde(rename = "REDLIB_BANNER")]
pub(crate) banner: Option<String>,
#[serde(rename = "LIBREDDIT_ROBOTS_DISABLE_INDEXING")]
#[serde(rename = "REDLIB_ROBOTS_DISABLE_INDEXING")]
pub(crate) robots_disable_indexing: Option<String>,
#[serde(rename = "LIBREDDIT_DISABLE_STATS_COLLECTION")]
#[serde(rename = "REDLIB_DISABLE_STATS_COLLECTION")]
pub(crate) disable_stats_collection: Option<String>,
#[serde(rename = "LIBREDDIT_PUSHSHIFT_FRONTEND")]
#[serde(rename = "REDLIB_PUSHSHIFT_FRONTEND")]
pub(crate) pushshift: Option<String>,
}
@ -83,59 +83,59 @@ impl Config {
/// In the case that there are no environment variables set and there is no
/// config file, this function returns a Config that contains all None values.
pub fn load() -> Self {
// Read from libreddit.toml config file. If for any reason, it fails, the
// Read from redlib.toml config file. If for any reason, it fails, the
// default `Config` is used (all None values)
let config: Config = toml::from_str(&read_to_string("libreddit.toml").unwrap_or_default()).unwrap_or_default();
let config: Config = toml::from_str(&read_to_string("redlib.toml").unwrap_or_default()).unwrap_or_default();
// This function defines the order of preference - first check for
// environment variables with "LIBREDDIT", then check the config, then if
// environment variables with "REDLIB", then check the config, then if
// both are `None`, return a `None` via the `map_or_else` function
let parse = |key: &str| -> Option<String> { var(key).ok().map_or_else(|| get_setting_from_config(key, &config), Some) };
Self {
sfw_only: parse("LIBREDDIT_SFW_ONLY"),
default_theme: parse("LIBREDDIT_DEFAULT_THEME"),
default_front_page: parse("LIBREDDIT_DEFAULT_FRONT_PAGE"),
default_layout: parse("LIBREDDIT_DEFAULT_LAYOUT"),
default_post_sort: parse("LIBREDDIT_DEFAULT_POST_SORT"),
default_wide: parse("LIBREDDIT_DEFAULT_WIDE"),
default_comment_sort: parse("LIBREDDIT_DEFAULT_COMMENT_SORT"),
default_show_nsfw: parse("LIBREDDIT_DEFAULT_SHOW_NSFW"),
default_blur_nsfw: parse("LIBREDDIT_DEFAULT_BLUR_NSFW"),
default_use_hls: parse("LIBREDDIT_DEFAULT_USE_HLS"),
default_hide_hls_notification: parse("LIBREDDIT_DEFAULT_HIDE_HLS"),
default_hide_awards: parse("LIBREDDIT_DEFAULT_HIDE_AWARDS"),
default_hide_score: parse("LIBREDDIT_DEFAULT_HIDE_SCORE"),
default_subscriptions: parse("LIBREDDIT_DEFAULT_SUBSCRIPTIONS"),
default_disable_visit_reddit_confirmation: parse("LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION"),
banner: parse("LIBREDDIT_BANNER"),
robots_disable_indexing: parse("LIBREDDIT_ROBOTS_DISABLE_INDEXING"),
disable_stats_collection: parse("LIBREDDIT_DISABLE_STATS_COLLECTION"),
pushshift: parse("LIBREDDIT_PUSHSHIFT_FRONTEND"),
sfw_only: parse("REDLIB_SFW_ONLY"),
default_theme: parse("REDLIB_DEFAULT_THEME"),
default_front_page: parse("REDLIB_DEFAULT_FRONT_PAGE"),
default_layout: parse("REDLIB_DEFAULT_LAYOUT"),
default_post_sort: parse("REDLIB_DEFAULT_POST_SORT"),
default_wide: parse("REDLIB_DEFAULT_WIDE"),
default_comment_sort: parse("REDLIB_DEFAULT_COMMENT_SORT"),
default_show_nsfw: parse("REDLIB_DEFAULT_SHOW_NSFW"),
default_blur_nsfw: parse("REDLIB_DEFAULT_BLUR_NSFW"),
default_use_hls: parse("REDLIB_DEFAULT_USE_HLS"),
default_hide_hls_notification: parse("REDLIB_DEFAULT_HIDE_HLS"),
default_hide_awards: parse("REDLIB_DEFAULT_HIDE_AWARDS"),
default_hide_score: parse("REDLIB_DEFAULT_HIDE_SCORE"),
default_subscriptions: parse("REDLIB_DEFAULT_SUBSCRIPTIONS"),
default_disable_visit_reddit_confirmation: parse("REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION"),
banner: parse("REDLIB_BANNER"),
robots_disable_indexing: parse("REDLIB_ROBOTS_DISABLE_INDEXING"),
disable_stats_collection: parse("REDLIB_DISABLE_STATS_COLLECTION"),
pushshift: parse("REDLIB_PUSHSHIFT_FRONTEND"),
}
}
}
fn get_setting_from_config(name: &str, config: &Config) -> Option<String> {
match name {
"LIBREDDIT_SFW_ONLY" => config.sfw_only.clone(),
"LIBREDDIT_DEFAULT_THEME" => config.default_theme.clone(),
"LIBREDDIT_DEFAULT_FRONT_PAGE" => config.default_front_page.clone(),
"LIBREDDIT_DEFAULT_LAYOUT" => config.default_layout.clone(),
"LIBREDDIT_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(),
"LIBREDDIT_DEFAULT_POST_SORT" => config.default_post_sort.clone(),
"LIBREDDIT_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
"LIBREDDIT_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
"LIBREDDIT_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
"LIBREDDIT_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
"LIBREDDIT_DEFAULT_WIDE" => config.default_wide.clone(),
"LIBREDDIT_DEFAULT_HIDE_AWARDS" => config.default_hide_awards.clone(),
"LIBREDDIT_DEFAULT_HIDE_SCORE" => config.default_hide_score.clone(),
"LIBREDDIT_DEFAULT_SUBSCRIPTIONS" => config.default_subscriptions.clone(),
"LIBREDDIT_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION" => config.default_disable_visit_reddit_confirmation.clone(),
"LIBREDDIT_BANNER" => config.banner.clone(),
"LIBREDDIT_ROBOTS_DISABLE_INDEXING" => config.robots_disable_indexing.clone(),
"LIBREDDIT_DISABLE_STATS_COLLECTION" => config.disable_stats_collection.clone(),
"LIBREDDIT_PUSHSHIFT_FRONTEND" => config.pushshift.clone(),
"REDLIB_SFW_ONLY" => config.sfw_only.clone(),
"REDLIB_DEFAULT_THEME" => config.default_theme.clone(),
"REDLIB_DEFAULT_FRONT_PAGE" => config.default_front_page.clone(),
"REDLIB_DEFAULT_LAYOUT" => config.default_layout.clone(),
"REDLIB_DEFAULT_COMMENT_SORT" => config.default_comment_sort.clone(),
"REDLIB_DEFAULT_POST_SORT" => config.default_post_sort.clone(),
"REDLIB_DEFAULT_SHOW_NSFW" => config.default_show_nsfw.clone(),
"REDLIB_DEFAULT_BLUR_NSFW" => config.default_blur_nsfw.clone(),
"REDLIB_DEFAULT_USE_HLS" => config.default_use_hls.clone(),
"REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION" => config.default_hide_hls_notification.clone(),
"REDLIB_DEFAULT_WIDE" => config.default_wide.clone(),
"REDLIB_DEFAULT_HIDE_AWARDS" => config.default_hide_awards.clone(),
"REDLIB_DEFAULT_HIDE_SCORE" => config.default_hide_score.clone(),
"REDLIB_DEFAULT_SUBSCRIPTIONS" => config.default_subscriptions.clone(),
"REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION" => config.default_disable_visit_reddit_confirmation.clone(),
"REDLIB_BANNER" => config.banner.clone(),
"REDLIB_ROBOTS_DISABLE_INDEXING" => config.robots_disable_indexing.clone(),
"REDLIB_DISABLE_STATS_COLLECTION" => config.disable_stats_collection.clone(),
"REDLIB_PUSHSHIFT_FRONTEND" => config.pushshift.clone(),
_ => None,
}
}
@ -156,7 +156,7 @@ fn test_deserialize() {
}
#[test]
#[sealed_test(env = [("LIBREDDIT_SFW_ONLY", "on")])]
#[sealed_test(env = [("REDLIB_SFW_ONLY", "on")])]
fn test_env_var() {
assert!(crate::utils::sfw_only())
}
@ -164,41 +164,41 @@ fn test_env_var() {
#[test]
#[sealed_test]
fn test_config() {
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
write("libreddit.toml", config_to_write).unwrap();
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("best".into()));
let config_to_write = r#"REDLIB_DEFAULT_COMMENT_SORT = "best""#;
write("redlib.toml", config_to_write).unwrap();
assert_eq!(get_setting("REDLIB_DEFAULT_COMMENT_SORT"), Some("best".into()));
}
#[test]
#[sealed_test(env = [("LIBREDDIT_DEFAULT_COMMENT_SORT", "top")])]
#[sealed_test(env = [("REDLIB_DEFAULT_COMMENT_SORT", "top")])]
fn test_env_config_precedence() {
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
write("libreddit.toml", config_to_write).unwrap();
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("top".into()))
let config_to_write = r#"REDLIB_DEFAULT_COMMENT_SORT = "best""#;
write("redlib.toml", config_to_write).unwrap();
assert_eq!(get_setting("REDLIB_DEFAULT_COMMENT_SORT"), Some("top".into()))
}
#[test]
#[sealed_test(env = [("LIBREDDIT_DEFAULT_COMMENT_SORT", "top")])]
#[sealed_test(env = [("REDLIB_DEFAULT_COMMENT_SORT", "top")])]
fn test_alt_env_config_precedence() {
let config_to_write = r#"LIBREDDIT_DEFAULT_COMMENT_SORT = "best""#;
write("libreddit.toml", config_to_write).unwrap();
assert_eq!(get_setting("LIBREDDIT_DEFAULT_COMMENT_SORT"), Some("top".into()))
let config_to_write = r#"REDLIB_DEFAULT_COMMENT_SORT = "best""#;
write("redlib.toml", config_to_write).unwrap();
assert_eq!(get_setting("REDLIB_DEFAULT_COMMENT_SORT"), Some("top".into()))
}
#[test]
#[sealed_test(env = [("LIBREDDIT_DEFAULT_SUBSCRIPTIONS", "news+bestof")])]
#[sealed_test(env = [("REDLIB_DEFAULT_SUBSCRIPTIONS", "news+bestof")])]
fn test_default_subscriptions() {
assert_eq!(get_setting("LIBREDDIT_DEFAULT_SUBSCRIPTIONS"), Some("news+bestof".into()));
assert_eq!(get_setting("REDLIB_DEFAULT_SUBSCRIPTIONS"), Some("news+bestof".into()));
}
#[test]
fn test_stats_collection_empty() {
assert_eq!(get_setting("LIBREDDIT_DISABLE_STATS_COLLECTION"), None);
assert_eq!(get_setting("REDLIB_DISABLE_STATS_COLLECTION"), None);
}
#[test]
#[sealed_test]
fn test_stats_collection_true() {
let config_to_write = r#"LIBREDDIT_DISABLE_STATS_COLLECTION = "1""#;
write("libreddit.toml", config_to_write).unwrap();
assert!(get_setting("LIBREDDIT_DISABLE_STATS_COLLECTION").is_some());
let config_to_write = r#"REDLIB_DISABLE_STATS_COLLECTION = "1""#;
write("redlib.toml", config_to_write).unwrap();
assert!(get_setting("REDLIB_DISABLE_STATS_COLLECTION").is_some());
}

View file

@ -118,7 +118,7 @@ async fn main() {
// Initialize logger
pretty_env_logger::init();
let matches = Command::new("Libreddit")
let matches = Command::new("Redlib")
.version(env!("CARGO_PKG_VERSION"))
.about("Private front-end for Reddit written in Rust ")
.arg(
@ -165,7 +165,7 @@ async fn main() {
let listener = [address, ":", port].concat();
println!("Starting Libreddit...");
println!("Starting Redlib...");
// Begin constructing a server
let mut app = server::Server::new();
@ -204,7 +204,7 @@ async fn main() {
.get(|_| resource(include_str!("../static/manifest.json"), "application/json", false).boxed());
app.at("/robots.txt").get(|_| {
resource(
if match config::get_setting("LIBREDDIT_ROBOTS_DISABLE_INDEXING") {
if match config::get_setting("REDLIB_ROBOTS_DISABLE_INDEXING") {
Some(val) => val == "on",
None => false,
} {
@ -229,7 +229,7 @@ async fn main() {
.at("/hls.min.js")
.get(|_| resource(include_str!("../static/hls.min.js"), "text/javascript", false).boxed());
// Proxy media through Libreddit
// Proxy media through Redlib
app.at("/vid/:id/:size").get(|r| proxy(r, "https://v.redd.it/{id}/DASH_{size}").boxed());
app.at("/hls/:id/*path").get(|r| proxy(r, "https://v.redd.it/{id}/{path}").boxed());
app.at("/img/*path").get(|r| proxy(r, "https://i.redd.it/{path}").boxed());
@ -370,7 +370,7 @@ async fn main() {
// Default service in case no routes match
app.at("/*").get(|req| error(req, "Nothing here".to_string()).boxed());
println!("Running Libreddit v{} on {}!", env!("CARGO_PKG_VERSION"), listener);
println!("Running Redlib v{} on {}!", env!("CARGO_PKG_VERSION"), listener);
let server = app.listen(listener);

View file

@ -171,7 +171,7 @@ fn build_comment(
let body = if (val(comment, "author") == "[deleted]" && val(comment, "body") == "[removed]") || val(comment, "body") == "[ Removed by Reddit ]" {
format!(
"<div class=\"md\"><p>[removed] — <a href=\"https://{}{}{}\">view removed comment</a></p></div>",
get_setting("LIBREDDIT_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)),
get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)),
post_link,
id
)
@ -218,7 +218,7 @@ fn build_comment(
let is_filtered = filters.contains(&["u_", author.name.as_str()].concat());
// Many subreddits have a default comment posted about the sub's rules etc.
// Many libreddit users do not wish to see this kind of comment by default.
// Many Redlib users do not wish to see this kind of comment by default.
// Reddit does not tell us which users are "bots", so a good heuristic is to
// collapse stickied moderator comments.
let is_moderator_comment = data["distinguished"].as_str().unwrap_or_default() == "moderator";

View file

@ -235,7 +235,7 @@ impl Server {
match router.recognize(&format!("/{}{}", req.method().as_str(), path)) {
// If a route was configured for this path
Ok(found) => {
if config::get_setting("LIBREDDIT_DISABLE_STATS_COLLECTION").is_none() {
if config::get_setting("REDLIB_DISABLE_STATS_COLLECTION").is_none() {
// Add to total_requests count
INSTANCE_INFO.total_requests.fetch_add(1, SeqCst);
}

View file

@ -43,7 +43,7 @@ pub async fn profile(req: Request<Body>) -> Result<Response<Body>, String> {
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
let redirect_url = url[1..].replace('?', "%3F").replace('&', "%26");
// Retrieve other variables from Libreddit request
// Retrieve other variables from Redlib request
let sort = param(&path, "sort").unwrap_or_default();
let username = req.param("name").unwrap_or_default();

View file

@ -673,7 +673,7 @@ pub async fn parse_post(post: &serde_json::Value) -> Post {
let body = if val(post, "removed_by_category") == "moderator" {
format!(
"<div class=\"md\"><p>[removed] — <a href=\"https://{}{}\">view removed post</a></p></div>",
get_setting("LIBREDDIT_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)),
get_setting("REDLIB_PUSHSHIFT_FRONTEND").unwrap_or(String::from(crate::config::DEFAULT_PUSHSHIFT_FRONTEND)),
permalink
)
} else {
@ -766,7 +766,7 @@ pub fn setting(req: &Request<Body>, name: &str) -> String {
.cookie(name)
.unwrap_or_else(|| {
// If there is no cookie for this setting, try receiving a default from the config
if let Some(default) = crate::config::get_setting(&format!("LIBREDDIT_DEFAULT_{}", name.to_uppercase())) {
if let Some(default) = crate::config::get_setting(&format!("REDLIB_DEFAULT_{}", name.to_uppercase())) {
Cookie::new(name, default)
} else {
Cookie::from(name)
@ -874,17 +874,17 @@ pub fn format_url(url: &str) -> String {
static REDDIT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|new\.|)(reddit\.com|redd\.it)/"#).unwrap());
static REDDIT_PREVIEW_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"https?://external-preview\.redd\.it(.*)[^?]").unwrap());
// Rewrite Reddit links to Libreddit in body of text
// Rewrite Reddit links to Redlib in body of text
pub fn rewrite_urls(input_text: &str) -> String {
let text1 =
// Rewrite Reddit links to Libreddit
// Rewrite Reddit links to Redlib
REDDIT_REGEX.replace_all(input_text, r#"href="/"#)
.to_string()
// Remove (html-encoded) "\" from URLs.
.replace("%5C", "")
.replace("\\_", "_");
// Rewrite external media previews to Libreddit
// Rewrite external media previews to Redlib
if REDDIT_PREVIEW_REGEX.is_match(&text1) {
REDDIT_PREVIEW_REGEX
.replace_all(&text1, format_url(REDDIT_PREVIEW_REGEX.find(&text1).map(|x| x.as_str()).unwrap_or_default()))
@ -987,7 +987,7 @@ pub async fn error(req: Request<Body>, msg: impl ToString) -> Result<Response<Bo
Ok(Response::builder().status(404).header("content-type", "text/html").body(body.into()).unwrap_or_default())
}
/// Returns true if the config/env variable `LIBREDDIT_SFW_ONLY` carries the
/// Returns true if the config/env variable `REDLIB_SFW_ONLY` carries the
/// value `on`.
///
/// If this variable is set as such, the instance will operate in SFW-only
@ -995,7 +995,7 @@ pub async fn error(req: Request<Body>, msg: impl ToString) -> Result<Response<Bo
/// subreddits or posts or userpages for users Reddit has deemed NSFW will
/// be denied.
pub fn sfw_only() -> bool {
match crate::config::get_setting("LIBREDDIT_SFW_ONLY") {
match crate::config::get_setting("REDLIB_SFW_ONLY") {
Some(val) => val == "on",
None => false,
}