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:
406
electron/database/mappers/book.mapper.ts
Normal file
406
electron/database/mappers/book.mapper.ts
Normal 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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user