use std::sync::Arc; use axum::{extract::State, http::HeaderMap, routing::get}; use reqwest::{header::USER_AGENT}; use sea_orm::{ConnectionTrait, Database, DatabaseConnection, EntityTrait, PaginatorTrait, Schema}; use utoipa::openapi::{ContactBuilder, InfoBuilder, LicenseBuilder}; use utoipa_axum::router::OpenApiRouter; use utoipa_redoc::{Redoc, Servable}; use utoipa_swagger_ui::{Config, SwaggerUi}; use utoipa_axum::routes; use crate::entities::prelude::BookInstance; pub mod entities; pub mod utils; pub mod routes; pub struct AppState { app_name: String, db_conn: Arc, web_client: reqwest::Client } async fn index( State(state): State> ) ->String { let app_name = &state.app_name; let db_conn = &state.db_conn; let status: &str = match db_conn.ping().await { Ok(_) => "working", Err(_) => "erroring" }; let book_count = BookInstance::find().count(db_conn.as_ref()).await.unwrap(); format!("Hello from {app_name}! Database is {status}. We currently have {book_count} books in stock !") } #[tokio::main] async fn main() { let db: Arc = Arc::new(Database::connect(String::from("sqlite://./alexandria.db?mode=rwc")).await.unwrap()); let builder = db.get_database_backend(); let schema = Schema::new(builder); if let Err(err) = db.execute(builder.build(schema.create_table_from_entity(crate::entities::prelude::Book).if_not_exists())).await { println!("Error while creating book table: {err:?}"); return; } if let Err(err) = db.execute(builder.build(schema.create_table_from_entity(crate::entities::prelude::BookInstance).if_not_exists())).await { println!("Error while creating book_instance table: {err:?}"); return; } if let Err(err) = db.execute(builder.build(schema.create_table_from_entity(crate::entities::prelude::Owner).if_not_exists())).await { println!("Error while creating owner table: {err:?}"); return; } let mut default_headers = HeaderMap::new(); default_headers.append(USER_AGENT, "Alexandria/1.0 (unionetudianteauvergne@gmail.com)".parse().unwrap()); let shared_state = Arc::new(AppState { app_name: "Alexandria".to_string(), db_conn: db, web_client: reqwest::Client::builder().default_headers(default_headers).build().expect("creating the reqwest client failed") }); let (router, mut api) = OpenApiRouter::new() .routes(routes!(routes::book::get_book_by_ean)) .routes(routes!(routes::book::get_book_by_id)) .routes(routes!(routes::book_instance::get_book_instance_by_id)) .routes(routes!(routes::book_instance::create_book_instance)) .routes(routes!(routes::owner::get_owner_by_id)) .routes(routes!(routes::owner::create_owner)) .route("/", get(index)) .with_state(shared_state) .split_for_parts(); api.info = InfoBuilder::new() .title("Alexandria") .description(Some("Alexandria is a server that manages books and users for Union Étudiante's book exchange")) .contact(Some(ContactBuilder::new() .url(Some("https://ueauvergne.fr")) .name(Some("Union Étudiante Auvergne")) .email(Some("unionetudianteauvergne@gmail.com")) .build())) .license(Some(LicenseBuilder::new().name("MIT").url(Some("https://spdx.org/licenses/MIT.html")).build())) .version("1.0.0") .build(); let redoc = Redoc::with_url("/docs/", api.clone()); let swagger = SwaggerUi::new("/docs2/") .url("/docs2/openapi.json", api) .config(Config::default() .try_it_out_enabled(true) ); let router = router.merge(redoc); let router = router.merge(swagger); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); axum::serve(listener, router).await.unwrap(); }