2025-05-23 15:25:48 +02:00
|
|
|
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;
|
2025-05-24 16:47:32 +02:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::convert::From;
|
2025-05-23 15:25:48 +02:00
|
|
|
|
|
|
|
#[derive(sqlx::FromRow, serde::Serialize)]
|
|
|
|
struct Book {
|
|
|
|
id: i32,
|
|
|
|
tytul: String,
|
|
|
|
autor: String,
|
|
|
|
cena: BigDecimal,
|
2025-05-24 16:47:32 +02:00
|
|
|
obraz_url: Option<String>,
|
|
|
|
opis: Option<String>,
|
2025-05-23 15:25:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/api/ksiazki")]
|
2025-05-24 16:47:32 +02:00
|
|
|
async fn get_ksiazki(
|
|
|
|
pool: web::Data<sqlx::PgPool>,
|
|
|
|
web::Query(params): web::Query<HashMap<String, String>>,
|
|
|
|
) -> 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<sqlx::PgPool>) -> 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<sqlx::PgPool>,
|
|
|
|
path: web::Path<i32>,
|
|
|
|
) -> impl Responder {
|
|
|
|
let id = path.into_inner();
|
|
|
|
|
2025-05-23 15:25:48 +02:00
|
|
|
match sqlx::query_as!(
|
|
|
|
Book,
|
2025-05-24 16:47:32 +02:00
|
|
|
r#"
|
|
|
|
SELECT
|
|
|
|
id,
|
|
|
|
tytul,
|
|
|
|
autor,
|
|
|
|
cena,
|
|
|
|
COALESCE('/images/' || obraz_url, '/images/placeholder.jpg') as obraz_url,
|
2025-05-24 19:23:52 +02:00
|
|
|
COALESCE(opis, 'Brak opisu') as opis
|
2025-05-24 16:47:32 +02:00
|
|
|
FROM ksiazki
|
|
|
|
WHERE id = $1
|
|
|
|
"#,
|
|
|
|
id
|
2025-05-23 15:25:48 +02:00
|
|
|
)
|
2025-05-24 16:47:32 +02:00
|
|
|
.fetch_optional(pool.get_ref())
|
2025-05-23 15:25:48 +02:00
|
|
|
.await
|
|
|
|
{
|
2025-05-24 16:47:32 +02:00
|
|
|
Ok(Some(book)) => HttpResponse::Ok().json(book),
|
2025-05-24 19:23:52 +02:00
|
|
|
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"}))
|
|
|
|
}
|
2025-05-23 15:25:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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)
|
2025-05-24 19:23:52 +02:00
|
|
|
.service(get_ksiazka)
|
2025-05-23 15:25:48 +02:00
|
|
|
.service(auth::rejestracja)
|
|
|
|
.service(auth::login)
|
2025-05-24 16:47:32 +02:00
|
|
|
.service(
|
|
|
|
Files::new("/images", "./static/images") // Nowy endpoint dla obrazków
|
|
|
|
.show_files_listing(),
|
|
|
|
)
|
2025-05-23 15:25:48 +02:00
|
|
|
.service(Files::new("/", "./static").index_file("index.html"))
|
|
|
|
})
|
|
|
|
.bind("0.0.0.0:7999")?
|
|
|
|
.run()
|
|
|
|
.await
|
|
|
|
}
|