â»ï¸ refactor: actual 404 error handling
This commit is contained in:
parent
aa56704c20
commit
5849c1a7aa
4 changed files with 42 additions and 25 deletions
2
build.rs
2
build.rs
|
@ -2,4 +2,4 @@
|
|||
fn main() {
|
||||
// trigger recompilation when a new migration is added
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
pub use actix_web::main;
|
||||
use actix_web::{
|
||||
delete,
|
||||
error::{ErrorBadRequest, ErrorForbidden, ErrorNotFound},
|
||||
get, post, web, HttpRequest, HttpResponse,
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use once_cell::sync::Lazy;
|
||||
|
@ -15,6 +13,8 @@ use crate::{auth, db::Database};
|
|||
mod snowflake;
|
||||
mod webhook;
|
||||
|
||||
use self::snowflake::Snowflake;
|
||||
|
||||
/// The host of the server
|
||||
pub static HOST: Lazy<String> =
|
||||
Lazy::new(|| std::env::var("TAMAKO_HOST").unwrap_or_else(|_| "127.0.0.1".to_owned()));
|
||||
|
@ -27,7 +27,13 @@ pub static PORT: Lazy<u16> = Lazy::new(|| {
|
|||
});
|
||||
|
||||
/// The snowflake generator
|
||||
static SNOWFLAKE: Lazy<snowflake::Snowflake> = Lazy::new(snowflake::Snowflake::new);
|
||||
static SNOWFLAKE: Lazy<Snowflake> = Lazy::new(Snowflake::new);
|
||||
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
return Err($e);
|
||||
};
|
||||
}
|
||||
|
||||
/// The representation of a whisper
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
|
@ -56,15 +62,15 @@ impl Whisper {
|
|||
fn validate(&mut self) -> actix_web::Result<()> {
|
||||
self.name = self.name.take().filter(|name| !name.is_empty());
|
||||
if self.message.is_empty() {
|
||||
return Err(ErrorBadRequest("whispers cannot be empty"));
|
||||
bail!(ErrorBadRequest("whispers cannot be empty"));
|
||||
}
|
||||
if let Some(name) = &self.name {
|
||||
if name.len() > 32 {
|
||||
return Err(ErrorBadRequest("name cannot be longer than 32 characters"));
|
||||
bail!(ErrorBadRequest("name cannot be longer than 32 characters"));
|
||||
}
|
||||
}
|
||||
if self.message.len() > 1024 {
|
||||
return Err(ErrorBadRequest(
|
||||
bail!(ErrorBadRequest(
|
||||
"whispers cannot be longer than 1024 characters",
|
||||
));
|
||||
}
|
||||
|
@ -124,15 +130,15 @@ impl Private for Vec<Whisper> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Authenticates the secret key
|
||||
/// Validates the secret key
|
||||
#[allow(clippy::unused_async)]
|
||||
#[post("/api/auth")]
|
||||
pub async fn authentication(req: HttpRequest) -> actix_web::Result<HttpResponse> {
|
||||
if !auth::validate_header(&req) {
|
||||
return Err(ErrorForbidden("Invalid token"));
|
||||
bail!(ErrorForbidden("Invalid token"));
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().body("Authenticated"))
|
||||
Ok(HttpResponse::Ok().body("Validated"))
|
||||
}
|
||||
|
||||
/// Adds a new whisper
|
||||
|
@ -222,11 +228,10 @@ pub async fn delete(
|
|||
database: web::Data<Database>,
|
||||
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
|
||||
if !auth::validate_header(&req) {
|
||||
return Err(actix_web::error::ErrorForbidden("Invalid token").into());
|
||||
bail!(Box::new(ErrorForbidden("Invalid token")));
|
||||
}
|
||||
|
||||
let snowflake = path.into_inner();
|
||||
|
||||
database
|
||||
.delete(snowflake)
|
||||
.await
|
||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -1,4 +1,10 @@
|
|||
use actix_web::{middleware, web, App, HttpServer};
|
||||
use actix_files::Files;
|
||||
use actix_governor::{Governor, GovernorConfigBuilder};
|
||||
use actix_web::{
|
||||
http::StatusCode,
|
||||
middleware::{Compress, ErrorHandlers, NormalizePath, TrailingSlash},
|
||||
web, App, HttpServer,
|
||||
};
|
||||
|
||||
mod api;
|
||||
mod auth;
|
||||
|
@ -17,21 +23,20 @@ async fn main() -> eyre::Result<()> {
|
|||
.wrap(actix_logger::Logger::new(twink::fmt!(
|
||||
"<green>%s <purple>%r</> took <cyan>%Dms</> | %{X-Forwarded-For}i <i>%{User-Agent}i</>"
|
||||
)))
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(middleware::NormalizePath::new(
|
||||
middleware::TrailingSlash::Trim,
|
||||
))
|
||||
.wrap(Compress::default())
|
||||
.wrap(NormalizePath::new(TrailingSlash::Trim))
|
||||
.wrap(ErrorHandlers::new().handler(StatusCode::NOT_FOUND, templates::not_found))
|
||||
.service(templates::home)
|
||||
.service(templates::auth)
|
||||
.service(actix_files::Files::new("/assets", "assets"))
|
||||
.service(Files::new("/assets", "assets"))
|
||||
.service(web::resource("/api/health").route(web::get().to(|| async { "ð" })))
|
||||
.service(api::list)
|
||||
.service(api::get)
|
||||
.service(
|
||||
web::resource("/api/whisper")
|
||||
.route(web::post().to(api::add))
|
||||
.wrap(actix_governor::Governor::new(
|
||||
&actix_governor::GovernorConfigBuilder::default()
|
||||
.wrap(Governor::new(
|
||||
&GovernorConfigBuilder::default()
|
||||
.per_second(360)
|
||||
.burst_size(2)
|
||||
.finish()
|
||||
|
@ -40,7 +45,6 @@ async fn main() -> eyre::Result<()> {
|
|||
)
|
||||
.service(api::delete)
|
||||
.service(api::authentication)
|
||||
.default_service(web::to(templates::not_found))
|
||||
})
|
||||
.bind((api::HOST.as_str(), *api::PORT))?
|
||||
.run()
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use actix_web::{get, web, HttpRequest, Responder};
|
||||
use actix_web::{
|
||||
dev::ServiceResponse, get, middleware::ErrorHandlerResponse, web, HttpRequest, HttpResponse,
|
||||
Responder,
|
||||
};
|
||||
use askama::Template;
|
||||
|
||||
use crate::{
|
||||
|
@ -112,7 +115,12 @@ impl NotFoundTemplate {
|
|||
}
|
||||
|
||||
/// Renders the not found page
|
||||
#[allow(clippy::unused_async)]
|
||||
pub async fn not_found() -> impl Responder {
|
||||
NotFoundTemplate::new()
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
pub fn not_found<B>(res: ServiceResponse<B>) -> actix_web::Result<ErrorHandlerResponse<B>> {
|
||||
Ok(ErrorHandlerResponse::Response(ServiceResponse::new(
|
||||
res.into_parts().0,
|
||||
HttpResponse::NotFound()
|
||||
.body(NotFoundTemplate::new().render().unwrap())
|
||||
.map_into_right_body(),
|
||||
)))
|
||||
}
|
||||
|
|
Loadingâ¦
Reference in a new issue