added nested routes, added separate config struct, among other items
This commit is contained in:
		
							
								
								
									
										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; | ||||
| 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; | ||||
| 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::{ | ||||
|     routing::{get, post}, | ||||
|     Router, | ||||
| }; | ||||
| use sqlx::postgres::PgPoolOptions; | ||||
| use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; | ||||
|  | ||||
| use axum::Router; | ||||
| use config::config; | ||||
| use sqlx::{postgres::PgPoolOptions, PgPool}; | ||||
| use std::time::Duration; | ||||
| use tokio::net::TcpListener; | ||||
| use tokio::signal; | ||||
| use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; | ||||
|  | ||||
| mod config; | ||||
| mod datasources; | ||||
| mod routes; | ||||
|  | ||||
| pub struct AppState { | ||||
|     db: PgPool, | ||||
| } | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     let _ = dotenvy::dotenv(); | ||||
|     let _ = config(); | ||||
|     tracing_subscriber::registry() | ||||
|         .with( | ||||
|             tracing_subscriber::EnvFilter::try_from_default_env() | ||||
|                 .unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()), | ||||
|             tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { | ||||
|                 format!( | ||||
|                     "{}=debug,tower_http=debug,axum=trace", | ||||
|                     env!("CARGO_CRATE_NAME") | ||||
|                 ) | ||||
|                 .into() | ||||
|             }), | ||||
|         ) | ||||
|         .with(tracing_subscriber::fmt::layer()) | ||||
|         .init(); | ||||
|  | ||||
|     let db_connection_str = std::env::var("DATABASE_URL") | ||||
|         .unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string()); | ||||
|     let db_connection_str = std::env::var("DATABASE_URL").unwrap(); | ||||
|     // .unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string()); | ||||
|  | ||||
|     // set up connection pool | ||||
|     let pool = PgPoolOptions::new() | ||||
| @@ -31,16 +39,48 @@ async fn main() { | ||||
|         .acquire_timeout(Duration::from_secs(3)) | ||||
|         .connect(&db_connection_str) | ||||
|         .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 | ||||
|     let app = Router::new() | ||||
|         .route("/", get(routes::root::RootRoute::root)) | ||||
|         .fallback(routes::root::RootRoute::not_found) | ||||
|         .with_state(pool); | ||||
|         .nest("/", routes::root::RootRoute::routes()) | ||||
|         .nest("/posts", routes::posts::PostsRoute::routes(&app_state)) | ||||
|         .nest( | ||||
|             "/comments", | ||||
|             routes::comments::CommentsRoute::routes(&app_state), | ||||
|         ); | ||||
|  | ||||
|     // run it with hyper | ||||
|     let listener = TcpListener::bind("0.0.0.0:3000").await.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; | ||||
| 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; | ||||
| 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::{ | ||||
|     http::StatusCode, | ||||
|     response::{Html, IntoResponse}, | ||||
|     routing::get, | ||||
|     Router, | ||||
| }; | ||||
|  | ||||
| pub struct 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>") | ||||
|     } | ||||
|  | ||||
|     pub async fn not_found() -> impl IntoResponse { | ||||
|     async fn not_found() -> impl IntoResponse { | ||||
|         (StatusCode::NOT_FOUND, "¯\\_(ツ)_/¯") | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user