Error Pages
This commit is contained in:
parent
7a176c6804
commit
1960e8a0fb
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -958,9 +958,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
|
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
@ -8,7 +8,7 @@ use subreddit::{posts, Post};
|
|||||||
|
|
||||||
#[path = "utils.rs"]
|
#[path = "utils.rs"]
|
||||||
mod utils;
|
mod utils;
|
||||||
use utils::Params;
|
use utils::{ErrorTemplate, Params};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -33,16 +33,27 @@ async fn render(sub_name: String, sort: Option<String>, ends: (Option<String>, O
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let items = posts(url).await?;
|
let items_result = posts(url).await;
|
||||||
|
|
||||||
let s = PopularTemplate {
|
if items_result.is_err() {
|
||||||
posts: items.0,
|
let s = ErrorTemplate {
|
||||||
sort: sorting,
|
message: items_result.err().unwrap().to_string(),
|
||||||
ends: (before, items.1),
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
|
} else {
|
||||||
|
let items = items_result.unwrap();
|
||||||
|
|
||||||
|
let s = PopularTemplate {
|
||||||
|
posts: items.0,
|
||||||
|
sort: sorting,
|
||||||
|
ends: (before, items.1),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
}
|
}
|
||||||
.render()
|
|
||||||
.unwrap();
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
|
56
src/post.rs
56
src/post.rs
@ -6,7 +6,7 @@ use pulldown_cmark::{html, Options, Parser};
|
|||||||
|
|
||||||
#[path = "utils.rs"]
|
#[path = "utils.rs"]
|
||||||
mod utils;
|
mod utils;
|
||||||
use utils::{request, val, Comment, Flair, Params, Post};
|
use utils::{request, val, Comment, ErrorTemplate, Flair, Params, Post};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -19,20 +19,26 @@ struct PostTemplate {
|
|||||||
|
|
||||||
async fn render(id: String, sort: String) -> Result<HttpResponse> {
|
async fn render(id: String, sort: String) -> Result<HttpResponse> {
|
||||||
println!("id: {}", id);
|
println!("id: {}", id);
|
||||||
let post: Post = fetch_post(&id).await?;
|
let post = fetch_post(&id).await;
|
||||||
let comments: Vec<Comment> = fetch_comments(id, &sort).await?;
|
let comments = fetch_comments(id, &sort).await;
|
||||||
|
|
||||||
let s = PostTemplate {
|
if post.is_err() || comments.is_err() {
|
||||||
comments: comments,
|
let s = ErrorTemplate {
|
||||||
post: post,
|
message: post.err().unwrap().to_string(),
|
||||||
sort: sort,
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
|
} else {
|
||||||
|
let s = PostTemplate {
|
||||||
|
comments: comments.unwrap(),
|
||||||
|
post: post.unwrap(),
|
||||||
|
sort: sort,
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
}
|
}
|
||||||
.render()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// println!("{}", s);
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
@ -86,12 +92,20 @@ async fn markdown_to_html(md: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POSTS
|
// POSTS
|
||||||
async fn fetch_post(id: &String) -> Result<Post> {
|
async fn fetch_post(id: &String) -> Result<Post, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("https://reddit.com/{}.json", id);
|
let url: String = format!("https://reddit.com/{}.json", id);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
let post_data: &serde_json::Value = &res[0]["data"]["children"][0];
|
let post_data: &serde_json::Value = &res[0]["data"]["children"][0];
|
||||||
|
|
||||||
@ -122,12 +136,20 @@ async fn fetch_post(id: &String) -> Result<Post> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// COMMENTS
|
// COMMENTS
|
||||||
async fn fetch_comments(id: String, sort: &String) -> Result<Vec<Comment>> {
|
async fn fetch_comments(id: String, sort: &String) -> Result<Vec<Comment>, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("https://reddit.com/{}.json?sort={}", id, sort);
|
let url: String = format!("https://reddit.com/{}.json?sort={}", id, sort);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
let comment_data = res[1]["data"]["children"].as_array().unwrap();
|
let comment_data = res[1]["data"]["children"].as_array().unwrap();
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc};
|
|||||||
|
|
||||||
#[path = "utils.rs"]
|
#[path = "utils.rs"]
|
||||||
mod utils;
|
mod utils;
|
||||||
pub use utils::{request, val, Flair, Params, Post, Subreddit};
|
pub use utils::{request, val, ErrorTemplate, Flair, Params, Post, Subreddit};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -37,33 +37,53 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sub: Subreddit = subreddit(&sub_name).await?;
|
let sub_result = subreddit(&sub_name).await;
|
||||||
let items = posts(url).await?;
|
let items_result = posts(url).await;
|
||||||
|
|
||||||
sub.icon = if sub.icon != "" {
|
if sub_result.is_err() || items_result.is_err() {
|
||||||
format!(r#"<img class="subreddit_icon" src="{}">"#, sub.icon)
|
let s = ErrorTemplate {
|
||||||
|
message: sub_result.err().unwrap().to_string(),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
let mut sub = sub_result.unwrap();
|
||||||
};
|
let items = items_result.unwrap();
|
||||||
|
|
||||||
let s = SubredditTemplate {
|
sub.icon = if sub.icon != "" {
|
||||||
sub: sub,
|
format!(r#"<img class="subreddit_icon" src="{}">"#, sub.icon)
|
||||||
posts: items.0,
|
} else {
|
||||||
sort: sorting,
|
String::new()
|
||||||
ends: (before, items.1),
|
};
|
||||||
|
|
||||||
|
let s = SubredditTemplate {
|
||||||
|
sub: sub,
|
||||||
|
posts: items.0,
|
||||||
|
sort: sorting,
|
||||||
|
ends: (before, items.1),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
}
|
}
|
||||||
.render()
|
|
||||||
.unwrap();
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SUBREDDIT
|
// SUBREDDIT
|
||||||
async fn subreddit(sub: &String) -> Result<Subreddit> {
|
async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("https://www.reddit.com/r/{}/about.json", sub);
|
let url: String = format!("https://www.reddit.com/r/{}/about.json", sub);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
let icon: String = String::from(res["data"]["community_icon"].as_str().unwrap()); //val(&data, "community_icon");
|
let icon: String = String::from(res["data"]["community_icon"].as_str().unwrap()); //val(&data, "community_icon");
|
||||||
let icon_split: std::str::Split<&str> = icon.split("?");
|
let icon_split: std::str::Split<&str> = icon.split("?");
|
||||||
@ -80,9 +100,17 @@ async fn subreddit(sub: &String) -> Result<Subreddit> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POSTS
|
// POSTS
|
||||||
pub async fn posts(url: String) -> Result<(Vec<Post>, String)> {
|
pub async fn posts(url: String) -> Result<(Vec<Post>, String), &'static str> {
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
// Fetch the list of posts from the JSON response
|
// Fetch the list of posts from the JSON response
|
||||||
let post_list = res["data"]["children"].as_array().unwrap();
|
let post_list = res["data"]["children"].as_array().unwrap();
|
||||||
|
56
src/user.rs
56
src/user.rs
@ -5,7 +5,7 @@ use chrono::{TimeZone, Utc};
|
|||||||
|
|
||||||
#[path = "utils.rs"]
|
#[path = "utils.rs"]
|
||||||
mod utils;
|
mod utils;
|
||||||
use utils::{nested_val, request, val, Flair, Params, Post, User};
|
use utils::{nested_val, request, val, ErrorTemplate, Flair, Params, Post, User};
|
||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
@ -17,11 +17,26 @@ struct UserTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
async fn render(username: String, sort: String) -> Result<HttpResponse> {
|
||||||
let user: User = user(&username).await;
|
let user = user(&username).await;
|
||||||
let posts: Vec<Post> = posts(username, &sort).await;
|
let posts = posts(username, &sort).await;
|
||||||
|
|
||||||
let s = UserTemplate { user: user, posts: posts, sort: sort }.render().unwrap();
|
if user.is_err() || posts.is_err() {
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
let s = ErrorTemplate {
|
||||||
|
message: user.err().unwrap().to_string(),
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
|
} else {
|
||||||
|
let s = UserTemplate {
|
||||||
|
user: user.unwrap(),
|
||||||
|
posts: posts.unwrap(),
|
||||||
|
sort: sort,
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap();
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(s))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SERVICES
|
// SERVICES
|
||||||
@ -34,29 +49,46 @@ async fn page(web::Path(username): web::Path<String>, params: web::Query<Params>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// USER
|
// USER
|
||||||
async fn user(name: &String) -> User {
|
async fn user(name: &String) -> Result<User, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("https://www.reddit.com/user/{}/about.json", name);
|
let url: String = format!("https://www.reddit.com/user/{}/about.json", name);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
User {
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
|
// Parse the JSON output into a User struct
|
||||||
|
Ok(User {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
icon: nested_val(&res, "subreddit", "icon_img").await,
|
icon: nested_val(&res, "subreddit", "icon_img").await,
|
||||||
karma: res["data"]["total_karma"].as_i64().unwrap(),
|
karma: res["data"]["total_karma"].as_i64().unwrap(),
|
||||||
banner: nested_val(&res, "subreddit", "banner_img").await,
|
banner: nested_val(&res, "subreddit", "banner_img").await,
|
||||||
description: nested_val(&res, "subreddit", "public_description").await,
|
description: nested_val(&res, "subreddit", "public_description").await,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// POSTS
|
// POSTS
|
||||||
async fn posts(sub: String, sort: &String) -> Vec<Post> {
|
async fn posts(sub: String, sort: &String) -> Result<Vec<Post>, &'static str> {
|
||||||
// Build the Reddit JSON API url
|
// Build the Reddit JSON API url
|
||||||
let url: String = format!("https://www.reddit.com/u/{}/.json?sort={}", sub, sort);
|
let url: String = format!("https://www.reddit.com/u/{}/.json?sort={}", sub, sort);
|
||||||
|
|
||||||
// Send a request to the url, receive JSON in response
|
// Send a request to the url, receive JSON in response
|
||||||
let res = request(url).await;
|
let req = request(url).await;
|
||||||
|
|
||||||
|
// If the Reddit API returns an error, exit this function
|
||||||
|
if req.is_err() {
|
||||||
|
return Err(req.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, grab the JSON output from the request
|
||||||
|
let res = req.unwrap();
|
||||||
|
|
||||||
let post_list = res["data"]["children"].as_array().unwrap();
|
let post_list = res["data"]["children"].as_array().unwrap();
|
||||||
|
|
||||||
@ -93,5 +125,5 @@ async fn posts(sub: String, sort: &String) -> Vec<Post> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
posts
|
Ok(posts)
|
||||||
}
|
}
|
||||||
|
38
src/utils.rs
38
src/utils.rs
@ -1,3 +1,6 @@
|
|||||||
|
//
|
||||||
|
// STRUCTS
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// Post flair with text, background color and foreground color
|
// Post flair with text, background color and foreground color
|
||||||
pub struct Flair(pub String, pub String, pub String);
|
pub struct Flair(pub String, pub String, pub String);
|
||||||
@ -52,6 +55,17 @@ pub struct Params {
|
|||||||
pub before: Option<String>,
|
pub before: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error template
|
||||||
|
#[derive(askama::Template)]
|
||||||
|
#[template(path = "error.html", escape = "none")]
|
||||||
|
pub struct ErrorTemplate {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// JSON PARSING
|
||||||
|
//
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// val() function used to parse JSON from Reddit APIs
|
// val() function used to parse JSON from Reddit APIs
|
||||||
pub async fn val(j: &serde_json::Value, k: &str) -> String {
|
pub async fn val(j: &serde_json::Value, k: &str) -> String {
|
||||||
@ -64,9 +78,13 @@ pub async fn nested_val(j: &serde_json::Value, n: &str, k: &str) -> String {
|
|||||||
String::from(j["data"][n][k].as_str().unwrap())
|
String::from(j["data"][n][k].as_str().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// NETWORKING
|
||||||
|
//
|
||||||
|
|
||||||
// Make a request to a Reddit API and parse the JSON response
|
// Make a request to a Reddit API and parse the JSON response
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn request(url: String) -> serde_json::Value {
|
pub async fn request(url: String) -> Result<serde_json::Value, &'static str> {
|
||||||
// --- actix-web::client ---
|
// --- actix-web::client ---
|
||||||
// let client = actix_web::client::Client::default();
|
// let client = actix_web::client::Client::default();
|
||||||
// let res = client
|
// let res = client
|
||||||
@ -86,10 +104,22 @@ pub async fn request(url: String) -> serde_json::Value {
|
|||||||
// let body = res.body_string().await.unwrap();
|
// let body = res.body_string().await.unwrap();
|
||||||
|
|
||||||
// --- reqwest ---
|
// --- reqwest ---
|
||||||
let resp: String = reqwest::get(&url).await.unwrap().text().await.unwrap();
|
let res = reqwest::get(&url).await.unwrap();
|
||||||
|
// Read the status from the response
|
||||||
|
let success = res.status().is_success();
|
||||||
|
// Read the body of the response
|
||||||
|
let body = res.text().await.unwrap();
|
||||||
|
|
||||||
// Parse the response from Reddit as JSON
|
// Parse the response from Reddit as JSON
|
||||||
let json: serde_json::Value = serde_json::from_str(resp.as_str()).expect("Failed to parse JSON");
|
let json: serde_json::Value = serde_json::from_str(body.as_str()).unwrap_or(serde_json::Value::Null);
|
||||||
|
|
||||||
json
|
if !success {
|
||||||
|
Ok(json)
|
||||||
|
} else if json == serde_json::Value::Null {
|
||||||
|
println!("! {} - {}", url, "Failed to parse page JSON data");
|
||||||
|
Err("Failed to parse page JSON data")
|
||||||
|
} else {
|
||||||
|
println!("! {} - {}", url, "Page not found");
|
||||||
|
Err("Page not found")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
templates/error.html
Normal file
18
templates/error.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Page not found.</title>
|
||||||
|
<meta name="description" content="View on Libreddit, an alternative private front-end to Reddit.">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a href="/"><span style="color:white">lib</span>reddit.</a>
|
||||||
|
<a style="color:white" href="https://github.com/spikecodes/libreddit">GITHUB</a>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1 style="text-align: center; font-size: 50px;">{{ message }}</h1>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user