Compare commits
No commits in common. "2e0b038e2a867739968483dc2bb331775e759968" and "03ffeb10fcafb61408cebc9d74f77de131e393f4" have entirely different histories.
2e0b038e2a
...
03ffeb10fc
4 changed files with 56 additions and 133 deletions
|
@ -27,16 +27,11 @@ pub async fn get_items(
|
||||||
db: web::Data<Arc<Mutex<Database>>>,
|
db: web::Data<Arc<Mutex<Database>>>,
|
||||||
url: web::Query<String>,
|
url: web::Query<String>,
|
||||||
) -> HttpResponse {
|
) -> HttpResponse {
|
||||||
log!("[SERVER] Received request for URL: {}", url);
|
|
||||||
|
|
||||||
let db = db.lock().await;
|
let db = db.lock().await;
|
||||||
match db.get_items_by_url(&url).await {
|
match db.get_items_by_url(&url).await {
|
||||||
Ok(items) => {
|
Ok(items) => HttpResponse::Ok().json(items),
|
||||||
log!("[SERVER] Returning {} items for URL: {}", items.len(), url);
|
|
||||||
HttpResponse::Ok().json(items)
|
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log!("[SERVER ERROR] Failed to fetch items for {}: {:?}", url, err);
|
leptos::logging::error!("Failed to fetch items: {:?}", err);
|
||||||
HttpResponse::InternalServerError().body("Failed to fetch items")
|
HttpResponse::InternalServerError().body("Failed to fetch items")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,47 +19,24 @@ struct WikidataSuggestion {
|
||||||
|
|
||||||
//function to load items from database
|
//function to load items from database
|
||||||
pub async fn load_items_from_db(current_url: &str) -> Result<Vec<Item>, String> {
|
pub async fn load_items_from_db(current_url: &str) -> Result<Vec<Item>, String> {
|
||||||
//logging for the raw URL
|
|
||||||
log!("[DEBUG] Loading items for URL: {}", current_url);
|
|
||||||
|
|
||||||
let encoded_url = encode(¤t_url);
|
let encoded_url = encode(¤t_url);
|
||||||
let api_url = format!("/api/urls/{}/items", encoded_url);
|
let api_url = format!("/api/urls/{}/items", encoded_url);
|
||||||
|
|
||||||
// Log the constructed API URL
|
|
||||||
log!("[DEBUG] Making request to API endpoint: {}", api_url);
|
|
||||||
|
|
||||||
let response = gloo_net::http::Request::get(&api_url)
|
let response = gloo_net::http::Request::get(&api_url)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| format!("Failed to fetch items: {:?}", err))?;
|
||||||
log!("[ERROR] Network error: {:?}", err);
|
|
||||||
format!("Failed to fetch items: {:?}", err)
|
|
||||||
})?;
|
|
||||||
// Log response metadata
|
|
||||||
log!("[DEBUG] Received response - Status: {}", response.status());
|
|
||||||
if response.status() == 200 {
|
if response.status() == 200 {
|
||||||
log!("[DEBUG] Successfully received items");
|
// Deserialize into Vec<Item>
|
||||||
|
log!("Loading items from DB...");
|
||||||
let items = response
|
let items = response
|
||||||
.json::<Vec<Item>>()
|
.json::<Vec<Item>>()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| format!("Failed to parse items: {:?}", err))?;
|
||||||
log!("[ERROR] JSON parsing error: {:?}", err);
|
|
||||||
format!("Failed to parse items: {:?}", err)
|
|
||||||
})?;
|
|
||||||
log!("[DEBUG] Successfully parsed {} items", items.len());
|
|
||||||
Ok(items)
|
Ok(items)
|
||||||
} else {
|
} else {
|
||||||
let body = response.text().await.unwrap_or_default();
|
let body = response.text().await.unwrap_or_default();
|
||||||
log!("[ERROR] Server error details:
|
|
||||||
Status: {}
|
|
||||||
URL: {}
|
|
||||||
Response Body: {}
|
|
||||||
Request URL: {}",
|
|
||||||
response.status(),
|
|
||||||
api_url,
|
|
||||||
body,
|
|
||||||
current_url
|
|
||||||
);
|
|
||||||
Err(format!("Server error ({}): {}", response.status(), body))
|
Err(format!("Server error ({}): {}", response.status(), body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
128
src/db.rs
128
src/db.rs
|
@ -132,6 +132,7 @@ mod db_impl {
|
||||||
// Retrieve all items from the database for a specific URL
|
// Retrieve all items from the database for a specific URL
|
||||||
pub async fn get_items_by_url(&self, url: &str) -> Result<Vec<Item>, Error> {
|
pub async fn get_items_by_url(&self, url: &str) -> Result<Vec<Item>, Error> {
|
||||||
let conn = self.conn.lock().await;
|
let conn = self.conn.lock().await;
|
||||||
|
|
||||||
let url_id: Option<i64> = match conn.query_row(
|
let url_id: Option<i64> = match conn.query_row(
|
||||||
"SELECT id FROM urls WHERE url = ?",
|
"SELECT id FROM urls WHERE url = ?",
|
||||||
&[url],
|
&[url],
|
||||||
|
@ -147,9 +148,6 @@ mod db_impl {
|
||||||
None => return Ok(Vec::new()), // Return empty list if URL not found
|
None => return Ok(Vec::new()), // Return empty list if URL not found
|
||||||
};
|
};
|
||||||
|
|
||||||
log!("Fetching items for URL '{}' (ID: {})", url, url_id);
|
|
||||||
|
|
||||||
|
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
"SELECT i.id, i.name, i.description, i.wikidata_id,
|
"SELECT i.id, i.name, i.description, i.wikidata_id,
|
||||||
p.name AS prop_name, ip.value
|
p.name AS prop_name, ip.value
|
||||||
|
@ -189,6 +187,15 @@ mod db_impl {
|
||||||
Ok(items.into_values().collect())
|
Ok(items.into_values().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_url_id(&self, url: &str) -> Result<Option<i64>, Error> {
|
||||||
|
let conn = self.conn.lock().await;
|
||||||
|
conn.query_row(
|
||||||
|
"SELECT id FROM urls WHERE url = ?",
|
||||||
|
&[url],
|
||||||
|
|row| row.get(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_or_create_property(&self, prop: &str) -> Result<i64, Error> {
|
async fn get_or_create_property(&self, prop: &str) -> Result<i64, Error> {
|
||||||
let conn = self.conn.lock().await;
|
let conn = self.conn.lock().await;
|
||||||
// Check existing
|
// Check existing
|
||||||
|
@ -211,99 +218,43 @@ mod db_impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert a new item into the database for a specific URL
|
// Insert a new item into the database for a specific URL
|
||||||
pub async fn insert_item_by_url(&self, url: &str, item: &Item) -> Result<(), Error> {
|
pub async fn insert_item_by_url(
|
||||||
log!("[DB] Starting insert for URL: {}, Item: {}", url, item.id);
|
&self,
|
||||||
|
url: &str,
|
||||||
|
item: &Item
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Log before DB operations
|
||||||
|
log!("[DATABASE] Inserting item - ID: {}, Name: '{}'", item.id, item.name);
|
||||||
|
let conn = self.conn.lock().await;
|
||||||
|
|
||||||
// 1. Check database lock acquisition
|
// Get or create URL record
|
||||||
let lock_start = std::time::Instant::now();
|
let url_id = match self.get_url_id(url).await {
|
||||||
let mut conn = self.conn.lock().await;
|
Ok(Some(id)) => id,
|
||||||
log!("[DB] Lock acquired in {:?}", lock_start.elapsed());
|
_ => self.insert_url(url).await?,
|
||||||
|
|
||||||
// 2. Transaction handling
|
|
||||||
log!("[DB] Starting transaction");
|
|
||||||
let tx = conn.transaction().map_err(|e| {
|
|
||||||
log!("[DB] Transaction start failed: {:?}", e);
|
|
||||||
e
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// 3. URL handling
|
|
||||||
log!("[DB] Checking URL existence: {}", url);
|
|
||||||
let url_id = match tx.query_row(
|
|
||||||
"SELECT id FROM urls WHERE url = ?",
|
|
||||||
[url],
|
|
||||||
|row| row.get::<_, i64>(0)
|
|
||||||
) {
|
|
||||||
Ok(id) => {
|
|
||||||
log!("[DB] Found existing URL ID: {}", id);
|
|
||||||
id
|
|
||||||
},
|
|
||||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
|
||||||
log!("[DB] Inserting new URL");
|
|
||||||
tx.execute("INSERT INTO urls (url) VALUES (?)", [url])?;
|
|
||||||
let id = tx.last_insert_rowid();
|
|
||||||
log!("[DB] Created URL ID: {}", id);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.into()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Item insertion
|
// Log final SQL parameters
|
||||||
log!("[DB] Inserting item into items table");
|
log!("[DATABASE] SQL params - ID: {}, URL ID: {}, Name: '{}'",
|
||||||
match tx.execute(
|
item.id, url_id, item.name);
|
||||||
|
|
||||||
|
// Insert item with URL relationship
|
||||||
|
conn.execute(
|
||||||
"INSERT INTO items (id, url_id, name, description, wikidata_id)
|
"INSERT INTO items (id, url_id, name, description, wikidata_id)
|
||||||
VALUES (?, ?, ?, ?, ?)",
|
VALUES (?, ?, ?, ?, ?)",
|
||||||
rusqlite::params![
|
&[&item.id, &url_id.to_string(), &item.name,
|
||||||
&item.id,
|
&item.description, &item.wikidata_id.as_ref().unwrap_or(&String::new())],
|
||||||
url_id,
|
)?;
|
||||||
&item.name,
|
|
||||||
&item.description,
|
|
||||||
&item.wikidata_id
|
|
||||||
],
|
|
||||||
) {
|
|
||||||
Ok(_) => log!("[DB] Item inserted successfully"),
|
|
||||||
Err(e) => {
|
|
||||||
log!("[DB] Item insert error: {:?}", e);
|
|
||||||
return Err(e.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Property handling with enhanced logging
|
|
||||||
log!("[DB] Processing {} properties", item.custom_properties.len());
|
|
||||||
for (prop, value) in &item.custom_properties {
|
|
||||||
log!("[DB] Handling property: {}", prop);
|
|
||||||
|
|
||||||
// Property Lookup/Creation
|
// Handle properties through junction table
|
||||||
let prop_id = match tx.query_row(
|
for (prop, value) in &item.custom_properties {
|
||||||
"SELECT id FROM properties WHERE name = ?",
|
let prop_id = self.get_or_create_property(&prop).await?;
|
||||||
[prop],
|
conn.execute(
|
||||||
|row| row.get::<_, i64>(0)
|
|
||||||
) {
|
|
||||||
Ok(id) => {
|
|
||||||
log!("[DB] Existing property ID: {}", id);
|
|
||||||
id
|
|
||||||
},
|
|
||||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
|
||||||
log!("[DB] Creating new property: {}", prop);
|
|
||||||
tx.execute("INSERT INTO properties (name) VALUES (?)", [prop])?;
|
|
||||||
let id = tx.last_insert_rowid();
|
|
||||||
log!("[DB] New property ID: {}", id);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log!("[DB] Property lookup error: {:?}", e);
|
|
||||||
return Err(e.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Property Value Insertion
|
|
||||||
log!("[DB] Inserting property {} with value {}", prop, value);
|
|
||||||
tx.execute(
|
|
||||||
"INSERT INTO item_properties (item_id, property_id, value)
|
"INSERT INTO item_properties (item_id, property_id, value)
|
||||||
VALUES (?, ?, ?)",
|
VALUES (?, ?, ?)",
|
||||||
rusqlite::params![&item.id, prop_id, &value],
|
&[&item.id, &prop_id.to_string(), &value],
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
log!("[DATABASE] Successfully inserted item ID: {}", item.id);
|
||||||
tx.commit()?;
|
|
||||||
log!("[DB] Transaction committed successfully");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,10 +280,7 @@ mod db_impl {
|
||||||
) AND item_id IN (
|
) AND item_id IN (
|
||||||
SELECT id FROM items WHERE url_id = ?
|
SELECT id FROM items WHERE url_id = ?
|
||||||
)",
|
)",
|
||||||
rusqlite::params![
|
&[property, &url_id.to_string()],
|
||||||
property, // &str
|
|
||||||
url_id // i64
|
|
||||||
],
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
logging::log!("Property deleted from the database for URL: {}", url);
|
logging::log!("Property deleted from the database for URL: {}", url);
|
||||||
|
|
|
@ -82,7 +82,10 @@ async fn get_items_handler(
|
||||||
db: web::Data<Arc<Mutex<Database>>>,
|
db: web::Data<Arc<Mutex<Database>>>,
|
||||||
url: web::Path<String>,
|
url: web::Path<String>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
get_items(db, web::Query(url.into_inner())).await
|
let decoded_url = urlencoding::decode(&url)
|
||||||
|
.map(|cow| cow.into_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
get_items(db, web::Query(decoded_url)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler to create an item for a specific URL
|
// Handler to create an item for a specific URL
|
||||||
|
|
Loading…
Add table
Reference in a new issue