added nested routes, added separate config struct, among other items
This commit is contained in:
parent
cf199ab48a
commit
845f058568
11
backend/public/src/config.rs
Normal file
11
backend/public/src/config.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn config() -> Configuration {
|
||||||
|
Configuration {
|
||||||
|
env: dotenvy::dotenv(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Configuration {
|
||||||
|
env: Result<PathBuf, dotenvy::Error>,
|
||||||
|
}
|
@ -1,2 +1,8 @@
|
|||||||
|
use crate::routes::comments::CommentInput;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
pub struct CommentsDatasource;
|
pub struct CommentsDatasource;
|
||||||
impl CommentsDatasource {}
|
impl CommentsDatasource {
|
||||||
|
pub async fn get_posts_comments(pool: PgPool) {}
|
||||||
|
pub async fn insert_comment(pool: PgPool, comment_input: CommentInput) {}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,11 @@
|
|||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
pub struct PostsDatasource;
|
pub struct PostsDatasource;
|
||||||
impl PostsDatasource {}
|
impl PostsDatasource {
|
||||||
|
pub async fn get_all(pool: PgPool) {
|
||||||
|
sqlx::query("SELECT title, body, created_at FROM posts ORDER BY created_at DESC LIMIT 10")
|
||||||
|
.fetch_all(&pool)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
pub async fn get_one(pool: PgPool) {}
|
||||||
|
}
|
||||||
|
@ -1,29 +1,37 @@
|
|||||||
use axum::{
|
use axum::Router;
|
||||||
routing::{get, post},
|
use config::config;
|
||||||
Router,
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||||
};
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
use tokio::signal;
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
mod config;
|
||||||
mod datasources;
|
mod datasources;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
db: PgPool,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let _ = dotenvy::dotenv();
|
let _ = config();
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(
|
.with(
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||||
.unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()),
|
format!(
|
||||||
|
"{}=debug,tower_http=debug,axum=trace",
|
||||||
|
env!("CARGO_CRATE_NAME")
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let db_connection_str = std::env::var("DATABASE_URL")
|
let db_connection_str = std::env::var("DATABASE_URL").unwrap();
|
||||||
.unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string());
|
// .unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string());
|
||||||
|
|
||||||
// set up connection pool
|
// set up connection pool
|
||||||
let pool = PgPoolOptions::new()
|
let pool = PgPoolOptions::new()
|
||||||
@ -31,16 +39,48 @@ async fn main() {
|
|||||||
.acquire_timeout(Duration::from_secs(3))
|
.acquire_timeout(Duration::from_secs(3))
|
||||||
.connect(&db_connection_str)
|
.connect(&db_connection_str)
|
||||||
.await
|
.await
|
||||||
.expect("can't connect to database");
|
.expect("Failed to connect to database");
|
||||||
|
|
||||||
|
let app_state = AppState { db: pool.clone() };
|
||||||
|
|
||||||
// build our application with some routes
|
// build our application with some routes
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(routes::root::RootRoute::root))
|
.nest("/", routes::root::RootRoute::routes())
|
||||||
.fallback(routes::root::RootRoute::not_found)
|
.nest("/posts", routes::posts::PostsRoute::routes(&app_state))
|
||||||
.with_state(pool);
|
.nest(
|
||||||
|
"/comments",
|
||||||
|
routes::comments::CommentsRoute::routes(&app_state),
|
||||||
|
);
|
||||||
|
|
||||||
// run it with hyper
|
// run it with hyper
|
||||||
let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||||
tracing::debug!("listening on {}", listener.local_addr().unwrap());
|
tracing::debug!("listening on {}", listener.local_addr().unwrap());
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app)
|
||||||
|
.with_graceful_shutdown(shutdown_signal())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shutdown_signal() {
|
||||||
|
let ctrl_c = async {
|
||||||
|
signal::ctrl_c()
|
||||||
|
.await
|
||||||
|
.expect("Failed to install Ctrl+C handler");
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let terminate = async {
|
||||||
|
signal::unix::signal(signal::unix::SignalKind::terminate())
|
||||||
|
.expect("Failed to install signal handler")
|
||||||
|
.recv()
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let terminate = std::future::pending::<()>();
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
_ = ctrl_c => {},
|
||||||
|
_ = terminate => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,39 @@
|
|||||||
|
use crate::{datasources::comments::CommentsDatasource, AppState};
|
||||||
|
use axum::{
|
||||||
|
extract::{Form, State},
|
||||||
|
routing::{get, post},
|
||||||
|
Json,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct CommentInput {
|
||||||
|
name: String,
|
||||||
|
body: String,
|
||||||
|
post_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CommentsRoute;
|
pub struct CommentsRoute;
|
||||||
impl CommentsRoute {}
|
impl CommentsRoute {
|
||||||
|
pub fn routes(app_state: &AppState) -> axum::Router {
|
||||||
|
// add more comment routes here!
|
||||||
|
axum::Router::new()
|
||||||
|
.route("/post/:id", get(CommentsRoute::get_post_comments))
|
||||||
|
.route("/add", post(CommentsRoute::insert_comment))
|
||||||
|
.with_state(app_state.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_post_comments(State(pool): State<PgPool>) -> Json<()> {
|
||||||
|
let results = CommentsDatasource::get_posts_comments(pool).await;
|
||||||
|
Json {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert_comment(
|
||||||
|
State(pool): State<PgPool>,
|
||||||
|
Form(comment_input): Form<CommentInput>,
|
||||||
|
) -> bool {
|
||||||
|
let results = CommentsDatasource::insert_comment(pool, comment_input).await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,32 @@
|
|||||||
|
use crate::{datasources::posts::PostsDatasource, AppState};
|
||||||
|
use axum::{extract::State, routing::get, Json, Router};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
|
||||||
pub struct PostsRoute;
|
pub struct PostsRoute;
|
||||||
impl PostsRoute {}
|
impl PostsRoute {
|
||||||
|
pub fn routes(app_state: &AppState) -> Router {
|
||||||
|
// add more post routes here!
|
||||||
|
Router::new()
|
||||||
|
.route("/", get(PostsRoute::get_all))
|
||||||
|
.route("/:id", get(PostsRoute::get_one))
|
||||||
|
.with_state(app_state.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all posts
|
||||||
|
async fn get_all(State(pool): State<PgPool>) -> Json<()> {
|
||||||
|
let results = PostsDatasource::get_all(pool).await;
|
||||||
|
Json {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get one post
|
||||||
|
async fn get_one(State(pool): State<PgPool>) -> Json<()> {
|
||||||
|
let results = PostsDatasource::get_one(pool).await;
|
||||||
|
Json {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the top three posts with the highest view count
|
||||||
|
async fn get_popular_posts(State(pool): State<PgPool>) -> Json<()> {}
|
||||||
|
|
||||||
|
// get the top three posts with the most comments
|
||||||
|
async fn get_hot_posts(State(pool): State<PgPool>) -> Json<()> {}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
|
routing::get,
|
||||||
|
Router,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RootRoute;
|
pub struct RootRoute;
|
||||||
impl RootRoute {
|
impl RootRoute {
|
||||||
pub async fn root() -> Html<&'static str> {
|
pub fn routes() -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/", get(RootRoute::root))
|
||||||
|
.fallback(RootRoute::not_found)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn root() -> Html<&'static str> {
|
||||||
Html("<p>Copyright Wyatt J. Miller 2024</p>")
|
Html("<p>Copyright Wyatt J. Miller 2024</p>")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn not_found() -> impl IntoResponse {
|
async fn not_found() -> impl IntoResponse {
|
||||||
(StatusCode::NOT_FOUND, "¯\\_(ツ)_/¯")
|
(StatusCode::NOT_FOUND, "¯\\_(ツ)_/¯")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user