90 lines
2.8 KiB
Rust
90 lines
2.8 KiB
Rust
mod answer;
|
|
mod api;
|
|
mod question;
|
|
mod json_store;
|
|
use axum::{
|
|
extract::{Path, Query, State},
|
|
http::{StatusCode, Uri},
|
|
response::{IntoResponse, Response},
|
|
routing::{delete, get, post, put},
|
|
Form, Json, Router,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::{
|
|
self,
|
|
postgres::{PgPool, Postgres},
|
|
Pool,
|
|
};
|
|
use std::{
|
|
collections::HashMap,
|
|
env::var,
|
|
fs::File,
|
|
io::{ErrorKind, Read, Seek, Write},
|
|
net::SocketAddr,
|
|
path::PathBuf,
|
|
sync::Arc,
|
|
};
|
|
use json_store::Store;
|
|
use tokio::sync::RwLock;
|
|
|
|
// generic handler for any not supported route/method combination
|
|
async fn handler_404() -> 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(),
|
|
}
|
|
}
|
|
|
|
async fn db_connection() -> Result<Pool<Postgres>, sqlx::Error> {
|
|
let url: String = format!(
|
|
"postgres://{}:{}@{}:5432"/*{}*/,
|
|
var("POSTGRES_USER").unwrap(),
|
|
var("POSTGRES_PASSWORD").unwrap(),
|
|
var("POSTGRES_HOST").unwrap(),
|
|
// var("POSTGRES_DBNAME").unwrap()
|
|
);
|
|
PgPool::connect(&url).await
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let store: Arc<RwLock<Store>> = Arc::new(RwLock::new(json_store::Store::new()));
|
|
let ip: SocketAddr = SocketAddr::new([127, 0, 0, 1].into(), 3000);
|
|
let listener: tokio::net::TcpListener = tokio::net::TcpListener::bind(ip).await.unwrap();
|
|
let apis = Router::new()
|
|
.route("/question/:id", get(api::read_question))
|
|
.route("/questions", get(api::read_questions))
|
|
.route("/question", post(api::create_question))
|
|
.route("/question", put(api::update_question))
|
|
.route("/question/:id", delete(api::delete_question))
|
|
.route("/answer", post(api::create_answer))
|
|
.with_state(store);
|
|
let app = Router::new()
|
|
.nest("/api/v1", apis)
|
|
.route("/", get(serve_file))
|
|
.route("/*path", get(serve_file))
|
|
.fallback(handler_404);
|
|
let db: Pool<Postgres> = db_connection().await.unwrap();
|
|
sqlx::migrate!().run(&db).await.unwrap();
|
|
axum::serve(listener, app).await.unwrap();
|
|
}
|