Unset chat field on import/export Closes #3781
| @@ -15,9 +15,9 @@ import storage from 'node-persist'; | |||
| 15 | 15 | ||
| 16 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; | 16 | import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js'; |
| 17 | import { default as validateAvatarUrlMiddleware, getFileNameValidationFunction } from '../middleware/validateFileName.js'; | 17 | import { default as validateAvatarUrlMiddleware, getFileNameValidationFunction } from '../middleware/validateFileName.js'; |
| 18 | import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap, getConfigValue } from '../util.js'; | 18 | import { deepMerge, humanizedISO8601DateTime, tryParse, extractFileFromZipBuffer, MemoryLimitedMap, getConfigValue, mutateJsonString } from '../util.js'; |
| 19 | import { TavernCardValidator } from '../validator/TavernCardValidator.js'; | 19 | import { TavernCardValidator } from '../validator/TavernCardValidator.js'; |
| 20 | import { parse, write } from '../character-card-parser.js'; | 20 | import { parse, read, write } from '../character-card-parser.js'; |
| 21 | import { readWorldInfoFile } from './worldinfo.js'; | 21 | import { readWorldInfoFile } from './worldinfo.js'; |
| 22 | import { invalidateThumbnail } from './thumbnails.js'; | 22 | import { invalidateThumbnail } from './thumbnails.js'; |
| 23 | import { importRisuSprites } from './sprites.js'; | 23 | import { importRisuSprites } from './sprites.js'; |
| @@ -490,10 +490,13 @@ function convertToV2(char, directories) { | |||
| 490 | return result; | 490 | return result; |
| 491 | } | 491 | } |
| 492 | 492 | ||
| 493 | 493 | /** | |
| 494 | function unsetFavFlag(char) { | 494 | * Removes fields that are not meant to be shared. |
| 495 | */ | ||
| 496 | function unsetPrivateFields(char) { | ||
| 495 | _.set(char, 'fav', false); | 497 | _.set(char, 'fav', false); |
| 496 | _.set(char, 'data.extensions.fav', false); | 498 | _.set(char, 'data.extensions.fav', false); |
| 499 | _.unset(char, 'chat'); | ||
| 497 | } | 500 | } |
| 498 | 501 | ||
| 499 | function readFromV2(char) { | 502 | function readFromV2(char) { |
| @@ -779,7 +782,7 @@ async function importFromCharX(uploadPath, { request }, preservedFileName) { | |||
| 779 | } | 782 | } |
| 780 | } | 783 | } |
| 781 | 784 | ||
| 782 | unsetFavFlag(card); | 785 | unsetPrivateFields(card); |
| 783 | card['create_date'] = humanizedISO8601DateTime(); | 786 | card['create_date'] = humanizedISO8601DateTime(); |
| 784 | card.name = sanitize(card.name); | 787 | card.name = sanitize(card.name); |
| 785 | const fileName = preservedFileName || getPngName(card.name, request.user.directories); | 788 | const fileName = preservedFileName || getPngName(card.name, request.user.directories); |
| @@ -803,7 +806,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) { | |||
| 803 | if (jsonData.spec !== undefined) { | 806 | if (jsonData.spec !== undefined) { |
| 804 | console.info(`Importing from ${jsonData.spec} json`); | 807 | console.info(`Importing from ${jsonData.spec} json`); |
| 805 | importRisuSprites(request.user.directories, jsonData); | 808 | importRisuSprites(request.user.directories, jsonData); |
| 806 | unsetFavFlag(jsonData); | 809 | unsetPrivateFields(jsonData); |
| 807 | jsonData = readFromV2(jsonData); | 810 | jsonData = readFromV2(jsonData); |
| 808 | jsonData['create_date'] = humanizedISO8601DateTime(); | 811 | jsonData['create_date'] = humanizedISO8601DateTime(); |
| 809 | const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories); | 812 | const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories); |
| @@ -886,7 +889,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) { | |||
| 886 | if (jsonData.spec !== undefined) { | 889 | if (jsonData.spec !== undefined) { |
| 887 | console.info(`Found a ${jsonData.spec} character file.`); | 890 | console.info(`Found a ${jsonData.spec} character file.`); |
| 888 | importRisuSprites(request.user.directories, jsonData); | 891 | importRisuSprites(request.user.directories, jsonData); |
| 889 | unsetFavFlag(jsonData); | 892 | unsetPrivateFields(jsonData); |
| 890 | jsonData = readFromV2(jsonData); | 893 | jsonData = readFromV2(jsonData); |
| 891 | jsonData['create_date'] = humanizedISO8601DateTime(); | 894 | jsonData['create_date'] = humanizedISO8601DateTime(); |
| 892 | const char = JSON.stringify(jsonData); | 895 | const char = JSON.stringify(jsonData); |
| @@ -1423,11 +1426,14 @@ router.post('/export', validateAvatarUrlMiddleware, async function (request, res | |||
| 1423 | 1426 | ||
| 1424 | switch (request.body.format) { | 1427 | switch (request.body.format) { |
| 1425 | case 'png': { | 1428 | case 'png': { |
| 1426 | const fileContent = await fsPromises.readFile(filename); | 1429 | const rawBuffer = await fsPromises.readFile(filename); |
| 1430 | const rawData = read(rawBuffer); | ||
| 1431 | const mutatedData = mutateJsonString(rawData, unsetPrivateFields); | ||
| 1432 | const mutatedBuffer = write(rawBuffer, mutatedData); | ||
| 1427 | const contentType = mime.lookup(filename) || 'image/png'; | 1433 | const contentType = mime.lookup(filename) || 'image/png'; |
| 1428 | response.setHeader('Content-Type', contentType); | 1434 | response.setHeader('Content-Type', contentType); |
| 1429 | response.setHeader('Content-Disposition', `attachment; filename="${encodeURI(path.basename(filename))}"`); | 1435 | response.setHeader('Content-Disposition', `attachment; filename="${encodeURI(path.basename(filename))}"`); |
| 1430 | return response.send(fileContent); | 1436 | return response.send(mutatedBuffer); |
| 1431 | } | 1437 | } |
| 1432 | case 'json': { | 1438 | case 'json': { |
| 1433 | try { | 1439 | try { |
| @@ -1054,3 +1054,20 @@ export function setWindowTitle(title) { | |||
| 1054 | process.stdout.write(`\x1b]2;${title}\x1b\x5c`); | 1054 | process.stdout.write(`\x1b]2;${title}\x1b\x5c`); |
| 1055 | } | 1055 | } |
| 1056 | } | 1056 | } |
| 1057 | |||
| 1058 | /** | ||
| 1059 | * Parses a JSON string and applies a mutation function to the parsed object. | ||
| 1060 | * @param {string} jsonString JSON string to parse | ||
| 1061 | * @param {function(any): void} mutation Mutation function to apply to the parsed JSON object | ||
| 1062 | * @returns {string} Mutated JSON string | ||
| 1063 | */ | ||
| 1064 | export function mutateJsonString(jsonString, mutation) { | ||
| 1065 | try { | ||
| 1066 | const json = JSON.parse(jsonString); | ||
| 1067 | mutation(json); | ||
| 1068 | return JSON.stringify(json); | ||
| 1069 | } catch (error) { | ||
| 1070 | console.error('Error parsing or mutating JSON:', error); | ||
| 1071 | return jsonString; | ||
| 1072 | } | ||
| 1073 | } | ||