diff --git a/api-backend/Cargo.lock b/api-backend/Cargo.lock
index d879336ec..86e876605 100644
--- a/api-backend/Cargo.lock
+++ b/api-backend/Cargo.lock
@@ -175,6 +175,28 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "axum-extra"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9"
+dependencies = [
+ "axum",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "serde",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "axum-macros"
version = "0.4.2"
@@ -723,10 +745,6 @@ dependencies = [
"utoipa-scalar",
]
-[[package]]
-name = "hartex_backend_extractors"
-version = "0.13.0"
-
[[package]]
name = "hartex_backend_models"
version = "0.13.0"
@@ -743,6 +761,7 @@ name = "hartex_backend_routes"
version = "0.13.0"
dependencies = [
"axum",
+ "axum-extra",
"bb8-postgres",
"hartex_backend_models",
"hartex_database_queries",
diff --git a/api-backend/Cargo.toml b/api-backend/Cargo.toml
index b2ed39034..6e1072e79 100644
--- a/api-backend/Cargo.toml
+++ b/api-backend/Cargo.toml
@@ -1,7 +1,6 @@
[workspace]
members = [
"hartex-backend-driver",
- "hartex-backend-extractors",
"hartex-backend-models",
"hartex-backend-routes",
]
diff --git a/api-backend/hartex-backend-driver/src/main.rs b/api-backend/hartex-backend-driver/src/main.rs
index 1fb9d176e..327210303 100644
--- a/api-backend/hartex-backend-driver/src/main.rs
+++ b/api-backend/hartex-backend-driver/src/main.rs
@@ -77,16 +77,15 @@ pub async fn main() -> miette::Result<()> {
let api_pgsql_url = env::var("API_PGSQL_URL").into_diagnostic()?;
log::debug!("building database connection pool");
- let manager = PostgresConnectionManager::new_from_stringlike(api_pgsql_url, NoTls).into_diagnostic()?;
+ let manager =
+ PostgresConnectionManager::new_from_stringlike(api_pgsql_url, NoTls).into_diagnostic()?;
let pool = Pool::builder().build(manager).await.into_diagnostic()?;
log::debug!("starting axum server");
let (app, mut openapi) = OpenApiRouter::new()
.layer(TraceLayer::new_for_http())
.layer(TimeoutLayer::new(Duration::from_secs(30)))
- .routes(routes!(
- hartex_backend_routes::uptime::get_uptime
- ))
+ .routes(routes!(hartex_backend_routes::uptime::get_uptime))
.with_state(pool)
.split_for_parts();
@@ -123,7 +122,7 @@ async fn shutdown() {
.recv()
.await;
};
-
+
#[cfg(not(unix))]
let terminate = future::pending::<()>();
diff --git a/api-backend/hartex-backend-extractors/Cargo.toml b/api-backend/hartex-backend-extractors/Cargo.toml
deleted file mode 100644
index 19c979c5f..000000000
--- a/api-backend/hartex-backend-extractors/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[package]
-name = "hartex_backend_extractors"
-version = "0.13.0"
-edition = "2021"
-description = """
-Backend extractors
-"""
-license = "AGPL-3.0-or-later"
-rust-version = "1.83.0"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-
-[features]
diff --git a/api-backend/hartex-backend-extractors/src/lib.rs b/api-backend/hartex-backend-extractors/src/lib.rs
deleted file mode 100644
index 0d9dfa73a..000000000
--- a/api-backend/hartex-backend-extractors/src/lib.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * SPDX-License-Identifier: AGPL-3.0-only
- *
- * This file is part of HarTex.
- *
- * HarTex
- * Copyright (c) 2021-2024 HarTex Project Developers
- *
- * HarTex is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * HarTex is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License along
- * with HarTex. If not, see .
- */
-
-//! # Backend Extractors
-//!
-//! This crate defines certain extractors for use with the Axum HTTP server.
-
-#![deny(clippy::pedantic)]
-#![deny(unsafe_code)]
-#![deny(warnings)]
diff --git a/api-backend/hartex-backend-models/src/lib.rs b/api-backend/hartex-backend-models/src/lib.rs
index 7f3c899df..0de02c95b 100644
--- a/api-backend/hartex-backend-models/src/lib.rs
+++ b/api-backend/hartex-backend-models/src/lib.rs
@@ -28,11 +28,11 @@
#![deny(unsafe_code)]
#![deny(warnings)]
+use axum::http::StatusCode;
use axum::Json;
use serde::Deserialize;
use serde::Serialize;
-pub use hartex_discord_configuration_models as config;
pub mod uptime;
/// An API response object.
@@ -40,7 +40,7 @@ pub mod uptime;
/// This is the object returned by a certain API endpoint.
#[derive(Deserialize, Serialize)]
pub struct Response {
- code: u16,
+ pub code: u16,
message: String,
data: Option,
}
@@ -49,22 +49,43 @@ impl<'a, T> Response
where
T: Clone + Deserialize<'a>,
{
+ #[allow(clippy::missing_panics_doc)]
+ pub fn from_code_with_data(code: StatusCode, data: T) -> (StatusCode, Json>) {
+ let code_display = code.to_string();
+ let part = code_display.split_once(' ').unwrap().1;
+
+ (
+ code,
+ Json(Self {
+ code: code.as_u16(),
+ message: part.to_lowercase(),
+ data: Some(data),
+ }),
+ )
+ }
+
/// Constructs a response object with a status code of 500 and its corresponding message.
- pub fn internal_server_error() -> Json> {
- Json(Self {
- code: 500,
- message: String::from("internal server error"),
- data: None,
- })
+ pub fn internal_server_error() -> (StatusCode, Json>) {
+ (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(Self {
+ code: 500,
+ message: String::from("internal server error"),
+ data: None,
+ }),
+ )
}
/// Constructs a response object with a status code of 200 and its corresponding message.
- pub fn ok(value: T) -> Json> {
- Json(Self {
- code: 200,
- message: String::from("ok"),
- data: Some(value),
- })
+ pub fn ok(value: T) -> (StatusCode, Json>) {
+ (
+ StatusCode::OK,
+ Json(Self {
+ code: 200,
+ message: String::from("ok"),
+ data: Some(value),
+ }),
+ )
}
}
diff --git a/api-backend/hartex-backend-models/src/uptime.rs b/api-backend/hartex-backend-models/src/uptime.rs
index ec5d39ddb..48510fd03 100644
--- a/api-backend/hartex-backend-models/src/uptime.rs
+++ b/api-backend/hartex-backend-models/src/uptime.rs
@@ -24,6 +24,10 @@
//!
//! Models for the uptime API specification V2 of the backend.
+use axum::extract::rejection::QueryRejection;
+use axum::http::StatusCode;
+use axum::response::IntoResponse;
+use axum::response::Response;
use serde::Deserialize;
use serde::Serialize;
use utoipa::IntoParams;
@@ -52,7 +56,27 @@ impl UptimeQuery {
}
}
-/// A response to an uptime query.
+pub struct UptimeQueryRejection {
+ status_code: StatusCode,
+ data_message: String,
+}
+
+impl From for UptimeQueryRejection {
+ fn from(value: QueryRejection) -> Self {
+ Self {
+ status_code: value.status(),
+ data_message: value.body_text().to_lowercase(),
+ }
+ }
+}
+
+impl IntoResponse for UptimeQueryRejection {
+ fn into_response(self) -> Response {
+ crate::Response::from_code_with_data(self.status_code, self.data_message).into_response()
+ }
+}
+
+/// The uptime of the specified component.
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Deserialize, Serialize, ToSchema)]
pub struct UptimeResponse {
@@ -66,7 +90,7 @@ impl UptimeResponse {
Self { start_timestamp }
}
- /// The start timestamp of the uptime entry.
+ /// The start timestamp of the uptime data.
#[must_use]
pub fn start_timestamp(&self) -> u128 {
self.start_timestamp
diff --git a/api-backend/hartex-backend-routes/Cargo.toml b/api-backend/hartex-backend-routes/Cargo.toml
index 92301a57f..6c0171d21 100644
--- a/api-backend/hartex-backend-routes/Cargo.toml
+++ b/api-backend/hartex-backend-routes/Cargo.toml
@@ -18,6 +18,7 @@ hartex_database_queries = { path = "../../database/hartex-database-queries" }
hartex_log = { path = "../../rust-utilities/hartex-log" }
axum = { version = "0.7.7", features = ["json", "macros"] }
+axum-extra = "0.9.4"
bb8-postgres = "0.8.1"
serde_json = "1.0.128"
time = "0.3.36"
diff --git a/api-backend/hartex-backend-routes/src/uptime.rs b/api-backend/hartex-backend-routes/src/uptime.rs
index aa76b63b2..946669bea 100644
--- a/api-backend/hartex-backend-routes/src/uptime.rs
+++ b/api-backend/hartex-backend-routes/src/uptime.rs
@@ -23,16 +23,17 @@
/// # Uptime Routes
///
/// Routes interacting with the uptime API.
-
use axum::extract::Query;
use axum::extract::State;
use axum::http::StatusCode;
use axum::Json;
+use axum_extra::extract::WithRejection;
use bb8_postgres::bb8::Pool;
use bb8_postgres::tokio_postgres::GenericClient;
use bb8_postgres::tokio_postgres::NoTls;
use bb8_postgres::PostgresConnectionManager;
use hartex_backend_models::uptime::UptimeQuery;
+use hartex_backend_models::uptime::UptimeQueryRejection;
use hartex_backend_models::uptime::UptimeResponse;
use hartex_backend_models::uptime::UptimeUpdate;
use hartex_backend_models::Response;
@@ -53,15 +54,12 @@ use time::OffsetDateTime;
)]
pub async fn get_uptime(
State(pool): State>>,
- Query(query): Query,
+ WithRejection(Query(query), _): WithRejection, UptimeQueryRejection>,
) -> (StatusCode, Json>) {
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Response::internal_server_error(),
- );
+ return Response::internal_server_error();
}
let connection = result.unwrap();
@@ -77,19 +75,13 @@ pub async fn get_uptime(
if result.is_err() {
log::error!("{:?}", result.unwrap_err());
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Response::internal_server_error(),
- );
+ return Response::internal_server_error();
}
let data = result.unwrap();
- (
- StatusCode::OK,
- Response::ok(UptimeResponse::with_start_timestamp(
- data.timestamp.unix_timestamp() as u128,
- )),
- )
+ Response::ok(UptimeResponse::with_start_timestamp(
+ data.timestamp.unix_timestamp() as u128,
+ ))
}
/// # `PATCH /stats/uptime`
@@ -106,10 +98,7 @@ pub async fn patch_uptime(
log::trace!("retrieving connection from database pool");
let result = pool.get().await;
if result.is_err() {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Response::internal_server_error(),
- );
+ return Response::internal_server_error();
}
let connection = result.unwrap();
@@ -119,24 +108,15 @@ pub async fn patch_uptime(
let Ok(timestamp) = OffsetDateTime::from_unix_timestamp(query.start_timestamp() as i64) else {
// FIXME: return a better status code as the timestamp is out of range if this branch is reached
// just 500 for now
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Response::internal_server_error(),
- );
+ return Response::internal_server_error();
};
let result = start_timestamp_upsert()
.bind(client, &query.component_name(), ×tamp)
.await;
if result.is_err() {
- return (
- StatusCode::INTERNAL_SERVER_ERROR,
- Response::internal_server_error(),
- );
+ return Response::internal_server_error();
}
- (
- StatusCode::OK,
- Response::ok(()),
- )
+ Response::ok(())
}