use std::env; use axum::{ routing::{get, post}, http::StatusCode, Json, Router, }; use axum::extract::{Path, State}; use dotenv::dotenv; use env_logger::fmt::style::Reset; use http::{header, Method}; use common::models::article::{insert_article, load_article, load_articles, Article}; use common::models::origin::Origin; use sqlx::{ postgres::PgPoolOptions, PgPool }; use tower_http::cors::{AllowOrigin, Any, CorsLayer}; use tracing::info; use http::header::{AUTHORIZATION, ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE, ACCESS_CONTROL_ALLOW_CREDENTIALS}; use common::models::article; use common::models::variant::{insert_variant, Variant}; #[tokio::main] async fn main() { dotenv().ok(); let server_address = env::var("SERVER_ADDRESS").expect("SERVER_ADDRESS is not set in .env file"); let server_port = env::var("SERVER_PORT").expect("SERVER_PORT is not set in .env file"); let database = env::var("DATABASE_URL").expect("DATABASE is not set in .env file"); let addr = format!("{}:{}", server_address, server_port); // initialize tracing tracing_subscriber::fmt().init(); let pool = PgPoolOptions::new() .max_connections(50) .connect(&database) .await .expect(&format!("Can't connect to database: {}", &database)); sqlx::migrate!().run(&pool).await.expect("Migrations failed"); let cors = CorsLayer::new() .allow_methods([Method::GET, Method::POST]) .allow_headers([ACCEPT, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE]) .allow_origin(AllowOrigin::any()); // build our application with a route let app = Router::new() .route("/articles", get(get_articles)) .route("/article", post(create_article)) .route("/article/{id}", get(get_article)) .route("/article/{id}/variant", post(add_variant)) .with_state(pool) .layer(cors); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); info!("Server started on: {}", &addr); axum::serve(listener, app).await.unwrap(); } #[tracing::instrument(ret)] async fn get_articles( State(pool): State, ) -> (StatusCode, Json>) { info!("Get all articles"); let result_articles: Option> = load_articles(pool).await; match result_articles { Some(articles) => { (StatusCode::OK, Json(articles)) } None => { (StatusCode::INTERNAL_SERVER_ERROR, Json(vec![])) } } } #[tracing::instrument(ret)] async fn get_article( Path(id): Path, State(pool): State, ) -> (StatusCode, Json
) { info!("Get article with id: {}", id); let result_article: Option
= load_article(id, pool).await; match result_article { Some(article) => { (StatusCode::OK, Json(article)) } None => { let article = Article { id, name: "".to_string(), bio: false, origin: Origin::EU, description: None, ingredients: None, variants: vec![], }; (StatusCode::INTERNAL_SERVER_ERROR, Json(article)) } } } #[tracing::instrument(ret)] async fn create_article( State(pool): State, Json(payload): Json
, ) -> (StatusCode, Json
) { info!("Test"); let article = Article { id: payload.id, name: payload.name, bio: payload.bio, origin: payload.origin, description: payload.description, ingredients: payload.ingredients, variants: payload.variants, }; insert_article(&article, pool).await; (StatusCode::CREATED, Json(article)) } async fn add_variant( State(pool): State, Path(article_id): Path, Json(payload): Json, ) -> (StatusCode, Json) { info!("Add variant"); let created_variant = insert_variant(article_id, &payload, pool).await; match created_variant { Some(variant) => { (StatusCode::OK, Json(variant)) } None => { let variant = Variant { id: 0, weight: "".to_string(), ean: None, name: None, description: None, ingredients: None, }; (StatusCode::INTERNAL_SERVER_ERROR, Json(variant)) } } }