OrangeGuidanceTomestone/server/src/web.rs

131 lines
4.7 KiB
Rust
Raw Normal View History

2022-09-03 10:35:15 +00:00
use std::convert::Infallible;
use std::sync::Arc;
use warp::{Filter, Rejection, Reply};
2022-09-10 10:30:46 +00:00
use warp::body::BodyDeserializeError;
2022-09-03 10:35:15 +00:00
use warp::filters::BoxedFilter;
use warp::http::StatusCode;
2022-09-10 10:30:46 +00:00
use warp::reject::{MethodNotAllowed, Reject};
2022-09-03 10:35:15 +00:00
use crate::State;
mod register;
mod unregister;
mod write;
mod erase;
mod get_location;
mod vote;
2022-09-04 04:32:56 +00:00
mod get_mine;
2022-09-04 07:12:51 +00:00
mod get_message;
2022-09-05 08:02:34 +00:00
mod claim;
2022-09-06 12:07:23 +00:00
mod ping;
2022-09-08 03:07:00 +00:00
mod packs;
2022-09-03 10:35:15 +00:00
pub fn routes(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
register::register(Arc::clone(&state))
.or(unregister::unregister(Arc::clone(&state)))
.or(write::write(Arc::clone(&state)))
.or(erase::erase(Arc::clone(&state)))
.or(vote::vote(Arc::clone(&state)))
2022-09-04 07:12:51 +00:00
.or(get_message::get_message(Arc::clone(&state)))
2022-09-04 04:32:56 +00:00
.or(get_location::get_location(Arc::clone(&state)))
.or(get_mine::get_mine(Arc::clone(&state)))
2022-09-05 08:02:34 +00:00
.or(claim::claim(Arc::clone(&state)))
2022-09-06 12:07:23 +00:00
.or(ping::ping(Arc::clone(&state)))
2022-09-08 03:07:00 +00:00
.or(packs::packs(Arc::clone(&state)))
2022-09-03 10:35:15 +00:00
.recover(handle_rejection)
.boxed()
}
2022-09-05 04:02:36 +00:00
pub fn get_id(state: Arc<State>) -> BoxedFilter<((i64, i64), )> {
2022-09-10 10:57:25 +00:00
warp::header::optional("x-api-key")
.and_then(move |access_token: Option<String>| {
2022-09-03 10:35:15 +00:00
let state = Arc::clone(&state);
async move {
2022-09-10 10:57:25 +00:00
let access_token = match access_token {
Some(t) => t,
None => return Err(warp::reject::custom(WebError::MissingAuthToken)),
};
2022-09-03 10:35:15 +00:00
let hashed = crate::util::hash(&access_token);
let id = sqlx::query!(
// language=sqlite
2022-09-05 04:02:36 +00:00
"select id, extra from users where auth = ?",
2022-09-03 10:35:15 +00:00
hashed,
)
.fetch_optional(&state.db)
.await;
match id {
2022-09-05 04:02:36 +00:00
Ok(Some(i)) => Ok((i.id, i.extra)),
2022-09-03 10:35:15 +00:00
Ok(None) => Err(warp::reject::custom(WebError::InvalidAuthToken)),
Err(e) => Err(warp::reject::custom(AnyhowRejection(e.into()))),
}
}
})
.boxed()
}
#[derive(Debug)]
pub enum WebError {
2022-09-10 10:57:25 +00:00
MissingAuthToken,
2022-09-03 10:35:15 +00:00
InvalidAuthToken,
InvalidPackId,
InvalidIndex,
2022-09-04 04:32:56 +00:00
TooManyMessages,
2022-09-04 07:12:51 +00:00
NoSuchMessage,
2022-09-05 08:02:34 +00:00
InvalidExtraCode,
2022-09-03 10:35:15 +00:00
}
impl Reject for WebError {}
#[derive(Debug)]
pub struct AnyhowRejection(anyhow::Error);
impl Reject for AnyhowRejection {}
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
2022-09-10 10:57:25 +00:00
let (status, name, desc) = if let Some(e) = err.find::<WebError>() {
match e {
WebError::MissingAuthToken => (StatusCode::BAD_REQUEST, "missing_auth_token", "an auth token was not provided".into()),
WebError::InvalidAuthToken => (StatusCode::BAD_REQUEST, "invalid_auth_token", "the auth token was not valid".into()),
WebError::InvalidPackId => (StatusCode::NOT_FOUND, "invalid_pack_id", "the server does not have a pack registered with that id".into()),
WebError::InvalidIndex => (StatusCode::NOT_FOUND, "invalid_index", "one of the provided indices was out of range".into()),
WebError::TooManyMessages => (StatusCode::BAD_REQUEST, "too_many_messages", "you have run out of messages - delete one and try again".into()),
WebError::NoSuchMessage => (StatusCode::NOT_FOUND, "no_such_message", "no message with that id was found".into()),
WebError::InvalidExtraCode => (StatusCode::BAD_REQUEST, "invalid_extra_code", "that extra code was not found".into()),
}
} else if err.is_not_found() {
(StatusCode::NOT_FOUND, "not_found", "route was unknown to the server".into())
2022-09-10 10:30:46 +00:00
} else if let Some(e) = err.find::<BodyDeserializeError>() {
(StatusCode::BAD_REQUEST, "invalid_body", format!("invalid body: {}", e))
} else if let Some(_) = err.find::<MethodNotAllowed>() {
2022-09-10 10:57:25 +00:00
(StatusCode::METHOD_NOT_ALLOWED, "method_not_allowed", "that http method is not allowed on that route".into())
2022-09-10 10:30:46 +00:00
} else if let Some(AnyhowRejection(e)) = err.find::<AnyhowRejection>() {
2022-09-03 10:35:15 +00:00
eprintln!("{:#?}", e);
2022-09-05 08:02:34 +00:00
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal_error",
2022-09-10 10:57:25 +00:00
"an internal logic error occured".into(),
2022-09-05 08:02:34 +00:00
)
2022-09-03 10:35:15 +00:00
} else {
eprintln!("{:#?}", err);
2022-09-05 08:02:34 +00:00
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal_error",
2022-09-10 10:57:25 +00:00
"an unhandled error was encountered".into(),
2022-09-05 08:02:34 +00:00
)
2022-09-03 10:35:15 +00:00
};
2022-09-05 08:02:34 +00:00
#[derive(serde::Serialize)]
struct ErrorMessage {
code: &'static str,
2022-09-10 10:57:25 +00:00
message: String,
2022-09-05 08:02:34 +00:00
}
let message = ErrorMessage {
code: name,
message: desc,
};
Ok(warp::reply::with_status(warp::reply::json(&message), status))
2022-09-03 10:35:15 +00:00
}