mod auth; use actix_cors::Cors; use actix_files::Files; use actix_web::{ get, http::header, web, App, HttpResponse, HttpServer, Responder, }; use dotenv::dotenv; use env_logger::{Builder, Env}; use sqlx::postgres::PgPoolOptions; use rust_decimal::Decimal; use bigdecimal::BigDecimal; use serde_json::json; use std::collections::HashMap; use std::convert::From; #[derive(sqlx::FromRow, serde::Serialize)] struct Book { id: i32, tytul: String, autor: String, cena: BigDecimal, obraz_url: Option, opis: Option, } #[get("/api/ksiazki")] async fn get_ksiazki( pool: web::Data, web::Query(params): web::Query>, ) -> impl Responder { let search_term = params.get("search").map(|s| s.as_str()).unwrap_or(""); let sort_by = params.get("sort").map(|s| s.as_str()).unwrap_or("default"); let base_query = "SELECT id, tytul, autor, cena, COALESCE('/images/' || obraz_url, '/images/placeholder.jpg') as obraz_url, COALESCE(opis, 'Brak opisu') as opis FROM ksiazki"; let sort_clause = match sort_by { "price_asc" => " ORDER BY cena ASC", "price_desc" => " ORDER BY cena DESC", "title_asc" => " ORDER BY tytul ASC", "author_asc" => " ORDER BY autor ASC", _ => "" }; let query = if !search_term.is_empty() { format!( "{} WHERE LOWER(tytul) LIKE LOWER($1) OR LOWER(autor) LIKE LOWER($1){}", base_query, sort_clause ) } else { format!("{}{}", base_query, sort_clause) }; let mut query_builder = sqlx::query_as::<_, Book>(&query); if !search_term.is_empty() { query_builder = query_builder.bind(format!("%{}%", search_term)); } match query_builder.fetch_all(pool.get_ref()).await { Ok(books) => HttpResponse::Ok().json(books), Err(e) => { log::error!("Błąd bazy danych: {:?}", e); HttpResponse::InternalServerError().json(json!({"error": "Błąd serwera"})) } } } //#[get("/api/ksiazki")] //async fn get_ksiazki(pool: web::Data) -> impl Responder { // match sqlx::query_as!( // Book, // r#"SELECT id, tytul, autor, cena, obraz_url FROM ksiazki"# // ) // .fetch_all(pool.get_ref()) // .await // { // Ok(books) => HttpResponse::Ok().json(books), // Err(e) => { // log::error!("Błąd bazy danych: {:?}", e); // HttpResponse::InternalServerError().body(format!("Błąd: {}", e)) // } // } //} #[get("/api/ksiazki/{id}")] async fn get_ksiazka( pool: web::Data, path: web::Path, ) -> impl Responder { let id = path.into_inner(); match sqlx::query_as!( Book, r#" SELECT id, tytul, autor, cena, COALESCE('/images/' || obraz_url, '/images/placeholder.jpg') as obraz_url, COALESCE(opis, 'Brak opisu') as opis FROM ksiazki WHERE id = $1 "#, id ) .fetch_optional(pool.get_ref()) .await { Ok(Some(book)) => HttpResponse::Ok().json(book), Ok(None) => HttpResponse::NotFound().json(json!({"error": "Książka nie znaleziona"})), Err(e) => { log::error!("Błąd bazy danych: {}", e); HttpResponse::InternalServerError().json(json!({"error": "Błąd serwera"})) } } } #[get("/api/check-auth")] async fn check_auth() -> impl Responder { HttpResponse::Ok().json(json!({ "authenticated": false, "user": null })) } #[actix_web::main] async fn main() -> std::io::Result<()> { // Inicjalizacja loggera Builder::from_env(Env::default().default_filter_or("debug")).init(); // Ładowanie zmiennych środowiskowych dotenv().ok(); let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set in .env"); // Utwórz pulę połączeń let pool = PgPoolOptions::new() .max_connections(5) .connect(&database_url) .await .expect("Failed to create pool"); HttpServer::new(move || { let cors = Cors::default() .allow_any_origin() .allowed_methods(vec!["GET", "POST"]) .allowed_headers(vec![ header::CONTENT_TYPE, header::AUTHORIZATION, header::ACCEPT, header::HeaderName::from_static("content-type"), ]); App::new() .app_data(web::Data::new(pool.clone())) // Dodaj pulę jako globalny stan .wrap(cors) .wrap(actix_web::middleware::Logger::default()) .service(get_ksiazki) .service(get_ksiazka) .service(auth::rejestracja) .service(auth::login) .service( Files::new("/images", "./static/images") // Nowy endpoint dla obrazków .show_files_listing(), ) .service(Files::new("/", "./static").index_file("index.html")) }) .bind("0.0.0.0:7999")? .run() .await }