webapi, inital schema and routes (get_users)
provides the infrastructure for the webapi including setting up the server and the routes. Implements get_users as a test route
This commit is contained in:
parent
1400b2fc95
commit
92763fd7dc
7 changed files with 197 additions and 0 deletions
92
koucha/Cargo.lock
generated
92
koucha/Cargo.lock
generated
|
|
@ -73,6 +73,70 @@ dependencies = [
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||||
|
dependencies = [
|
||||||
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
|
"bytes",
|
||||||
|
"form_urlencoded",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"itoa",
|
||||||
|
"matchit",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"serde_core",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-core"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"mime",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-macros"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
|
@ -726,6 +790,12 @@ version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
|
|
@ -740,6 +810,7 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
|
@ -999,9 +1070,12 @@ dependencies = [
|
||||||
name = "koucha"
|
name = "koucha"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rss",
|
"rss",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
@ -1076,6 +1150,12 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
|
@ -1708,6 +1788,17 @@ dependencies = [
|
||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|
@ -2221,6 +2312,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,6 @@ rss = "2.0.12"
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
tokio = { version = "1.49.0", features = ["full"] }
|
||||||
sqlx = { version = "0.8.6", features = [ "runtime-tokio", "sqlite" ] }
|
sqlx = { version = "0.8.6", features = [ "runtime-tokio", "sqlite" ] }
|
||||||
chrono = "0.4.43"
|
chrono = "0.4.43"
|
||||||
|
axum = { version= "0.8.8", features = [ "macros" ] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
|
|
||||||
25
koucha/src/bin/webapi/main.rs
Normal file
25
koucha/src/bin/webapi/main.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use koucha::{
|
||||||
|
AdapterBuilder,
|
||||||
|
Adapter,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod routes;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct AppState {
|
||||||
|
pub adapter: Adapter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let db_url = std::env::var("DATABASE_URL").unwrap();
|
||||||
|
let adapter = AdapterBuilder::new()
|
||||||
|
.database_url(&db_url)
|
||||||
|
.create().await.unwrap();
|
||||||
|
|
||||||
|
let app = routes::router().with_state(AppState { adapter });
|
||||||
|
|
||||||
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:4142").await.unwrap();
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
}
|
||||||
36
koucha/src/bin/webapi/routes/get_users.rs
Normal file
36
koucha/src/bin/webapi/routes/get_users.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use axum::{Json, extract::State};
|
||||||
|
use koucha::db::User as DbUser;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
AppState, routes::{ApiError, ApiResult, ApiResponse}, types::User
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Input { }
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Output {
|
||||||
|
users: Vec<User>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(_body): Json<Input>,
|
||||||
|
) -> ApiResult<Output> {
|
||||||
|
let dbusers = DbUser::get_all(state.adapter.get_pool())
|
||||||
|
.await.map_err(|_e| {
|
||||||
|
// TODO: Logging
|
||||||
|
ApiError {
|
||||||
|
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
error: "InternalError",
|
||||||
|
message: "Error getting all users from DB.".to_string(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let users: Vec<User> = dbusers.iter().map(|u| User {
|
||||||
|
name: u.name().to_string(),
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
Ok(ApiResponse(StatusCode::OK, Output { users }))
|
||||||
|
}
|
||||||
35
koucha/src/bin/webapi/routes/mod.rs
Normal file
35
koucha/src/bin/webapi/routes/mod.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use axum::{Json, Router, response::IntoResponse, routing::get};
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
mod get_users;
|
||||||
|
|
||||||
|
use crate::AppState;
|
||||||
|
|
||||||
|
pub type ApiResult<T> = Result<ApiResponse<T>, ApiError>;
|
||||||
|
|
||||||
|
pub struct ApiResponse<T>(pub StatusCode, pub T);
|
||||||
|
|
||||||
|
pub struct ApiError {
|
||||||
|
pub status: StatusCode,
|
||||||
|
pub error: &'static str,
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn router() -> Router<AppState> {
|
||||||
|
Router::new()
|
||||||
|
.route("/get_users", get(get_users::handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for ApiError {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
let body = json!({ "error": self.error, "message": self.message });
|
||||||
|
(self.status, Json(body)).into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Serialize> IntoResponse for ApiResponse<T> {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
(self.0, Json(self.1)).into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
6
koucha/src/bin/webapi/types.rs
Normal file
6
koucha/src/bin/webapi/types.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct User {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue