Add database schema, encryption utilities, and local database service

- Implement `schema.ts` for SQLite schema creation, indexing, and sync metadata initialization.
- Develop `encryption.ts` with AES-256-GCM encryption utilities for securing database data.
- Add `database.service.ts` to manage CRUD operations with encryption support, user-specific databases, and schema initialization.
- Integrate book, chapter, and character operations with encrypted content handling and sync preparation.
This commit is contained in:
natreex
2025-11-17 09:34:54 -05:00
parent 09768aafcf
commit d5eb1691d9
12 changed files with 2763 additions and 197 deletions

View File

@@ -27,6 +27,7 @@ import GuideTour, {GuideStep} from "@/components/GuideTour";
import {UserProps} from "@/lib/models/User";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import {getOfflineDataService} from "@/lib/services/offline-data.service";
interface MinMax {
min: number;
@@ -122,29 +123,43 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<
}
setIsAddingBook(true);
try {
const bookId: string = await System.authPostToServer<string>('book/add', {
title: title,
subTitle: subtitle,
type: selectedBookType,
summary: summary,
serie: 0,
publicationDate: publicationDate,
desiredWordCount: wordCount,
}, token, lang)
if (!bookId) {
errorMessage(t('addNewBookForm.error.addingBook'));
setIsAddingBook(false);
return;
}
const book: BookProps = {
bookId: bookId,
const offlineDataService = getOfflineDataService();
const bookData = {
title,
subTitle: subtitle,
type: selectedBookType,
summary, serie: 0,
summary,
serie: 0,
publicationDate,
desiredWordCount: wordCount
};
const bookId: string = await offlineDataService.createBook(
bookData,
session.user?.id || '',
async () => {
// Only called if online
const id = await System.authPostToServer<string>('book/add', {
title: title,
subTitle: subtitle,
type: selectedBookType,
summary: summary,
serie: 0,
publicationDate: publicationDate,
desiredWordCount: wordCount,
}, token, lang);
if (!id) {
throw new Error(t('addNewBookForm.error.addingBook'));
}
return id;
}
);
const book: BookProps = {
bookId: bookId,
...bookData
};
setSession({
...session,
user: {

View File

@@ -13,6 +13,7 @@ import GuideTour, {GuideStep} from "@/components/GuideTour";
import User from "@/lib/models/User";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import {getOfflineDataService} from "@/lib/services/offline-data.service";
export default function BookList() {
const {session, setSession} = useContext(SessionContext);
@@ -113,7 +114,10 @@ export default function BookList() {
async function getBooks(): Promise<void> {
setIsLoadingBooks(true);
try {
const bookResponse: BookListProps[] = await System.authGetQueryToServer<BookListProps[]>('books', accessToken, lang);
const offlineDataService = getOfflineDataService();
const bookResponse: BookListProps[] = await offlineDataService.getBooks(
() => System.authGetQueryToServer<BookListProps[]>('books', accessToken, lang)
);
if (bookResponse) {
const booksByType: Record<string, BookProps[]> = bookResponse.reduce((groups: Record<string, BookProps[]>, book: BookListProps): Record<string, BookProps[]> => {
const imageDataUrl: string = book.coverImage ? 'data:image/jpeg;base64,' + book.coverImage : '';