diff --git a/.gitignore b/.gitignore index 0163ac2..cd53400 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # my gitignore todo.md .env -compose.yaml # ---> Rust # Generated by Cargo diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..52922f9 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,9 @@ +services: + database: + image: docker.io/postgres:17 + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + ports: + - ${POSTGRES_PORT}:${POSTGRES_PORT} diff --git a/example.env b/example.env new file mode 100644 index 0000000..f46085b --- /dev/null +++ b/example.env @@ -0,0 +1,6 @@ +POSTGRES_DB=server-db +POSTGRES_USER=axum-user +POSTGRES_PASSWORD=my-cool-pass +POSTGRES_PORT=5432 +POSTGRES_HOST=localhost +DATABASE_URL=postgres://axum-user:my-cool-pass@localhost:5432/server-db diff --git a/src/main.rs b/src/main.rs index f059042..40a959f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,10 @@ use axum::{ Json, extract::{Query, State}, response::IntoResponse, - routing::{delete, get, post}, + routing::{get, post}, }; use sqlx::PgPool; -use std::fs; +use std::{env, fs}; use tokio::net::TcpListener; #[derive(Clone)] @@ -51,7 +51,17 @@ pub struct GetGameQuery { fn database_url() -> String { let _ = dotenvy::dotenv(); - let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL should be set"); + let database_url = format!( + "postgres://{}:{}@{}:{}/{}", + env::var("POSTGRES_USER").unwrap(), + env::var("POSTGRES_PASSWORD").unwrap(), + env::var("POSTGRES_HOST").unwrap(), + env::var("POSTGRES_PORT").unwrap(), + env::var("POSTGRES_DB").unwrap(), + ); + if database_url != env::var("DATABASE_URL").unwrap() { + panic!("DATABASE_URL is not set correctly"); + } println!("DATABASE_URL={database_url}"); database_url } @@ -68,7 +78,7 @@ async fn create_table(pool: &PgPool) { fn router(state: AppState) -> axum::Router { axum::Router::new() - .route("/api/games", get(games_list).post(add_game)) + .route("/api/games", get(games_list).post(insert_game)) .route("/api/games/{id}", post(insert_game).delete(delete_game)) .with_state(state) } @@ -89,10 +99,7 @@ async fn main() { axum::serve(listener, router(state)).await.unwrap(); } -async fn games_list( - State(state): State, - Query(query): Query, -) -> impl IntoResponse { +async fn games_list(State(state): State) -> impl IntoResponse { let games: Vec = sqlx::query_as!(Game, "SELECT * FROM games") .fetch_all(&state.pool) .await @@ -101,63 +108,40 @@ async fn games_list( Json(games) } -async fn add_game( - State(state): State, - Json(game): Json, -) -> impl IntoResponse { - let result = sqlx::query!( - "INSERT INTO games (name, publishing_house, developer, description, multiplayer, is_free_to_play) - VALUES ($1, $2, $3, $4, $5, $6)", - game.name, - game.publishing_house, - game.developer, - game.description, - game.multiplayer, - game.is_free_to_play, - ) - .execute(&state.pool) - .await; - - match result { - Ok(_) => axum::http::StatusCode::CREATED, - Err(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR, - } -} - async fn insert_game( State(state): State, + Query(query): Query, Json(game): Json, ) -> impl IntoResponse { - sqlx::query!( - "INSERT INTO games(name, publishing_house, developer, description, multiplayer, is_free_to_play) VALUES ($1, $2, $3, $4, $5, $6)", - game.name, - game.publishing_house, - game.developer, - game.description, - game.multiplayer, - game.is_free_to_play, - ) - .execute(&state.pool) - .await - .unwrap(); + let sql_up = if query.id.is_some() { + "UPDATE games SET name=$1, publishing_house=$2, developer=$3, description=$4, multiplayer=$5, is_free_to_play=$6 WHERE id=$7" + } else { + "INSERT INTO games(name, publishing_house, developer, description, multiplayer, is_free_to_play) VALUES ($1, $2, $3, $4, $5, $6)" + }; + + let mut sql_query = sqlx::query(sql_up) + .bind(game.name) + .bind(game.publishing_house) + .bind(game.developer) + .bind(game.description) + .bind(game.multiplayer) + .bind(game.is_free_to_play); + + if let Some(id) = query.id { + sql_query = sql_query.bind(id); + } + sql_query.execute(&state.pool).await.unwrap(); + + axum::http::StatusCode::CREATED +} + +use axum::extract::Path; + +async fn delete_game(State(state): State, Path(id): Path) -> impl IntoResponse { + sqlx::query!("DELETE FROM games WHERE id = $1", id) + .execute(&state.pool) + .await + .unwrap(); axum::http::StatusCode::OK } - -async fn delete_game( - State(state): State, - Query(query): Query, -) -> impl IntoResponse { - if let Some(id) = query.id { - let result = sqlx::query!("DELETE FROM games WHERE id = $1", id) - .execute(&state.pool) - .await; - - match result { - Ok(_) => axum::http::StatusCode::OK, - Err(_) => axum::http::StatusCode::INTERNAL_SERVER_ERROR, - } - } else { - axum::http::StatusCode::BAD_REQUEST - } -}