- Added `series-world.repo.ts` to handle database operations related to series worlds and their elements. - Implemented `series-sync.repo.ts` for managing synchronization between books and series. - Expanded `spell.ipc.ts` data models to include `seriesSpellId` for spell synchronization. - Refactored `insertSpellTag` method in `spelltag.repo.ts` for improved error handling and logic clarity.
200 lines
8.5 KiB
TypeScript
200 lines
8.5 KiB
TypeScript
import { getUserEncryptionKey } from "../keyManager.js";
|
|
import System from "../System.js";
|
|
import SeriesSyncRepo, { SyncElementType } from "../repositories/series-sync.repo.js";
|
|
|
|
export interface SeriesSyncUploadPayload {
|
|
type: SyncElementType;
|
|
bookElementId: string;
|
|
field: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface SeriesSyncResult {
|
|
success: boolean;
|
|
updatedCount: number;
|
|
}
|
|
|
|
export default class SeriesSync {
|
|
/**
|
|
* Uploads a field value from a book element to its linked series element,
|
|
* and propagates the change to all other book elements linked to the same series element.
|
|
* @param userId - The unique identifier of the user
|
|
* @param payload - The upload payload containing type, bookElementId, field, and value
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The upload response
|
|
*/
|
|
public static uploadFieldToSeries(userId: string, payload: SeriesSyncUploadPayload, lang: 'fr' | 'en' = 'fr'): SeriesSyncResult {
|
|
const { type, bookElementId, field, value } = payload;
|
|
|
|
const seriesElementId: string | null = this.getSeriesLink(userId, type, bookElementId, lang);
|
|
if (!seriesElementId) {
|
|
throw new Error(lang === 'fr' ? `Cet élément n'est pas lié à une série.` : `This element is not linked to a series.`);
|
|
}
|
|
|
|
const userKey: string = getUserEncryptionKey(userId);
|
|
const encryptedValue: string = value ? System.encryptDataWithUserKey(value, userKey) : '';
|
|
|
|
const dbField: string = this.mapFieldToDbColumn(type, field);
|
|
|
|
this.updateSeriesElement(userId, type, seriesElementId, dbField, encryptedValue, lang);
|
|
const updatedCount: number = this.updateLinkedBookElements(userId, type, seriesElementId, dbField, encryptedValue, lang);
|
|
|
|
return {
|
|
success: true,
|
|
updatedCount: updatedCount + 1
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets the series element ID linked to a book element.
|
|
* @param userId - The unique identifier of the user
|
|
* @param type - The type of element (character, world, location, spell)
|
|
* @param bookElementId - The unique identifier of the book element
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The series element ID or null if not linked
|
|
*/
|
|
private static getSeriesLink(userId: string, type: SyncElementType, bookElementId: string, lang: 'fr' | 'en'): string | null {
|
|
switch (type) {
|
|
case 'character':
|
|
return SeriesSyncRepo.getCharacterSeriesLink(userId, bookElementId, lang);
|
|
case 'world':
|
|
return SeriesSyncRepo.getWorldSeriesLink(userId, bookElementId, lang);
|
|
case 'location':
|
|
return SeriesSyncRepo.getLocationSeriesLink(userId, bookElementId, lang);
|
|
case 'spell':
|
|
return SeriesSyncRepo.getSpellSeriesLink(userId, bookElementId, lang);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maps frontend field names to database column names.
|
|
* @param type - The type of element (character, world, location, spell)
|
|
* @param field - The frontend field name to map
|
|
* @returns The corresponding database column name
|
|
*/
|
|
private static mapFieldToDbColumn(type: SyncElementType, field: string): string {
|
|
const characterFieldMap: Record<string, string> = {
|
|
'name': 'first_name',
|
|
'firstName': 'first_name',
|
|
'lastName': 'last_name',
|
|
'nickname': 'nickname',
|
|
'age': 'age',
|
|
'gender': 'gender',
|
|
'species': 'species',
|
|
'nationality': 'nationality',
|
|
'status': 'status',
|
|
'title': 'title',
|
|
'category': 'category',
|
|
'role': 'role',
|
|
'biography': 'biography',
|
|
'history': 'history',
|
|
'speechPattern': 'speech_pattern',
|
|
'catchphrase': 'catchphrase',
|
|
'residence': 'residence',
|
|
'notes': 'notes',
|
|
'color': 'color'
|
|
};
|
|
|
|
const worldFieldMap: Record<string, string> = {
|
|
'name': 'name',
|
|
'history': 'history',
|
|
'politics': 'politics',
|
|
'economy': 'economy',
|
|
'religion': 'religion',
|
|
'languages': 'languages'
|
|
};
|
|
|
|
const locationFieldMap: Record<string, string> = {
|
|
'name': 'name',
|
|
'loc_name': 'loc_name'
|
|
};
|
|
|
|
const spellFieldMap: Record<string, string> = {
|
|
'name': 'name',
|
|
'description': 'description',
|
|
'type': 'type',
|
|
'level': 'level',
|
|
'range': 'range',
|
|
'duration': 'duration',
|
|
'cost': 'cost',
|
|
'effect': 'effect',
|
|
'components': 'components',
|
|
'notes': 'notes'
|
|
};
|
|
|
|
switch (type) {
|
|
case 'character':
|
|
return characterFieldMap[field] || field;
|
|
case 'world':
|
|
return worldFieldMap[field] || field;
|
|
case 'location':
|
|
return locationFieldMap[field] || field;
|
|
case 'spell':
|
|
return spellFieldMap[field] || field;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the series element field.
|
|
* @param userId - The unique identifier of the user
|
|
* @param type - The type of element (character, world, location, spell)
|
|
* @param seriesElementId - The unique identifier of the series element
|
|
* @param field - The database column name to update
|
|
* @param encryptedValue - The encrypted value to set
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns True if updated successfully
|
|
*/
|
|
private static updateSeriesElement(userId: string, type: SyncElementType, seriesElementId: string, field: string, encryptedValue: string, lang: 'fr' | 'en'): boolean {
|
|
switch (type) {
|
|
case 'character':
|
|
return SeriesSyncRepo.updateSeriesCharacterField(userId, seriesElementId, field, encryptedValue, lang);
|
|
case 'world':
|
|
return SeriesSyncRepo.updateSeriesWorldField(userId, seriesElementId, field, encryptedValue, lang);
|
|
case 'location':
|
|
return SeriesSyncRepo.updateSeriesLocationField(userId, seriesElementId, field, encryptedValue, lang);
|
|
case 'spell':
|
|
return SeriesSyncRepo.updateSeriesSpellField(userId, seriesElementId, field, encryptedValue, lang);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates all book elements linked to the series element.
|
|
* @param userId - The unique identifier of the user
|
|
* @param type - The type of element (character, world, location, spell)
|
|
* @param seriesElementId - The unique identifier of the series element
|
|
* @param field - The database column name to update
|
|
* @param encryptedValue - The encrypted value to set
|
|
* @param lang - The language for error messages ('fr' or 'en')
|
|
* @returns The number of book elements updated
|
|
*/
|
|
private static updateLinkedBookElements(userId: string, type: SyncElementType, seriesElementId: string, field: string, encryptedValue: string, lang: 'fr' | 'en'): number {
|
|
const bookField: string = this.mapSeriesFieldToBookField(type, field);
|
|
|
|
switch (type) {
|
|
case 'character':
|
|
return SeriesSyncRepo.updateLinkedBookCharactersField(userId, seriesElementId, bookField, encryptedValue, lang);
|
|
case 'world':
|
|
return SeriesSyncRepo.updateLinkedBookWorldsField(userId, seriesElementId, bookField, encryptedValue, lang);
|
|
case 'location':
|
|
return SeriesSyncRepo.updateLinkedBookLocationsField(userId, seriesElementId, bookField, encryptedValue, lang);
|
|
case 'spell':
|
|
return SeriesSyncRepo.updateLinkedBookSpellsField(userId, seriesElementId, bookField, encryptedValue, lang);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maps series table field names to book table field names (if different).
|
|
* @param type - The type of element (character, world, location, spell)
|
|
* @param seriesField - The series table field name
|
|
* @returns The corresponding book table field name
|
|
*/
|
|
private static mapSeriesFieldToBookField(type: SyncElementType, seriesField: string): string {
|
|
if (type === 'location') {
|
|
if (seriesField === 'name') {
|
|
return 'loc_name';
|
|
}
|
|
}
|
|
return seriesField;
|
|
}
|
|
}
|