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

@@ -0,0 +1,406 @@
/**
* TypeScript interfaces (copied from lib/models for type safety)
*/
export interface Author {
id: string;
name: string;
lastName: string;
authorName?: string;
}
export interface ActChapter {
chapterInfoId: string;
chapterId: string;
title: string;
chapterOrder: number;
actId: number;
incidentId?: string;
plotPointId?: string;
summary: string;
goal: string;
}
export interface ChapterProps {
chapterId: string;
chapterOrder: number;
title: string;
chapterContent: ChapterContent;
}
export interface ChapterContent {
version: number;
content: string;
wordsCount: number;
}
export interface BookProps {
bookId: string;
type: string;
title: string;
author?: Author;
serie?: number;
subTitle?: string;
summary?: string;
publicationDate?: string;
desiredWordCount?: number;
totalWordCount?: number;
coverImage?: string;
chapters?: ChapterProps[];
}
export interface BookListProps {
id: string;
type: string;
authorId: string;
title: string;
subTitle?: string;
summary?: string;
serieId?: number;
desiredReleaseDate?: string;
desiredWordCount?: number;
wordCount?: number;
coverImage?: string;
bookMeta?: string;
}
export interface GuideLine {
tone: string;
atmosphere: string;
writingStyle: string;
themes: string;
symbolism: string;
motifs: string;
narrativeVoice: string;
pacing: string;
intendedAudience: string;
keyMessages: string;
}
export interface GuideLineAI {
narrativeType: number;
dialogueType: number;
globalResume: string;
atmosphere: string;
verbeTense: number;
langue: number;
themes: string;
}
export interface PlotPoint {
plotPointId: string;
title: string;
summary: string;
linkedIncidentId: string;
chapters?: ActChapter[];
}
export interface Incident {
incidentId: string;
title: string;
summary: string;
chapters?: ActChapter[];
}
export interface Issue {
id: string;
name: string;
}
/**
* Database row types (snake_case from SQLite)
*/
export interface DBBook {
book_id: string;
type: string;
author_id: string;
title: string;
hashed_title: string;
sub_title?: string;
hashed_sub_title: string;
summary: string;
serie_id?: number;
desired_release_date?: string;
desired_word_count?: number;
words_count?: number;
cover_image?: string;
book_meta?: string;
synced?: number;
}
export interface DBGuideLine {
user_id: string;
book_id: string;
tone: string;
atmosphere: string;
writing_style: string;
themes: string;
symbolism: string;
motifs: string;
narrative_voice: string;
pacing: string;
intended_audience: string;
key_messages: string;
meta_guide_line: string;
synced?: number;
}
export interface DBGuideLineAI {
user_id: string;
book_id: string;
global_resume: string;
themes: string;
verbe_tense: number;
narrative_type: number;
langue: number;
dialogue_type: number;
tone: string;
atmosphere: string;
current_resume: string;
meta: string;
synced?: number;
}
export interface DBPlotPoint {
plot_point_id: string;
title: string;
hashed_title: string;
summary?: string;
linked_incident_id?: string;
author_id: string;
book_id: string;
meta_plot: string;
synced?: number;
}
export interface DBIncident {
incident_id: string;
author_id: string;
book_id: string;
title: string;
hashed_title: string;
summary?: string;
meta_incident: string;
synced?: number;
}
export interface DBIssue {
issue_id: string;
author_id: string;
book_id: string;
name: string;
hashed_issue_name: string;
meta_issue: string;
synced?: number;
}
/**
* MAPPERS: DB → TypeScript Interfaces
*/
export function dbToBookList(dbBook: DBBook): BookListProps {
return {
id: dbBook.book_id,
type: dbBook.type,
authorId: dbBook.author_id,
title: dbBook.title,
subTitle: dbBook.sub_title,
summary: dbBook.summary,
serieId: dbBook.serie_id,
desiredReleaseDate: dbBook.desired_release_date,
desiredWordCount: dbBook.desired_word_count,
wordCount: dbBook.words_count,
coverImage: dbBook.cover_image,
bookMeta: dbBook.book_meta
};
}
export function dbToBook(dbBook: DBBook, author?: Author): BookProps {
return {
bookId: dbBook.book_id,
type: dbBook.type,
title: dbBook.title,
author,
serie: dbBook.serie_id,
subTitle: dbBook.sub_title,
summary: dbBook.summary,
publicationDate: dbBook.desired_release_date,
desiredWordCount: dbBook.desired_word_count,
totalWordCount: dbBook.words_count,
coverImage: dbBook.cover_image,
chapters: [] // Populated separately
};
}
export function dbToGuideLine(dbGuideLine: DBGuideLine): GuideLine {
return {
tone: dbGuideLine.tone,
atmosphere: dbGuideLine.atmosphere,
writingStyle: dbGuideLine.writing_style,
themes: dbGuideLine.themes,
symbolism: dbGuideLine.symbolism,
motifs: dbGuideLine.motifs,
narrativeVoice: dbGuideLine.narrative_voice,
pacing: dbGuideLine.pacing,
intendedAudience: dbGuideLine.intended_audience,
keyMessages: dbGuideLine.key_messages
};
}
export function dbToGuideLineAI(dbGuideLineAI: DBGuideLineAI): GuideLineAI {
return {
narrativeType: dbGuideLineAI.narrative_type,
dialogueType: dbGuideLineAI.dialogue_type,
globalResume: dbGuideLineAI.global_resume,
atmosphere: dbGuideLineAI.atmosphere,
verbeTense: dbGuideLineAI.verbe_tense,
langue: dbGuideLineAI.langue,
themes: dbGuideLineAI.themes
};
}
export function dbToPlotPoint(dbPlotPoint: DBPlotPoint): PlotPoint {
return {
plotPointId: dbPlotPoint.plot_point_id,
title: dbPlotPoint.title,
summary: dbPlotPoint.summary || '',
linkedIncidentId: dbPlotPoint.linked_incident_id || '',
chapters: [] // Populated separately
};
}
export function dbToIncident(dbIncident: DBIncident): Incident {
return {
incidentId: dbIncident.incident_id,
title: dbIncident.title,
summary: dbIncident.summary || '',
chapters: [] // Populated separately
};
}
export function dbToIssue(dbIssue: DBIssue): Issue {
return {
id: dbIssue.issue_id,
name: dbIssue.name
};
}
/**
* MAPPERS: TypeScript Interfaces → DB
*/
export function bookListToDb(book: BookListProps, synced: number = 0): DBBook {
return {
book_id: book.id,
type: book.type,
author_id: book.authorId,
title: book.title,
hashed_title: '', // Will be computed with hash function
sub_title: book.subTitle,
hashed_sub_title: '',
summary: book.summary || '',
serie_id: book.serieId,
desired_release_date: book.desiredReleaseDate,
desired_word_count: book.desiredWordCount,
words_count: book.wordCount,
cover_image: book.coverImage,
book_meta: book.bookMeta || '',
synced
};
}
export function bookToDb(book: BookProps, authorId: string, synced: number = 0): DBBook {
return {
book_id: book.bookId,
type: book.type,
author_id: authorId,
title: book.title,
hashed_title: '',
sub_title: book.subTitle,
hashed_sub_title: '',
summary: book.summary || '',
serie_id: book.serie,
desired_release_date: book.publicationDate,
desired_word_count: book.desiredWordCount,
words_count: book.totalWordCount,
cover_image: book.coverImage,
book_meta: '',
synced
};
}
export function guideLineToDb(guideLine: GuideLine, userId: string, bookId: string, synced: number = 0): DBGuideLine {
return {
user_id: userId,
book_id: bookId,
tone: guideLine.tone,
atmosphere: guideLine.atmosphere,
writing_style: guideLine.writingStyle,
themes: guideLine.themes,
symbolism: guideLine.symbolism,
motifs: guideLine.motifs,
narrative_voice: guideLine.narrativeVoice,
pacing: guideLine.pacing,
intended_audience: guideLine.intendedAudience,
key_messages: guideLine.keyMessages,
meta_guide_line: '',
synced
};
}
export function guideLineAIToDb(guideLineAI: GuideLineAI, userId: string, bookId: string, synced: number = 0): DBGuideLineAI {
return {
user_id: userId,
book_id: bookId,
global_resume: guideLineAI.globalResume,
themes: guideLineAI.themes,
verbe_tense: guideLineAI.verbeTense,
narrative_type: guideLineAI.narrativeType,
langue: guideLineAI.langue,
dialogue_type: guideLineAI.dialogueType,
tone: '',
atmosphere: guideLineAI.atmosphere,
current_resume: '',
meta: '',
synced
};
}
export function plotPointToDb(plotPoint: PlotPoint, authorId: string, bookId: string, synced: number = 0): DBPlotPoint {
return {
plot_point_id: plotPoint.plotPointId,
title: plotPoint.title,
hashed_title: '',
summary: plotPoint.summary,
linked_incident_id: plotPoint.linkedIncidentId,
author_id: authorId,
book_id: bookId,
meta_plot: '',
synced
};
}
export function incidentToDb(incident: Incident, authorId: string, bookId: string, synced: number = 0): DBIncident {
return {
incident_id: incident.incidentId,
author_id: authorId,
book_id: bookId,
title: incident.title,
hashed_title: '',
summary: incident.summary,
meta_incident: '',
synced
};
}
export function issueToDb(issue: Issue, authorId: string, bookId: string, synced: number = 0): DBIssue {
return {
issue_id: issue.id,
author_id: authorId,
book_id: bookId,
name: issue.name,
hashed_issue_name: '',
meta_issue: '',
synced
};
}