use rusqlite::Connection; use serde::Serialize; use crate::crypto::encryption::{decrypt_data_with_user_key, encrypt_data_with_user_key, hash_element}; use crate::crypto::key_manager::get_user_encryption_key; use crate::domains::issue::repo; use crate::domains::tombstone::repo as tombstone_repo; use crate::error::AppResult; use crate::helpers::{create_unique_id, timestamp_in_seconds}; use crate::shared::types::Lang; /// Represents a synced issue with its metadata. pub struct SyncedIssue { pub id: String, pub name: String, pub last_update: i64, } #[derive(Serialize)] pub struct IssueProps { pub id: String, pub name: String, } /// Retrieves all issues associated with a specific book. /// Decrypts issue names using the user's encryption key. /// * `conn` - Database connection /// * `user_id` - The unique identifier of the user /// * `book_id` - The unique identifier of the book /// * `lang` - The language for error messages ("fr" or "en") /// Returns a list of decrypted issues. pub fn get_issues_from_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult> { let issue_query_results: Vec = repo::fetch_issues_from_book(conn, user_id, book_id, lang)?; let user_encryption_key: String = get_user_encryption_key(user_id)?; let mut decrypted_issues: Vec = Vec::new(); if !issue_query_results.is_empty() { for issue_record in issue_query_results { decrypted_issues.push(IssueProps { id: issue_record.issue_id, name: decrypt_data_with_user_key(&issue_record.name, &user_encryption_key)?, }); } } Ok(decrypted_issues) } /// Creates a new issue for a book. /// Encrypts and hashes the issue name before storage. /// * `conn` - Database connection /// * `user_id` - The unique identifier of the user /// * `book_id` - The unique identifier of the book /// * `name` - The plain text name of the issue /// * `lang` - The language for error messages ("fr" or "en") /// * `existing_issue_id` - Optional existing issue ID for syncing purposes /// Returns the unique identifier of the created issue. pub fn add_new_issue(conn: &Connection, user_id: &str, book_id: &str, name: &str, lang: Lang, existing_issue_id: Option<&str>) -> AppResult { let user_encryption_key: String = get_user_encryption_key(user_id)?; let encrypted_name: String = encrypt_data_with_user_key(name, &user_encryption_key)?; let hashed_name: String = hash_element(name); let issue_id: String = create_unique_id(existing_issue_id); let last_update: i64 = timestamp_in_seconds(); repo::insert_new_issue(conn, &issue_id, user_id, book_id, &encrypted_name, &hashed_name, last_update, lang) } /// Removes an issue from the database. /// * `conn` - Database connection /// * `user_id` - The unique identifier of the user /// * `book_id` - The unique identifier of the book /// * `issue_id` - The unique identifier of the issue to remove /// * `deleted_at` - The timestamp of deletion /// * `lang` - The language for error messages ("fr" or "en") /// Returns true if the issue was successfully removed, false otherwise. pub fn remove_issue(conn: &Connection, user_id: &str, book_id: &str, issue_id: &str, deleted_at: i64, lang: Lang) -> AppResult { let deleted: bool = repo::delete_issue(conn, user_id, issue_id, lang)?; if deleted { tombstone_repo::insert(conn, book_id, "book_issues", issue_id, Some(book_id), user_id, deleted_at, lang)?; } Ok(deleted) }