support static routes
This commit is contained in:
parent
167d34731c
commit
fe2311746b
@ -64,7 +64,6 @@ A test dataset of questions. Generated by [ChatGPT](https://chat.openai.com/)
|
|||||||
### Looking ahead
|
### Looking ahead
|
||||||
These are a few things still to be added
|
These are a few things still to be added
|
||||||
#### Higher priority
|
#### Higher priority
|
||||||
* Support POST /answers
|
|
||||||
* Add some simple API curl examples to this README
|
* Add some simple API curl examples to this README
|
||||||
* API documentation tooling (`utoipa`)
|
* API documentation tooling (`utoipa`)
|
||||||
* API testing tooling (`swagger`)
|
* API testing tooling (`swagger`)
|
||||||
@ -74,7 +73,6 @@ These are a few things still to be added
|
|||||||
* Optimize flush/file writing: Write out the JSON in a pretty structure and avoid re-writing the whole file in cases when it can be avoid (like adding 1 item)
|
* Optimize flush/file writing: Write out the JSON in a pretty structure and avoid re-writing the whole file in cases when it can be avoid (like adding 1 item)
|
||||||
* Use a database alltogether instead of a json file for persistance
|
* Use a database alltogether instead of a json file for persistance
|
||||||
* Serve basic web page(s) to utilize all APIs
|
* Serve basic web page(s) to utilize all APIs
|
||||||
* Re-structure the application to serve API endpoint from an `api` directory, and generically serve web content from a `web` directory
|
|
||||||
* Optimize the put/update handler to support path or body identification, and to update only individual fields passed.
|
* Optimize the put/update handler to support path or body identification, and to update only individual fields passed.
|
||||||
* Host application on my raspberry pi on the internet
|
* Host application on my raspberry pi on the internet
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ pub async fn delete_question(
|
|||||||
Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
|
Err(e) => (StatusCode::NOT_FOUND, e).into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an Answer
|
/// Create an Answer
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
/// `answer_dto` Form URL encoded answer DTO
|
/// `answer_dto` Form URL encoded answer DTO
|
||||||
@ -100,7 +101,6 @@ pub async fn delete_question(
|
|||||||
/// Status Created 201 and the created answer upon success
|
/// Status Created 201 and the created answer upon success
|
||||||
/// Status Conflict 409 and message if answer with ID already exists
|
/// Status Conflict 409 and message if answer with ID already exists
|
||||||
/// Status Unprocessable Entity 422 is returned (implicitlly) for a malformed body
|
/// Status Unprocessable Entity 422 is returned (implicitlly) for a malformed body
|
||||||
|
|
||||||
pub async fn create_answer(
|
pub async fn create_answer(
|
||||||
State(store): State<Arc<RwLock<Store>>>,
|
State(store): State<Arc<RwLock<Store>>>,
|
||||||
Form(answer_dto): Form<AnswerDTO>,
|
Form(answer_dto): Form<AnswerDTO>,
|
||||||
|
40
src/main.rs
40
src/main.rs
@ -4,7 +4,7 @@ mod question;
|
|||||||
mod store;
|
mod store;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Query, State},
|
extract::{Path, Query, State},
|
||||||
http::StatusCode,
|
http::{StatusCode, Uri},
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
routing::{delete, get, post, put},
|
routing::{delete, get, post, put},
|
||||||
Form, Json, Router,
|
Form, Json, Router,
|
||||||
@ -13,22 +13,40 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{ErrorKind, Seek, Write},
|
io::{ErrorKind, Read, Seek, Write},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use store::Store;
|
use store::Store;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
async fn handle() -> Response {
|
// generic handler for any not supported route/method combination
|
||||||
(StatusCode::OK, "Visiting the root").into_response()
|
|
||||||
}
|
|
||||||
|
|
||||||
//generic handler for any not supported route/method combination
|
|
||||||
async fn handler_404() -> Response {
|
async fn handler_404() -> Response {
|
||||||
(StatusCode::NOT_FOUND, "404 Not Found").into_response()
|
(StatusCode::NOT_FOUND, "404 Not Found").into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generic handler to serve static for non-api routes
|
||||||
|
async fn serve_file(uri: Uri) -> impl IntoResponse {
|
||||||
|
let mut path_buf: PathBuf = PathBuf::from("./src/static/").join(uri.path().trim_start_matches('/'));
|
||||||
|
if path_buf.is_dir() {
|
||||||
|
path_buf.push("index.html")
|
||||||
|
}
|
||||||
|
match File::open(path_buf) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
let mut contents = Vec::new();
|
||||||
|
match file.read_to_end(&mut contents) {
|
||||||
|
Ok(_) => axum::response::Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.body(axum::body::Body::from(contents))
|
||||||
|
.unwrap(),
|
||||||
|
Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Server Error").into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => (StatusCode::NOT_FOUND, "404 Not Found").into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let store: Arc<RwLock<Store>> = Arc::new(RwLock::new(store::Store::new()));
|
let store: Arc<RwLock<Store>> = Arc::new(RwLock::new(store::Store::new()));
|
||||||
@ -41,9 +59,11 @@ async fn main() {
|
|||||||
.route("/question", put(api::update_question))
|
.route("/question", put(api::update_question))
|
||||||
.route("/question/:id", delete(api::delete_question))
|
.route("/question/:id", delete(api::delete_question))
|
||||||
.route("/answer", post(api::create_answer))
|
.route("/answer", post(api::create_answer))
|
||||||
.with_state(store)
|
.with_state(store);
|
||||||
|
let app = Router::new()
|
||||||
|
.nest("/api/v1", apis)
|
||||||
|
.route("/", get(serve_file))
|
||||||
|
.route("/*path", get(serve_file))
|
||||||
.fallback(handler_404);
|
.fallback(handler_404);
|
||||||
|
|
||||||
let app = Router::new().route("/", get(handle)).merge(apis);
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
}
|
}
|
||||||
|
8
src/static/cats/index.html
Normal file
8
src/static/cats/index.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Welcome again</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Hello cats!
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
src/static/cats/warhol.jpg
Executable file
BIN
src/static/cats/warhol.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
8
src/static/index.html
Normal file
8
src/static/index.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Welcome</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Hello world!
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user