Skip to content

Commit

Permalink
Split routes to files
Browse files Browse the repository at this point in the history
  • Loading branch information
cofob committed Aug 1, 2024
1 parent 30f315b commit db358a5
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 142 deletions.
27 changes: 27 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use actix_web::http::StatusCode;
use askama::Template;
use thiserror::Error;

#[derive(Template)]
#[template(path = "error.html")]
Expand Down Expand Up @@ -53,3 +54,29 @@ macro_rules! impl_api_error {
};
}
pub(crate) use impl_api_error;

use crate::search::SearchError;

#[derive(Error, Debug)]
pub enum RedirectError {
#[error("search error: `{0}`")]
Search(#[from] SearchError),
#[error("serialization error: `{0}`")]
Serialization(#[from] serde_json::Error),
#[error("urlencode error: `{0}`")]
Base64Decode(#[from] base64::DecodeError),
#[error("url parse error: `{0}`")]
UrlParse(#[from] url::ParseError),
}

impl_api_error!(RedirectError,
status => {
RedirectError::Search(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::Serialization(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::Base64Decode(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::UrlParse(_) => StatusCode::INTERNAL_SERVER_ERROR,
},
data => {
_ => None,
}
);
7 changes: 7 additions & 0 deletions src/filters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::crawler::CrawledInstance;

pub fn sort_crawled_instances(l: &[CrawledInstance]) -> askama::Result<Vec<CrawledInstance>> {
let mut new = l.to_owned();
new.sort_by(|a, b| a.status.as_isize().cmp(&b.status.as_isize()));
Ok(new)
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod config;
mod crawler;
mod errors;
mod filters;
mod log_setup;
mod routes;
mod search;
mod serde_types;
mod utils;

use crate::crawler::Crawler;
use crate::serde_types::ServicesData;
Expand Down
57 changes: 57 additions & 0 deletions src/routes/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use actix_web::{cookie::Cookie, get, http::header::LOCATION, web, HttpRequest, Responder, Scope};
use askama::Template;
use base64::prelude::*;

use crate::{
config::AppConfig,
errors::RedirectError,
serde_types::{LoadedData, UserConfig},
utils::user_config::load_settings_cookie,
};

pub fn scope(_config: &AppConfig) -> Scope {
web::scope("/configure")
.service(configure_page)
.service(configure_save)
}

#[derive(Template)]
#[template(path = "configure.html")]
pub struct ConfigureTemplate<'a> {
current_config: &'a str,
}

#[get("")]
async fn configure_page(
req: HttpRequest,
loaded_data: web::Data<LoadedData>,
) -> actix_web::Result<impl Responder> {
let user_config = load_settings_cookie(&req, &loaded_data.default_settings);
let json: String = serde_json::to_string(&user_config).map_err(RedirectError::Serialization)?;
let data = BASE64_STANDARD.encode(json.as_bytes());

let template = ConfigureTemplate {
current_config: &data,
};

Ok(actix_web::HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(template.render().expect("failed to render error page")))
}

#[get("/save")]
async fn configure_save(req: HttpRequest) -> actix_web::Result<impl Responder> {
let query_string = req.query_string();
let b64_decoded = BASE64_STANDARD
.decode(query_string.as_bytes())
.map_err(RedirectError::Base64Decode)?;
let user_config: UserConfig =
serde_json::from_slice(&b64_decoded).map_err(RedirectError::Serialization)?;
let json: String = serde_json::to_string(&user_config).map_err(RedirectError::Serialization)?;
let data = BASE64_STANDARD.encode(json.as_bytes());
let cookie = Cookie::build("config", data).path("/").finish();
Ok(actix_web::HttpResponse::TemporaryRedirect()
.cookie(cookie)
.insert_header((LOCATION, "/configure?success"))
.finish())
}
51 changes: 51 additions & 0 deletions src/routes/index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::collections::HashMap;

use actix_web::{get, web, Responder, Scope};
use askama::Template;
use chrono::{DateTime, Utc};

use crate::{
config::AppConfig,
crawler::{CrawledService, Crawler},
errors::RedirectError,
filters,
search::SearchError,
serde_types::{LoadedData, ServicesData},
};

use super::{config, redirect};

pub fn scope(app_config: &AppConfig) -> Scope {
web::scope("")
.service(index)
.service(config::scope(app_config))
.service(redirect::scope(app_config))
}

#[derive(Template)]
#[template(path = "index.html")]
pub struct IndexTemplate<'a> {
pub crawled_services: &'a HashMap<String, CrawledService>,
pub services: &'a ServicesData,
pub time: &'a DateTime<Utc>,
}

#[get("/")]
async fn index(
crawler: web::Data<Crawler>,
loaded_data: web::Data<LoadedData>,
) -> actix_web::Result<impl Responder> {
let data = crawler.read().await;
let Some(crawled_services) = data.as_ref() else {
return Err(RedirectError::from(SearchError::CrawlerNotFetchedYet))?;
};
let template = IndexTemplate {
services: &loaded_data.services,
crawled_services: &crawled_services.services,
time: &crawled_services.time,
};

Ok(actix_web::HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(template.render().expect("failed to render error page")))
}
6 changes: 4 additions & 2 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod config;
mod index;
mod redirect;

use actix_web::{web, Scope};
use actix_web::Scope;

use crate::config::AppConfig;

pub fn main_scope(config: &AppConfig) -> Scope {
web::scope("").service(redirect::scope(config))
index::scope(config)
}
143 changes: 4 additions & 139 deletions src/routes/redirect.rs
Original file line number Diff line number Diff line change
@@ -1,165 +1,30 @@
use std::collections::HashMap;

use actix_web::{
cookie::Cookie,
get,
http::{header::LOCATION, Method, StatusCode},
http::{header::LOCATION, Method},
web, HttpRequest, Responder, Scope,
};
use askama::Template;
use base64::prelude::*;
use chrono::{DateTime, Utc};
use thiserror::Error;

use crate::{
config::AppConfig,
crawler::{CrawledService, Crawler},
errors::impl_api_error,
errors::RedirectError,
search::{
find_redirect_service_by_name, find_redirect_service_by_url, get_redirect_instance,
get_redirect_instances, SearchError,
},
serde_types::{LoadedData, Regexes, SelectMethod, Service, ServicesData, UserConfig},
serde_types::{LoadedData, Regexes, SelectMethod, Service},
utils::user_config::load_settings_cookie,
};

pub fn scope(_config: &AppConfig) -> Scope {
web::scope("")
.service(index)
.service(configure_page)
.service(configure_save)
.service(history_redirect)
.service(cached_redirect)
.route("/{path:.*}", web::get().to(base_redirect))
.route("/{path:.*}", web::post().to(base_redirect))
}

#[derive(Error, Debug)]
pub enum RedirectError {
#[error("search error: `{0}`")]
Search(#[from] SearchError),
#[error("serialization error: `{0}`")]
Serialization(#[from] serde_json::Error),
#[error("urlencode error: `{0}`")]
Base64Decode(#[from] base64::DecodeError),
#[error("url parse error: `{0}`")]
UrlParse(#[from] url::ParseError),
}

impl_api_error!(RedirectError,
status => {
RedirectError::Search(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::Serialization(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::Base64Decode(_) => StatusCode::INTERNAL_SERVER_ERROR,
RedirectError::UrlParse(_) => StatusCode::INTERNAL_SERVER_ERROR,
},
data => {
_ => None,
}
);

#[derive(Template)]
#[template(path = "index.html")]
pub struct IndexTemplate<'a> {
pub crawled_services: &'a HashMap<String, CrawledService>,
pub services: &'a ServicesData,
pub time: &'a DateTime<Utc>,
}

mod filters {
use crate::crawler::CrawledInstance;

pub fn sort_list(l: &[CrawledInstance]) -> ::askama::Result<Vec<CrawledInstance>> {
let mut new = l.to_owned();
new.sort_by(|a, b| a.status.as_isize().cmp(&b.status.as_isize()));
Ok(new)
}
}

#[get("/")]
async fn index(
crawler: web::Data<Crawler>,
loaded_data: web::Data<LoadedData>,
) -> actix_web::Result<impl Responder> {
let data = crawler.read().await;
let Some(crawled_services) = data.as_ref() else {
return Err(RedirectError::from(SearchError::CrawlerNotFetchedYet))?;
};
let template = IndexTemplate {
services: &loaded_data.services,
crawled_services: &crawled_services.services,
time: &crawled_services.time,
};

Ok(actix_web::HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(template.render().expect("failed to render error page")))
}

#[derive(Template)]
#[template(path = "configure.html")]
pub struct ConfigureTemplate<'a> {
current_config: &'a str,
}

#[get("/configure")]
async fn configure_page(
req: HttpRequest,
loaded_data: web::Data<LoadedData>,
) -> actix_web::Result<impl Responder> {
let user_config = load_settings_cookie(&req, &loaded_data.default_settings);
let json: String = serde_json::to_string(&user_config).map_err(RedirectError::Serialization)?;
let data = BASE64_STANDARD.encode(json.as_bytes());

let template = ConfigureTemplate {
current_config: &data,
};

Ok(actix_web::HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(template.render().expect("failed to render error page")))
}

#[get("/configure/save")]
async fn configure_save(req: HttpRequest) -> actix_web::Result<impl Responder> {
let query_string = req.query_string();
let b64_decoded = BASE64_STANDARD
.decode(query_string.as_bytes())
.map_err(RedirectError::Base64Decode)?;
let user_config: UserConfig =
serde_json::from_slice(&b64_decoded).map_err(RedirectError::Serialization)?;
let json: String = serde_json::to_string(&user_config).map_err(RedirectError::Serialization)?;
let data = BASE64_STANDARD.encode(json.as_bytes());
let cookie = Cookie::build("config", data).path("/").finish();
Ok(actix_web::HttpResponse::TemporaryRedirect()
.cookie(cookie)
.insert_header((LOCATION, "/configure?success"))
.finish())
}

fn load_settings_cookie(req: &HttpRequest, default: &UserConfig) -> UserConfig {
let cookie = match req.cookie("config") {
Some(cookie) => cookie,
None => {
debug!("Cookie not found");
return default.clone();
}
};
let data = match BASE64_STANDARD.decode(cookie.value().as_bytes()) {
Ok(data) => data,
Err(_) => {
debug!("invalid cookie data");
return default.clone();
}
};
match serde_json::from_slice(&data) {
Ok(user_config) => user_config,
Err(_) => {
debug!("invalid cookie query string");
default.clone()
}
}
}

#[derive(Template)]
#[template(path = "cached_redirect.html", escape = "none")]
pub struct CachedRedirectTemplate<'a> {
Expand Down
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod user_config;
28 changes: 28 additions & 0 deletions src/utils/user_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use actix_web::HttpRequest;
use base64::prelude::*;

use crate::serde_types::UserConfig;

pub fn load_settings_cookie(req: &HttpRequest, default: &UserConfig) -> UserConfig {
let cookie = match req.cookie("config") {
Some(cookie) => cookie,
None => {
debug!("Cookie not found");
return default.clone();
}
};
let data = match BASE64_STANDARD.decode(cookie.value().as_bytes()) {
Ok(data) => data,
Err(_) => {
debug!("invalid cookie data");
return default.clone();
}
};
match serde_json::from_slice(&data) {
Ok(user_config) => user_config,
Err(_) => {
debug!("invalid cookie query string");
default.clone()
}
}
}
2 changes: 1 addition & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ <h3>Last synced {{ time }}</h3>
Fallback: <code>{{ services[name].fallback }}</code>
</li>
<ul>
{% let instances = crawled_service.instances|sort_list %}
{% let instances = crawled_service.instances|sort_crawled_instances %}
{% for instance in instances %}
<li>
<a href="{{ instance.url }}">{{ instance.url }}</a> <span>Status:
Expand Down

0 comments on commit db358a5

Please sign in to comment.