From e8aaef108bd1df7df675a70129e8c13e60eb36ca Mon Sep 17 00:00:00 2001 From: natreex Date: Sun, 22 Mar 2026 15:50:36 -0400 Subject: [PATCH] Refactor decryption logic to handle empty fields consistently across services --- app/login/login/page.tsx | 19 ++++++++++ app/page.tsx | 4 +-- components/ScribeControllerBar.tsx | 16 ++++++--- components/book/AddNewBookForm.tsx | 10 +++--- lib/errors.ts | 12 ++++--- src-tauri/src/crypto/key_manager.rs | 4 +++ src-tauri/src/db/connection.rs | 8 +++++ src-tauri/src/domains/book/commands.rs | 1 + src-tauri/src/domains/book/service.rs | 31 +++++++++++----- src-tauri/src/domains/chapter/service.rs | 8 ++--- src-tauri/src/domains/character/service.rs | 22 ++++++------ src-tauri/src/domains/guideline/service.rs | 8 ++--- src-tauri/src/domains/series/service.rs | 4 +-- .../src/domains/series_character/service.rs | 22 ++++++------ src-tauri/src/domains/series_sync/service.rs | 14 ++++---- src-tauri/src/domains/series_world/service.rs | 14 ++++---- src-tauri/src/domains/spell/service.rs | 6 ++-- src-tauri/src/domains/sync/service.rs | 18 +++++----- src-tauri/src/domains/upload/service.rs | 16 ++++----- src-tauri/src/domains/user/commands.rs | 36 ++++++++++++++++++- src-tauri/src/domains/user/service.rs | 2 +- src-tauri/src/domains/world/service.rs | 20 +++++------ src-tauri/src/error.rs | 26 ++++++-------- src-tauri/src/lib.rs | 1 + src-tauri/src/shared/ai_models.rs | 0 src-tauri/src/shared/session.rs | 24 +++++++++++++ 26 files changed, 226 insertions(+), 120 deletions(-) create mode 100644 src-tauri/src/shared/ai_models.rs create mode 100644 src-tauri/src/shared/session.rs diff --git a/app/login/login/page.tsx b/app/login/login/page.tsx index d8202e3..f12c149 100755 --- a/app/login/login/page.tsx +++ b/app/login/login/page.tsx @@ -14,6 +14,18 @@ export default function LoginPage() { const {lang, setLang} = useContext(LangContext); const [showOfflineWarning, setShowOfflineWarning] = useState(false); const [isOnline, setIsOnline] = useState(true); + const [resetDone, setResetDone] = useState(false); + + const isDev = process.env.NODE_ENV === 'development'; + + const handleDevReset = async () => { + try { + await tauri.devResetAll(); + setResetDone(true); + } catch (error) { + console.error('[DevReset]', error); + } + }; const toggleLanguage = () => { const newLang = lang === 'fr' ? 'en' : 'fr'; @@ -148,6 +160,13 @@ export default function LoginPage() { + {isDev && ( + resetDone + ? Reset OK + : + )} ); } \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index ea1b139..c2aef6d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -588,7 +588,7 @@ function ScribeContent() { const offlineStatus = await tauri.offlineModeGet(); if (offlineStatus.hasPin && offlineStatus.lastUserId) { - setOfflineMode((prev:OfflineMode):OfflineMode => ({...prev, isOffline: true, isNetworkOnline: false})); + setOfflineMode((prev:OfflineMode):OfflineMode => ({...prev, isOffline: true, isManuallyOffline: true, isNetworkOnline: false})); setShowPinVerify(true); setIsLoading(false); return; @@ -611,7 +611,7 @@ function ScribeContent() { const offlineStatus = await tauri.offlineModeGet(); if (offlineStatus.hasPin && offlineStatus.lastUserId) { - setOfflineMode(prev => ({...prev, isOffline: true, isNetworkOnline: false})); + setOfflineMode(prev => ({...prev, isOffline: true, isManuallyOffline: true, isNetworkOnline: false})); setShowPinVerify(true); setIsLoading(false); return; diff --git a/components/ScribeControllerBar.tsx b/components/ScribeControllerBar.tsx index 519a81b..84c7d2c 100644 --- a/components/ScribeControllerBar.tsx +++ b/components/ScribeControllerBar.tsx @@ -1,4 +1,5 @@ import React, {useContext, useState} from "react"; +import * as tauri from '@/lib/tauri'; import {ChapterProps, chapterVersions} from "@/lib/models/Chapter"; import {ChapterContext} from "@/context/ChapterContext"; import {BookContext} from "@/context/BookContext"; @@ -39,11 +40,16 @@ export default function ScribeControllerBar() { async function handleChapterVersionChanged(version: number) { try { - const response: ChapterProps = await System.authGetQueryToServer(`chapter/whole`, session.accessToken, lang, { - bookid: book?.bookId, - id: chapter?.chapterId, - version: version, - }); + let response: ChapterProps | null; + if (isCurrentlyOffline() || book?.localBook) { + response = await tauri.getWholeChapter(chapter?.chapterId ?? '', version, book?.bookId ?? ''); + } else { + response = await System.authGetQueryToServer(`chapter/whole`, session.accessToken, lang, { + bookid: book?.bookId, + id: chapter?.chapterId, + version: version, + }); + } if (!response) { errorMessage(t("controllerBar.chapterNotFound")); return; diff --git a/components/book/AddNewBookForm.tsx b/components/book/AddNewBookForm.tsx index 7d86e98..53190d0 100644 --- a/components/book/AddNewBookForm.tsx +++ b/components/book/AddNewBookForm.tsx @@ -204,11 +204,9 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch< setIsAddingBook(false); setCloseForm(false) } catch (e: unknown) { - if (e instanceof Error) { - errorMessage(e.message); - } else { - errorMessage(t('addNewBookForm.error.addingBook')); - } + console.error('[AddBook] Raw error:', e, typeof e, JSON.stringify(e)); + const msg = e instanceof Error ? e.message : typeof e === 'object' && e !== null && 'message' in e ? String((e as {message:string}).message) : typeof e === 'string' ? e : t('addNewBookForm.error.addingBook'); + errorMessage(msg); setIsAddingBook(false); } } @@ -250,7 +248,7 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch< return (
+ className="fixed inset-0 flex items-center justify-center bg-black/60 z-40 backdrop-blur-md animate-fadeIn">
diff --git a/lib/errors.ts b/lib/errors.ts index cbf3024..6538023 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -1,14 +1,16 @@ /** - * Clean error messages from Electron IPC prefix - * Transforms: "Error invoking remote method 'channel': Error: Message" - * Into: "Message" + * Extract user-facing error message from various error formats. + * Supports: Error objects, Tauri AppError { kind, message }, and plain strings. */ export function cleanErrorMessage(error: unknown): string { if (error instanceof Error) { - return error.message.replace(/^Error invoking remote method '[^']+': Error: /, ''); + return error.message; + } + if (typeof error === 'object' && error !== null && 'message' in error) { + return String((error as { message: string }).message); } if (typeof error === 'string') { - return error.replace(/^Error invoking remote method '[^']+': Error: /, ''); + return error; } return 'An unknown error occurred'; } diff --git a/src-tauri/src/crypto/key_manager.rs b/src-tauri/src/crypto/key_manager.rs index d6a6192..d7b17cc 100644 --- a/src-tauri/src/crypto/key_manager.rs +++ b/src-tauri/src/crypto/key_manager.rs @@ -165,3 +165,7 @@ pub fn get_last_user_id() -> AppResult> { let vault = read_vault(); Ok(vault.last_user_id) } + +pub fn clear_vault() -> AppResult<()> { + write_vault(&SecureVault::default()) +} diff --git a/src-tauri/src/db/connection.rs b/src-tauri/src/db/connection.rs index f7f2f22..c21f706 100644 --- a/src-tauri/src/db/connection.rs +++ b/src-tauri/src/db/connection.rs @@ -53,6 +53,14 @@ impl DatabaseManager { pub fn close(&mut self, user_id: &str) { self.connections.remove(user_id); } + + pub fn close_all(&mut self) { + self.connections.clear(); + } + + pub fn base_path(&self) -> &PathBuf { + &self.base_path + } } pub type DbManager = Arc>; diff --git a/src-tauri/src/domains/book/commands.rs b/src-tauri/src/domains/book/commands.rs index d146f86..bbed397 100644 --- a/src-tauri/src/domains/book/commands.rs +++ b/src-tauri/src/domains/book/commands.rs @@ -38,6 +38,7 @@ pub struct CreateBookData { pub title: String, pub sub_title: Option, pub summary: Option, + #[serde(rename = "type")] pub book_type: String, pub serie_id: Option, pub desired_release_date: Option, diff --git a/src-tauri/src/domains/book/service.rs b/src-tauri/src/domains/book/service.rs index f9bcb09..61a625f 100644 --- a/src-tauri/src/domains/book/service.rs +++ b/src-tauri/src/domains/book/service.rs @@ -722,8 +722,14 @@ pub fn get_books(conn: &Connection, user_id: &str, lang: Lang) -> AppResult = Vec::with_capacity(books.len()); for book in books { let decrypted_title: String = decrypt_data_with_user_key(&book.title, &user_key)?; - let decrypted_sub_title: String = if let Some(ref sub_title) = book.sub_title { decrypt_data_with_user_key(sub_title, &user_key)? } else { String::new() }; - let decrypted_summary: String = if let Some(ref summary) = book.summary { decrypt_data_with_user_key(summary, &user_key)? } else { String::new() }; + let decrypted_sub_title: String = match book.sub_title.as_deref() { + Some(sub_title) if !sub_title.is_empty() => decrypt_data_with_user_key(sub_title, &user_key)?, + _ => String::new(), + }; + let decrypted_summary: String = match book.summary.as_deref() { + Some(summary) if !summary.is_empty() => decrypt_data_with_user_key(summary, &user_key)?, + _ => String::new(), + }; book_props_list.push(BookProps { book_id: book.book_id, @@ -769,6 +775,7 @@ pub fn add_book( let encrypted_title: String = encrypt_data_with_user_key(title, &user_key)?; let encrypted_sub_title: String = if sub_title.is_empty() { String::new() } else { encrypt_data_with_user_key(sub_title, &user_key)? }; let encrypted_summary: String = if summary.is_empty() { String::new() } else { encrypt_data_with_user_key(summary, &user_key)? }; + let hashed_title: String = hash_element(title); let hashed_sub_title: String = if sub_title.is_empty() { String::new() } else { hash_element(sub_title) }; @@ -795,8 +802,14 @@ pub fn get_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> let series_id: Option = repo::fetch_book_series_id(conn, book_id, lang); let decrypted_title: String = decrypt_data_with_user_key(&book_data.title, &user_key)?; - let decrypted_sub_title: String = if let Some(ref sub_title) = book_data.sub_title { decrypt_data_with_user_key(sub_title, &user_key)? } else { String::new() }; - let decrypted_summary: String = if let Some(ref summary) = book_data.summary { decrypt_data_with_user_key(summary, &user_key)? } else { String::new() }; + let decrypted_sub_title: String = match book_data.sub_title.as_deref() { + Some(sub_title) if !sub_title.is_empty() => decrypt_data_with_user_key(sub_title, &user_key)?, + _ => String::new(), + }; + let decrypted_summary: String = match book_data.summary.as_deref() { + Some(summary) if !summary.is_empty() => decrypt_data_with_user_key(summary, &user_key)?, + _ => String::new(), + }; Ok(BookProps { book_id: book_data.book_id, @@ -892,7 +905,7 @@ pub fn complete_book_data(conn: &Connection, user_id: &str, book_id: &str, lang: for chapter in chapters { let decrypted_title: String = if chapter.title.is_empty() { String::new() } else { decrypt_data_with_user_key(&chapter.title, &user_key)? }; - let decrypted_content: String = if let Some(ref content) = chapter.content { decrypt_data_with_user_key(content, &user_key)? } else { String::new() }; + let decrypted_content: String = if let Some(ref content) = chapter.content { if content.is_empty() { String::new() } else { decrypt_data_with_user_key(content, &user_key)? } } else { String::new() }; decrypted_chapters.push(CompleteChapterContent { id: String::new(), title: decrypted_title, @@ -907,12 +920,12 @@ pub fn complete_book_data(conn: &Connection, user_id: &str, book_id: &str, lang: Ok(CompleteBookData { book_id: book_id.to_string(), title: book_title, - sub_title: if let Some(ref sub_title) = book_data.sub_title { decrypt_data_with_user_key(sub_title, &user_key)? } else { String::new() }, - summary: if let Some(ref summary) = book_data.summary { decrypt_data_with_user_key(summary, &user_key)? } else { String::new() }, + sub_title: if let Some(ref sub_title) = book_data.sub_title { if sub_title.is_empty() { String::new() } else { decrypt_data_with_user_key(sub_title, &user_key)? } } else { String::new() }, + summary: if let Some(ref summary) = book_data.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_key)? } } else { String::new() }, cover_image, user_infos: BookUserInfos { - username: decrypt_data_with_user_key(&user_infos.username, &user_key)?, - author_name: if let Some(ref author_name) = user_infos.author_name { decrypt_data_with_user_key(author_name, &user_key)? } else { String::new() }, + username: if user_infos.username.is_empty() { String::new() } else { decrypt_data_with_user_key(&user_infos.username, &user_key)? }, + author_name: if let Some(ref author_name) = user_infos.author_name { if author_name.is_empty() { String::new() } else { decrypt_data_with_user_key(author_name, &user_key)? } } else { String::new() }, }, chapters: decrypted_chapters, }) diff --git a/src-tauri/src/domains/chapter/service.rs b/src-tauri/src/domains/chapter/service.rs index 2e0008d..1317c39 100644 --- a/src-tauri/src/domains/chapter/service.rs +++ b/src-tauri/src/domains/chapter/service.rs @@ -368,8 +368,8 @@ pub fn get_chapter_story(conn: &Connection, user_id: &str, chapter_id: &str, lan } if let Some(ref _incident_id) = story_result.incident_id { - let decrypted_incident_title: String = if let Some(ref incident_title) = story_result.incident_title { decrypt_data_with_user_key(incident_title, &user_encryption_key)? } else { String::new() }; - let decrypted_incident_summary: String = if let Some(ref incident_summary) = story_result.incident_summary { decrypt_data_with_user_key(incident_summary, &user_encryption_key)? } else { String::new() }; + let decrypted_incident_title: String = if let Some(ref incident_title) = story_result.incident_title { if incident_title.is_empty() { String::new() } else { decrypt_data_with_user_key(incident_title, &user_encryption_key)? } } else { String::new() }; + let decrypted_incident_summary: String = if let Some(ref incident_summary) = story_result.incident_summary { if incident_summary.is_empty() { String::new() } else { decrypt_data_with_user_key(incident_summary, &user_encryption_key)? } } else { String::new() }; let act_story = act_stories_map.get(&act_id).unwrap(); let incident_already_exists: bool = act_story.incidents.iter().any( @@ -390,8 +390,8 @@ pub fn get_chapter_story(conn: &Connection, user_id: &str, chapter_id: &str, lan } if let Some(ref _plot_point_id) = story_result.plot_point_id { - let decrypted_plot_title: String = if let Some(ref plot_title) = story_result.plot_title { decrypt_data_with_user_key(plot_title, &user_encryption_key)? } else { String::new() }; - let decrypted_plot_summary: String = if let Some(ref plot_summary) = story_result.plot_summary { decrypt_data_with_user_key(plot_summary, &user_encryption_key)? } else { String::new() }; + let decrypted_plot_title: String = if let Some(ref plot_title) = story_result.plot_title { if plot_title.is_empty() { String::new() } else { decrypt_data_with_user_key(plot_title, &user_encryption_key)? } } else { String::new() }; + let decrypted_plot_summary: String = if let Some(ref plot_summary) = story_result.plot_summary { if plot_summary.is_empty() { String::new() } else { decrypt_data_with_user_key(plot_summary, &user_encryption_key)? } } else { String::new() }; let act_story = act_stories_map.get(&act_id).unwrap(); let plot_point_already_exists: bool = act_story.plot_points.iter().any( diff --git a/src-tauri/src/domains/character/service.rs b/src-tauri/src/domains/character/service.rs index 7adf2f5..248f9ce 100644 --- a/src-tauri/src/domains/character/service.rs +++ b/src-tauri/src/domains/character/service.rs @@ -130,23 +130,23 @@ pub fn get_character_list(conn: &Connection, user_id: &str, book_id: &str, lang: id: encrypted_character.character_id, name: if encrypted_character.first_name.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.first_name, &user_key)? }, last_name: if encrypted_character.last_name.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.last_name, &user_key)? }, - nickname: if let Some(ref value) = encrypted_character.nickname { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - age: if let Some(ref value) = encrypted_character.age { Some(decrypt_data_with_user_key(value, &user_key)?.parse::().unwrap_or(0)) } else { None }, - gender: if let Some(ref value) = encrypted_character.gender { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - species: if let Some(ref value) = encrypted_character.species { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - nationality: if let Some(ref value) = encrypted_character.nationality { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - status: if let Some(ref value) = encrypted_character.status { decrypt_data_with_user_key(value, &user_key)? } else { "alive".to_string() }, + nickname: if let Some(ref nickname) = encrypted_character.nickname { if nickname.is_empty() { String::new() } else { decrypt_data_with_user_key(nickname, &user_key)? } } else { String::new() }, + age: if let Some(ref age_val) = encrypted_character.age { if age_val.is_empty() { None } else { Some(decrypt_data_with_user_key(age_val, &user_key)?.parse::().unwrap_or(0)) } } else { None }, + gender: if let Some(ref gender) = encrypted_character.gender { if gender.is_empty() { String::new() } else { decrypt_data_with_user_key(gender, &user_key)? } } else { String::new() }, + species: if let Some(ref species) = encrypted_character.species { if species.is_empty() { String::new() } else { decrypt_data_with_user_key(species, &user_key)? } } else { String::new() }, + nationality: if let Some(ref nationality) = encrypted_character.nationality { if nationality.is_empty() { String::new() } else { decrypt_data_with_user_key(nationality, &user_key)? } } else { String::new() }, + status: if let Some(ref status) = encrypted_character.status { if status.is_empty() { "alive".to_string() } else { decrypt_data_with_user_key(status, &user_key)? } } else { "alive".to_string() }, title: if encrypted_character.title.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.title, &user_key)? }, category: if encrypted_character.category.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.category, &user_key)? }, image: if encrypted_character.image.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.image, &user_key)? }, role: if encrypted_character.role.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.role, &user_key)? }, biography: if encrypted_character.biography.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.biography, &user_key)? }, history: if encrypted_character.history.is_empty() { String::new() } else { decrypt_data_with_user_key(&encrypted_character.history, &user_key)? }, - speech_pattern: if let Some(ref value) = encrypted_character.speech_pattern { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - catchphrase: if let Some(ref value) = encrypted_character.catchphrase { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - residence: if let Some(ref value) = encrypted_character.residence { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - notes: if let Some(ref value) = encrypted_character.notes { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, - color: if let Some(ref value) = encrypted_character.color { decrypt_data_with_user_key(value, &user_key)? } else { String::new() }, + speech_pattern: if let Some(ref speech_pattern) = encrypted_character.speech_pattern { if speech_pattern.is_empty() { String::new() } else { decrypt_data_with_user_key(speech_pattern, &user_key)? } } else { String::new() }, + catchphrase: if let Some(ref catchphrase) = encrypted_character.catchphrase { if catchphrase.is_empty() { String::new() } else { decrypt_data_with_user_key(catchphrase, &user_key)? } } else { String::new() }, + residence: if let Some(ref residence) = encrypted_character.residence { if residence.is_empty() { String::new() } else { decrypt_data_with_user_key(residence, &user_key)? } } else { String::new() }, + notes: if let Some(ref notes) = encrypted_character.notes { if notes.is_empty() { String::new() } else { decrypt_data_with_user_key(notes, &user_key)? } } else { String::new() }, + color: if let Some(ref color) = encrypted_character.color { if color.is_empty() { String::new() } else { decrypt_data_with_user_key(color, &user_key)? } } else { String::new() }, series_character_id: encrypted_character.series_character_id, }); } diff --git a/src-tauri/src/domains/guideline/service.rs b/src-tauri/src/domains/guideline/service.rs index ecde5d8..7b29bbd 100644 --- a/src-tauri/src/domains/guideline/service.rs +++ b/src-tauri/src/domains/guideline/service.rs @@ -131,11 +131,11 @@ pub fn get_guide_line_ai(conn: &Connection, user_id: &str, book_id: &str, lang: Ok(GuideLineAI { narrative_type: ai_guide_line_data.narrative_type, dialogue_type: ai_guide_line_data.dialogue_type, - global_resume: Some(if let Some(ref global_resume) = ai_guide_line_data.global_resume { decrypt_data_with_user_key(global_resume, &encryption_key)? } else { String::new() }), - atmosphere: Some(if let Some(ref atmosphere) = ai_guide_line_data.atmosphere { decrypt_data_with_user_key(atmosphere, &encryption_key)? } else { String::new() }), + global_resume: Some(if let Some(ref global_resume) = ai_guide_line_data.global_resume { if global_resume.is_empty() { String::new() } else { decrypt_data_with_user_key(global_resume, &encryption_key)? } } else { String::new() }), + atmosphere: Some(if let Some(ref atmosphere) = ai_guide_line_data.atmosphere { if atmosphere.is_empty() { String::new() } else { decrypt_data_with_user_key(atmosphere, &encryption_key)? } } else { String::new() }), verbe_tense: ai_guide_line_data.verbe_tense, - themes: Some(if let Some(ref themes) = ai_guide_line_data.themes { decrypt_data_with_user_key(themes, &encryption_key)? } else { String::new() }), - current_resume: Some(if let Some(ref current_resume) = ai_guide_line_data.current_resume { decrypt_data_with_user_key(current_resume, &encryption_key)? } else { String::new() }), + themes: Some(if let Some(ref themes) = ai_guide_line_data.themes { if themes.is_empty() { String::new() } else { decrypt_data_with_user_key(themes, &encryption_key)? } } else { String::new() }), + current_resume: Some(if let Some(ref current_resume) = ai_guide_line_data.current_resume { if current_resume.is_empty() { String::new() } else { decrypt_data_with_user_key(current_resume, &encryption_key)? } } else { String::new() }), langue: ai_guide_line_data.langue, }) } diff --git a/src-tauri/src/domains/series/service.rs b/src-tauri/src/domains/series/service.rs index d34748a..ac7fe8d 100644 --- a/src-tauri/src/domains/series/service.rs +++ b/src-tauri/src/domains/series/service.rs @@ -58,7 +58,7 @@ pub fn get_series_list(conn: &Connection, user_id: &str, lang: Lang) -> AppResul let mut series_list: Vec = Vec::with_capacity(series_results.len()); for series_item in series_results { let decrypted_name: String = decrypt_data_with_user_key(&series_item.name, &user_key)?; - let decrypted_description: String = if let Some(ref description) = series_item.description { decrypt_data_with_user_key(description, &user_key)? } else { String::new() }; + let decrypted_description: String = if let Some(ref description) = series_item.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_key)? } } else { String::new() }; let book_ids: Vec = if let Some(ref book_ids_str) = series_item.book_ids { book_ids_str.split(',').map(|s| s.to_string()).collect() } else { vec![] }; series_list.push(SeriesListItemProps { @@ -102,7 +102,7 @@ pub fn get_series_detail(conn: &Connection, user_id: &str, series_id: &str, lang } let decrypted_name: String = decrypt_data_with_user_key(&series_result.name, &user_key)?; - let decrypted_description: String = if let Some(ref description) = series_result.description { decrypt_data_with_user_key(description, &user_key)? } else { String::new() }; + let decrypted_description: String = if let Some(ref description) = series_result.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_key)? } } else { String::new() }; Ok(SeriesDetailProps { id: series_result.series_id, diff --git a/src-tauri/src/domains/series_character/service.rs b/src-tauri/src/domains/series_character/service.rs index 79b8c68..5fd69e2 100644 --- a/src-tauri/src/domains/series_character/service.rs +++ b/src-tauri/src/domains/series_character/service.rs @@ -112,23 +112,23 @@ pub fn get_character_list(conn: &Connection, user_id: &str, series_id: &str, lan for character in characters { let decrypted_name: String = if !character.first_name.is_empty() { decrypt_data_with_user_key(&character.first_name, &user_key)? } else { String::new() }; let decrypted_last_name: String = if !character.last_name.is_empty() { decrypt_data_with_user_key(&character.last_name, &user_key)? } else { String::new() }; - let decrypted_nickname: String = if let Some(ref nickname) = character.nickname { decrypt_data_with_user_key(nickname, &user_key)? } else { String::new() }; - let decrypted_age: Option = if let Some(ref age) = character.age { Some(decrypt_data_with_user_key(age, &user_key)?.parse::().unwrap_or(0)) } else { None }; - let decrypted_gender: String = if let Some(ref gender) = character.gender { decrypt_data_with_user_key(gender, &user_key)? } else { String::new() }; - let decrypted_species: String = if let Some(ref species) = character.species { decrypt_data_with_user_key(species, &user_key)? } else { String::new() }; - let decrypted_nationality: String = if let Some(ref nationality) = character.nationality { decrypt_data_with_user_key(nationality, &user_key)? } else { String::new() }; - let decrypted_status: String = if let Some(ref status) = character.status { decrypt_data_with_user_key(status, &user_key)? } else { "alive".to_string() }; + let decrypted_nickname: String = if let Some(ref nickname) = character.nickname { if nickname.is_empty() { String::new() } else { decrypt_data_with_user_key(nickname, &user_key)? } } else { String::new() }; + let decrypted_age: Option = if let Some(ref age) = character.age { if age.is_empty() { None } else { Some(decrypt_data_with_user_key(age, &user_key)?.parse::().unwrap_or(0)) } } else { None }; + let decrypted_gender: String = if let Some(ref gender) = character.gender { if gender.is_empty() { String::new() } else { decrypt_data_with_user_key(gender, &user_key)? } } else { String::new() }; + let decrypted_species: String = if let Some(ref species) = character.species { if species.is_empty() { String::new() } else { decrypt_data_with_user_key(species, &user_key)? } } else { String::new() }; + let decrypted_nationality: String = if let Some(ref nationality) = character.nationality { if nationality.is_empty() { String::new() } else { decrypt_data_with_user_key(nationality, &user_key)? } } else { String::new() }; + let decrypted_status: String = if let Some(ref status) = character.status { if status.is_empty() { "alive".to_string() } else { decrypt_data_with_user_key(status, &user_key)? } } else { "alive".to_string() }; let decrypted_title: String = if !character.title.is_empty() { decrypt_data_with_user_key(&character.title, &user_key)? } else { String::new() }; let decrypted_category: String = if !character.category.is_empty() { decrypt_data_with_user_key(&character.category, &user_key)? } else { String::new() }; let decrypted_image: String = if !character.image.is_empty() { decrypt_data_with_user_key(&character.image, &user_key)? } else { String::new() }; let decrypted_role: String = if !character.role.is_empty() { decrypt_data_with_user_key(&character.role, &user_key)? } else { String::new() }; let decrypted_biography: String = if !character.biography.is_empty() { decrypt_data_with_user_key(&character.biography, &user_key)? } else { String::new() }; let decrypted_history: String = if !character.history.is_empty() { decrypt_data_with_user_key(&character.history, &user_key)? } else { String::new() }; - let decrypted_speech_pattern: String = if let Some(ref speech_pattern) = character.speech_pattern { decrypt_data_with_user_key(speech_pattern, &user_key)? } else { String::new() }; - let decrypted_catchphrase: String = if let Some(ref catchphrase) = character.catchphrase { decrypt_data_with_user_key(catchphrase, &user_key)? } else { String::new() }; - let decrypted_residence: String = if let Some(ref residence) = character.residence { decrypt_data_with_user_key(residence, &user_key)? } else { String::new() }; - let decrypted_notes: String = if let Some(ref notes) = character.notes { decrypt_data_with_user_key(notes, &user_key)? } else { String::new() }; - let decrypted_color: String = if let Some(ref color) = character.color { decrypt_data_with_user_key(color, &user_key)? } else { String::new() }; + let decrypted_speech_pattern: String = if let Some(ref speech_pattern) = character.speech_pattern { if speech_pattern.is_empty() { String::new() } else { decrypt_data_with_user_key(speech_pattern, &user_key)? } } else { String::new() }; + let decrypted_catchphrase: String = if let Some(ref catchphrase) = character.catchphrase { if catchphrase.is_empty() { String::new() } else { decrypt_data_with_user_key(catchphrase, &user_key)? } } else { String::new() }; + let decrypted_residence: String = if let Some(ref residence) = character.residence { if residence.is_empty() { String::new() } else { decrypt_data_with_user_key(residence, &user_key)? } } else { String::new() }; + let decrypted_notes: String = if let Some(ref notes) = character.notes { if notes.is_empty() { String::new() } else { decrypt_data_with_user_key(notes, &user_key)? } } else { String::new() }; + let decrypted_color: String = if let Some(ref color) = character.color { if color.is_empty() { String::new() } else { decrypt_data_with_user_key(color, &user_key)? } } else { String::new() }; character_list.push(SeriesCharacterListProps { id: character.character_id, diff --git a/src-tauri/src/domains/series_sync/service.rs b/src-tauri/src/domains/series_sync/service.rs index 4f41a1f..468618d 100644 --- a/src-tauri/src/domains/series_sync/service.rs +++ b/src-tauri/src/domains/series_sync/service.rs @@ -318,13 +318,13 @@ pub fn get_complete_series_for_upload(conn: &Connection, user_id: &str, series_i user_id: s.user_id, name: decrypt_data_with_user_key(&s.name, &user_key)?, name_hash: s.name_hash, - description: if let Some(ref val) = s.description { decrypt_data_with_user_key(val, &user_key)? } else { String::new() }, - appearance: if let Some(ref val) = s.appearance { decrypt_data_with_user_key(val, &user_key)? } else { String::new() }, - tags: if let Some(ref val) = s.tags { decrypt_data_with_user_key(val, &user_key)? } else { String::new() }, - power_level: if let Some(ref val) = s.power_level { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None }, - components: if let Some(ref val) = s.components { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None }, - limitations: if let Some(ref val) = s.limitations { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None }, - notes: if let Some(ref val) = s.notes { Some(decrypt_data_with_user_key(val, &user_key)?) } else { None }, + description: if let Some(ref description) = s.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_key)? } } else { String::new() }, + appearance: if let Some(ref appearance) = s.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_key)? } } else { String::new() }, + tags: if let Some(ref tags) = s.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_key)? } } else { String::new() }, + power_level: if let Some(ref power_level) = s.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_key)?) } } else { None }, + components: if let Some(ref components) = s.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_key)?) } } else { None }, + limitations: if let Some(ref limitations) = s.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_key)?) } } else { None }, + notes: if let Some(ref notes) = s.notes { if notes.is_empty() { None } else { Some(decrypt_data_with_user_key(notes, &user_key)?) } } else { None }, last_update: s.last_update, }); } diff --git a/src-tauri/src/domains/series_world/service.rs b/src-tauri/src/domains/series_world/service.rs index d3ecd85..e1f9484 100644 --- a/src-tauri/src/domains/series_world/service.rs +++ b/src-tauri/src/domains/series_world/service.rs @@ -71,11 +71,11 @@ pub fn get_world_list(conn: &Connection, user_id: &str, series_id: &str, lang: L worlds_map.insert(row.world_id.clone(), SeriesWorldListProps { id: row.world_id.clone(), name: if row.world_name.is_empty() { String::new() } else { decrypt_data_with_user_key(&row.world_name, &user_key)? }, - history: if let Some(ref history) = row.history { decrypt_data_with_user_key(history, &user_key)? } else { String::new() }, - politics: if let Some(ref politics) = row.politics { decrypt_data_with_user_key(politics, &user_key)? } else { String::new() }, - economy: if let Some(ref economy) = row.economy { decrypt_data_with_user_key(economy, &user_key)? } else { String::new() }, - religion: if let Some(ref religion) = row.religion { decrypt_data_with_user_key(religion, &user_key)? } else { String::new() }, - languages: if let Some(ref languages) = row.languages { decrypt_data_with_user_key(languages, &user_key)? } else { String::new() }, + history: if let Some(ref history) = row.history { if history.is_empty() { String::new() } else { decrypt_data_with_user_key(history, &user_key)? } } else { String::new() }, + politics: if let Some(ref politics) = row.politics { if politics.is_empty() { String::new() } else { decrypt_data_with_user_key(politics, &user_key)? } } else { String::new() }, + economy: if let Some(ref economy) = row.economy { if economy.is_empty() { String::new() } else { decrypt_data_with_user_key(economy, &user_key)? } } else { String::new() }, + religion: if let Some(ref religion) = row.religion { if religion.is_empty() { String::new() } else { decrypt_data_with_user_key(religion, &user_key)? } } else { String::new() }, + languages: if let Some(ref languages) = row.languages { if languages.is_empty() { String::new() } else { decrypt_data_with_user_key(languages, &user_key)? } } else { String::new() }, laws: Vec::new(), biomes: Vec::new(), issues: Vec::new(), @@ -95,8 +95,8 @@ pub fn get_world_list(conn: &Connection, user_id: &str, series_id: &str, lang: L let world = worlds_map.get_mut(&row.world_id).unwrap(); let element = SeriesWorldElementProps { id: element_id.clone(), - name: if let Some(ref element_name) = row.element_name { decrypt_data_with_user_key(element_name, &user_key)? } else { String::new() }, - description: if let Some(ref element_description) = row.element_description { decrypt_data_with_user_key(element_description, &user_key)? } else { String::new() }, + name: if let Some(ref element_name) = row.element_name { if element_name.is_empty() { String::new() } else { decrypt_data_with_user_key(element_name, &user_key)? } } else { String::new() }, + description: if let Some(ref element_description) = row.element_description { if element_description.is_empty() { String::new() } else { decrypt_data_with_user_key(element_description, &user_key)? } } else { String::new() }, }; if let Some(element_type) = row.element_type { diff --git a/src-tauri/src/domains/spell/service.rs b/src-tauri/src/domains/spell/service.rs index c99a440..19cb4c2 100644 --- a/src-tauri/src/domains/spell/service.rs +++ b/src-tauri/src/domains/spell/service.rs @@ -252,9 +252,9 @@ pub fn get_spell_detail(conn: &Connection, user_id: &str, spell_id: &str, lang: .ok_or_else(|| AppError::Internal(if lang == Lang::Fr { "Sort non trouvé.".to_string() } else { "Spell not found.".to_string() }))?; let decrypted_name: String = decrypt_data_with_user_key(&spell.name, &user_key)?; - let decrypted_description: String = if let Some(ref description) = spell.description { decrypt_data_with_user_key(description, &user_key)? } else { String::new() }; - let decrypted_appearance: String = if let Some(ref appearance) = spell.appearance { decrypt_data_with_user_key(appearance, &user_key)? } else { String::new() }; - let decrypted_tags: Option = if let Some(ref tags) = spell.tags { Some(decrypt_data_with_user_key(tags, &user_key)?) } else { None }; + let decrypted_description: String = if let Some(ref description) = spell.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_key)? } } else { String::new() }; + let decrypted_appearance: String = if let Some(ref appearance) = spell.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_key)? } } else { String::new() }; + let decrypted_tags: Option = if let Some(ref tags) = spell.tags { if tags.is_empty() { None } else { Some(decrypt_data_with_user_key(tags, &user_key)?) } } else { None }; let tag_ids: Vec = match decrypted_tags { Some(ref tags_str) => serde_json::from_str(tags_str).unwrap_or_default(), diff --git a/src-tauri/src/domains/sync/service.rs b/src-tauri/src/domains/sync/service.rs index a5fdf8b..0cd5eb0 100644 --- a/src-tauri/src/domains/sync/service.rs +++ b/src-tauri/src/domains/sync/service.rs @@ -199,7 +199,7 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat book_id: act_summary_record.book_id.clone(), user_id: act_summary_record.user_id.clone(), act_number: act_summary_record.act_index, - summary: if let Some(ref summary) = act_summary_record.summary { decrypt_data_with_user_key(summary, &user_encryption_key)? } else { String::new() }, + summary: if let Some(ref summary) = act_summary_record.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_encryption_key)? } } else { String::new() }, last_update: act_summary_record.last_update, }); } @@ -529,12 +529,12 @@ pub fn get_complete_sync_book(conn: &Connection, user_id: &str, sync_compare_dat user_id: spell_record.user_id, name: decrypt_data_with_user_key(&spell_record.name, &user_encryption_key)?, name_hash: spell_record.name_hash, - description: if let Some(ref description) = spell_record.description { decrypt_data_with_user_key(description, &user_encryption_key).ok().unwrap_or_default() } else { String::new() }, - appearance: if let Some(ref appearance) = spell_record.appearance { decrypt_data_with_user_key(appearance, &user_encryption_key).ok().unwrap_or_default() } else { String::new() }, - tags: if let Some(ref tags) = spell_record.tags { decrypt_data_with_user_key(tags, &user_encryption_key).ok().unwrap_or_default() } else { String::new() }, - power_level: if let Some(ref power_level) = spell_record.power_level { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } else { None }, - components: if let Some(ref components) = spell_record.components { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } else { None }, - limitations: if let Some(ref limitations) = spell_record.limitations { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None }, + description: if let Some(ref description) = spell_record.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, + appearance: if let Some(ref appearance) = spell_record.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, + tags: if let Some(ref tags) = spell_record.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_encryption_key).ok().unwrap_or_default() } } else { String::new() }, + power_level: if let Some(ref power_level) = spell_record.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } } else { None }, + components: if let Some(ref components) = spell_record.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } } else { None }, + limitations: if let Some(ref limitations) = spell_record.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } } else { None }, notes: if let Some(ref notes) = spell_record.notes { Some(decrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None }, last_update: spell_record.last_update, }); @@ -1125,7 +1125,7 @@ pub fn get_synced_books(conn: &Connection, user_id: &str, lang: Lang) -> AppResu id: current_book_id.to_string(), book_type: book_record.book_type.clone(), title: decrypt_data_with_user_key(&book_record.title, &user_encryption_key).unwrap_or_default(), - sub_title: if let Some(ref sub_title) = book_record.sub_title { decrypt_data_with_user_key(sub_title, &user_encryption_key).ok() } else { None }, + sub_title: if let Some(ref sub_title) = book_record.sub_title { if sub_title.is_empty() { None } else { decrypt_data_with_user_key(sub_title, &user_encryption_key).ok() } } else { None }, last_update: book_record.last_update, chapters: book_chapters, characters: book_characters, @@ -1250,7 +1250,7 @@ pub fn get_synced_series(conn: &Connection, user_id: &str, lang: Lang) -> AppRes synced_series_list.push(SyncedSeries { id: series_id.to_string(), name: decrypt_data_with_user_key(&series.name, &user_encryption_key).unwrap_or_default(), - description: if let Some(ref description) = series.description { decrypt_data_with_user_key(description, &user_encryption_key).ok() } else { None }, + description: if let Some(ref description) = series.description { if description.is_empty() { None } else { decrypt_data_with_user_key(description, &user_encryption_key).ok() } } else { None }, last_update: series.last_update, books, characters, diff --git a/src-tauri/src/domains/upload/service.rs b/src-tauri/src/domains/upload/service.rs index 62b1214..edffca0 100644 --- a/src-tauri/src/domains/upload/service.rs +++ b/src-tauri/src/domains/upload/service.rs @@ -100,7 +100,7 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let mut act_summaries: Vec = Vec::with_capacity(encrypted_act_summaries.len()); for act_summary in encrypted_act_summaries { - let decrypted_summary: String = if let Some(ref summary) = act_summary.summary { decrypt_data_with_user_key(summary, &user_encryption_key)? } else { String::new() }; + let decrypted_summary: String = if let Some(ref summary) = act_summary.summary { if summary.is_empty() { String::new() } else { decrypt_data_with_user_key(summary, &user_encryption_key)? } } else { String::new() }; act_summaries.push(BookActSummariesTable { summary_id: act_summary.act_sum_id, book_id: act_summary.book_id, user_id: act_summary.user_id, act_number: act_summary.act_index, @@ -333,13 +333,13 @@ pub fn upload_book_for_sync(conn: &Connection, user_id: &str, book_id: &str, lan let mut spells: Vec = Vec::with_capacity(encrypted_spells.len()); for spell in encrypted_spells { let decrypted_name: String = decrypt_data_with_user_key(&spell.name, &user_encryption_key)?; - let decrypted_description: String = if let Some(ref description) = spell.description { decrypt_data_with_user_key(description, &user_encryption_key)? } else { String::new() }; - let decrypted_appearance: String = if let Some(ref appearance) = spell.appearance { decrypt_data_with_user_key(appearance, &user_encryption_key)? } else { String::new() }; - let decrypted_tags: String = if let Some(ref tags) = spell.tags { decrypt_data_with_user_key(tags, &user_encryption_key)? } else { String::new() }; - let decrypted_power_level: Option = if let Some(ref power_level) = spell.power_level { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } else { None }; - let decrypted_components: Option = if let Some(ref components) = spell.components { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } else { None }; - let decrypted_limitations: Option = if let Some(ref limitations) = spell.limitations { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } else { None }; - let decrypted_notes: Option = if let Some(ref notes) = spell.notes { Some(decrypt_data_with_user_key(notes, &user_encryption_key)?) } else { None }; + let decrypted_description: String = if let Some(ref description) = spell.description { if description.is_empty() { String::new() } else { decrypt_data_with_user_key(description, &user_encryption_key)? } } else { String::new() }; + let decrypted_appearance: String = if let Some(ref appearance) = spell.appearance { if appearance.is_empty() { String::new() } else { decrypt_data_with_user_key(appearance, &user_encryption_key)? } } else { String::new() }; + let decrypted_tags: String = if let Some(ref tags) = spell.tags { if tags.is_empty() { String::new() } else { decrypt_data_with_user_key(tags, &user_encryption_key)? } } else { String::new() }; + let decrypted_power_level: Option = if let Some(ref power_level) = spell.power_level { if power_level.is_empty() { None } else { Some(decrypt_data_with_user_key(power_level, &user_encryption_key)?) } } else { None }; + let decrypted_components: Option = if let Some(ref components) = spell.components { if components.is_empty() { None } else { Some(decrypt_data_with_user_key(components, &user_encryption_key)?) } } else { None }; + let decrypted_limitations: Option = if let Some(ref limitations) = spell.limitations { if limitations.is_empty() { None } else { Some(decrypt_data_with_user_key(limitations, &user_encryption_key)?) } } else { None }; + let decrypted_notes: Option = if let Some(ref notes) = spell.notes { if notes.is_empty() { None } else { Some(decrypt_data_with_user_key(notes, &user_encryption_key)?) } } else { None }; spells.push(BookSpellsTable { spell_id: spell.spell_id, book_id: spell.book_id, user_id: spell.user_id, name: decrypted_name, diff --git a/src-tauri/src/domains/user/commands.rs b/src-tauri/src/domains/user/commands.rs index 4824f40..07c3a0e 100644 --- a/src-tauri/src/domains/user/commands.rs +++ b/src-tauri/src/domains/user/commands.rs @@ -5,7 +5,7 @@ use crate::crypto::{encryption, key_manager}; use crate::db::connection::DbManager; use crate::db::schema; use crate::domains::user::service; -use crate::error::AppError; +use crate::error::{AppError, AppResult}; use crate::shared::session::SessionState; #[derive(Deserialize)] @@ -180,3 +180,37 @@ pub fn update_user_info(data: UpdateUserData, db: State, session: Sta service::update_user_infos(conn, &user_key, &user_id, &data.username, &data.email, data.author_name.as_deref(), lang) } + +#[tauri::command] +pub fn dev_reset_all(db: State, session: State) -> AppResult { + if !cfg!(debug_assertions) { + return Err(AppError::Internal("dev_reset_all is only available in dev mode".to_string())); + } + + // 1. Close all DB connections + let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?; + let base_path = db_manager.base_path().clone(); + db_manager.close_all(); + drop(db_manager); + + // 2. Delete all eritors-local-*.db files (+ WAL/SHM) + if let Ok(entries) = std::fs::read_dir(&base_path) { + for entry in entries.flatten() { + let path = entry.path(); + if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + if name.starts_with("eritors-local-") { + let _ = std::fs::remove_file(&path); + } + } + } + } + + // 3. Clear vault (tokens, encryption keys, PIN hashes) + key_manager::clear_vault()?; + + // 4. Reset session + let mut session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?; + session_guard.user_id = None; + + Ok(true) +} diff --git a/src-tauri/src/domains/user/service.rs b/src-tauri/src/domains/user/service.rs index 698fcec..4185246 100644 --- a/src-tauri/src/domains/user/service.rs +++ b/src-tauri/src/domains/user/service.rs @@ -21,7 +21,7 @@ pub fn return_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppRes let username: String = decrypt_data_with_user_key(&user_infos_data.username, &user_encryption_key)?; let email: String = decrypt_data_with_user_key(&user_infos_data.email, &user_encryption_key)?; - let author_name: String = if let Some(ref author_name_val) = user_infos_data.author_name { decrypt_data_with_user_key(author_name_val, &user_encryption_key)? } else { String::new() }; + let author_name: String = if let Some(ref author_name_val) = user_infos_data.author_name { if author_name_val.is_empty() { String::new() } else { decrypt_data_with_user_key(author_name_val, &user_encryption_key)? } } else { String::new() }; Ok(UserInfoResponse { id: user_id.to_string(), diff --git a/src-tauri/src/domains/world/service.rs b/src-tauri/src/domains/world/service.rs index 32f2e34..5dc1a96 100644 --- a/src-tauri/src/domains/world/service.rs +++ b/src-tauri/src/domains/world/service.rs @@ -140,8 +140,8 @@ pub fn get_worlds(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) - if let Some(element_type) = query_row.element_type { let world_element: WorldElement = WorldElement { id: query_row.element_id.clone().unwrap_or_default(), - name: if let Some(ref element_name) = query_row.element_name { decrypt_data_with_user_key(element_name, &user_encryption_key)? } else { String::new() }, - description: if let Some(ref element_description) = query_row.element_description { decrypt_data_with_user_key(element_description, &user_encryption_key)? } else { String::new() }, + name: if let Some(ref element_name) = query_row.element_name { if element_name.is_empty() { String::new() } else { decrypt_data_with_user_key(element_name, &user_encryption_key)? } } else { String::new() }, + description: if let Some(ref element_description) = query_row.element_description { if element_description.is_empty() { String::new() } else { decrypt_data_with_user_key(element_description, &user_encryption_key)? } } else { String::new() }, element_type: None, }; push_element_to_world(&mut worlds[index], element_type, world_element); @@ -149,12 +149,12 @@ pub fn get_worlds(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) - } else { let mut new_world: WorldProps = WorldProps { id: query_row.world_id.clone(), - name: decrypt_data_with_user_key(&query_row.world_name, &user_encryption_key)?, - history: if let Some(ref history) = query_row.history { decrypt_data_with_user_key(history, &user_encryption_key)? } else { String::new() }, - politics: if let Some(ref politics) = query_row.politics { decrypt_data_with_user_key(politics, &user_encryption_key)? } else { String::new() }, - economy: if let Some(ref economy) = query_row.economy { decrypt_data_with_user_key(economy, &user_encryption_key)? } else { String::new() }, - religion: if let Some(ref religion) = query_row.religion { decrypt_data_with_user_key(religion, &user_encryption_key)? } else { String::new() }, - languages: if let Some(ref languages) = query_row.languages { decrypt_data_with_user_key(languages, &user_encryption_key)? } else { String::new() }, + name: if query_row.world_name.is_empty() { String::new() } else { decrypt_data_with_user_key(&query_row.world_name, &user_encryption_key)? }, + history: if let Some(ref history) = query_row.history { if history.is_empty() { String::new() } else { decrypt_data_with_user_key(history, &user_encryption_key)? } } else { String::new() }, + politics: if let Some(ref politics) = query_row.politics { if politics.is_empty() { String::new() } else { decrypt_data_with_user_key(politics, &user_encryption_key)? } } else { String::new() }, + economy: if let Some(ref economy) = query_row.economy { if economy.is_empty() { String::new() } else { decrypt_data_with_user_key(economy, &user_encryption_key)? } } else { String::new() }, + religion: if let Some(ref religion) = query_row.religion { if religion.is_empty() { String::new() } else { decrypt_data_with_user_key(religion, &user_encryption_key)? } } else { String::new() }, + languages: if let Some(ref languages) = query_row.languages { if languages.is_empty() { String::new() } else { decrypt_data_with_user_key(languages, &user_encryption_key)? } } else { String::new() }, laws: Vec::new(), biomes: Vec::new(), issues: Vec::new(), @@ -173,8 +173,8 @@ pub fn get_worlds(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) - if let Some(element_type) = query_row.element_type { let world_element: WorldElement = WorldElement { id: query_row.element_id.clone().unwrap_or_default(), - name: if let Some(ref element_name) = query_row.element_name { decrypt_data_with_user_key(element_name, &user_encryption_key)? } else { String::new() }, - description: if let Some(ref element_description) = query_row.element_description { decrypt_data_with_user_key(element_description, &user_encryption_key)? } else { String::new() }, + name: if let Some(ref element_name) = query_row.element_name { if element_name.is_empty() { String::new() } else { decrypt_data_with_user_key(element_name, &user_encryption_key)? } } else { String::new() }, + description: if let Some(ref element_description) = query_row.element_description { if element_description.is_empty() { String::new() } else { decrypt_data_with_user_key(element_description, &user_encryption_key)? } } else { String::new() }, element_type: None, }; push_element_to_world(&mut new_world, element_type, world_element); diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs index 7e35d8b..2bd412c 100644 --- a/src-tauri/src/error.rs +++ b/src-tauri/src/error.rs @@ -35,26 +35,22 @@ impl Serialize for AppError { where S: serde::Serializer, { - use serde::ser::SerializeStruct; - let mut state = serializer.serialize_struct("AppError", 2)?; - state.serialize_field("kind", &self.error_kind())?; - state.serialize_field("message", &self.to_string())?; - state.end() + serializer.serialize_str(&self.user_message()) } } impl AppError { - fn error_kind(&self) -> &str { + fn user_message(&self) -> String { match self { - AppError::Database(_) => "DATABASE", - AppError::Encryption(_) => "ENCRYPTION", - AppError::Keyring(_) => "KEYRING", - AppError::Auth(_) => "AUTH", - AppError::NotFound(_) => "NOT_FOUND", - AppError::Validation(_) => "VALIDATION", - AppError::Io(_) => "IO", - AppError::Json(_) => "JSON", - AppError::Internal(_) => "INTERNAL", + AppError::Database(e) => e.to_string(), + AppError::Encryption(msg) + | AppError::Keyring(msg) + | AppError::Auth(msg) + | AppError::NotFound(msg) + | AppError::Validation(msg) + | AppError::Internal(msg) => msg.clone(), + AppError::Io(e) => e.to_string(), + AppError::Json(e) => e.to_string(), } } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 030e683..8dbe764 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -33,6 +33,7 @@ pub fn run() { domains::user::commands::get_user_info, domains::user::commands::sync_user, domains::user::commands::update_user_info, + domains::user::commands::dev_reset_all, // ─── Offline ─────────────────────────────────── domains::offline::commands::offline_pin_set, domains::offline::commands::offline_pin_verify, diff --git a/src-tauri/src/shared/ai_models.rs b/src-tauri/src/shared/ai_models.rs new file mode 100644 index 0000000..e69de29 diff --git a/src-tauri/src/shared/session.rs b/src-tauri/src/shared/session.rs new file mode 100644 index 0000000..3c99a57 --- /dev/null +++ b/src-tauri/src/shared/session.rs @@ -0,0 +1,24 @@ +use std::sync::{Arc, Mutex}; + +use crate::shared::types::Lang; + +pub struct Session { + pub user_id: Option, + pub lang: Lang, +} + +impl Session { + pub fn new() -> Self { + Self { user_id: None, lang: Lang::Fr } + } + + pub fn get_user_id(&self) -> Result<&str, String> { + self.user_id.as_deref().ok_or_else(|| "No user session. Please log in first.".to_string()) + } +} + +pub type SessionState = Arc>; + +pub fn create_session() -> SessionState { + Arc::new(Mutex::new(Session::new())) +}