File size: 5,336 Bytes
6efa67a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | import fs from 'node:fs';
import path from 'node:path';
import express from 'express';
import sanitize from 'sanitize-filename';
import _ from 'lodash';
import { sync as writeFileAtomicSync } from 'write-file-atomic';
import { tryParse } from '../util.js';
/**
* Reads a World Info file and returns its contents
* @param {import('../users.js').UserDirectoryList} directories User directories
* @param {string} worldInfoName Name of the World Info file
* @param {boolean} allowDummy If true, returns an empty object if the file doesn't exist
* @returns {object} World Info file contents
*/
export function readWorldInfoFile(directories, worldInfoName, allowDummy) {
const dummyObject = allowDummy ? { entries: {} } : null;
if (!worldInfoName) {
return dummyObject;
}
const filename = sanitize(`${worldInfoName}.json`);
const pathToWorldInfo = path.join(directories.worlds, filename);
if (!fs.existsSync(pathToWorldInfo)) {
console.error(`World info file ${filename} doesn't exist.`);
return dummyObject;
}
const worldInfoText = fs.readFileSync(pathToWorldInfo, 'utf8');
const worldInfo = JSON.parse(worldInfoText);
return worldInfo;
}
export const router = express.Router();
router.post('/list', async (request, response) => {
try {
const data = [];
const jsonFiles = (await fs.promises.readdir(request.user.directories.worlds, { withFileTypes: true }))
.filter((file) => file.isFile() && path.extname(file.name).toLowerCase() === '.json')
.sort((a, b) => a.name.localeCompare(b.name));
for (const file of jsonFiles) {
try {
const filePath = path.join(request.user.directories.worlds, file.name);
const fileContents = await fs.promises.readFile(filePath, 'utf8');
const fileContentsParsed = tryParse(fileContents) || {};
const fileExtensions = fileContentsParsed?.extensions || {};
const fileNameWithoutExt = path.parse(file.name).name;
const fileData = {
file_id: fileNameWithoutExt,
name: fileContentsParsed?.name || fileNameWithoutExt,
extensions: _.isObjectLike(fileExtensions) ? fileExtensions : {},
};
data.push(fileData);
} catch (err) {
console.warn(`Error reading or parsing World Info file ${file.name}:`, err);
}
}
return response.send(data);
} catch (err) {
console.error('Error reading World Info directory:', err);
return response.sendStatus(500);
}
});
router.post('/get', (request, response) => {
if (!request.body?.name) {
return response.sendStatus(400);
}
const file = readWorldInfoFile(request.user.directories, request.body.name, true);
return response.send(file);
});
router.post('/delete', (request, response) => {
if (!request.body?.name) {
return response.sendStatus(400);
}
const worldInfoName = request.body.name;
const filename = sanitize(`${worldInfoName}.json`);
const pathToWorldInfo = path.join(request.user.directories.worlds, filename);
if (!fs.existsSync(pathToWorldInfo)) {
throw new Error(`World info file ${filename} doesn't exist.`);
}
fs.unlinkSync(pathToWorldInfo);
return response.sendStatus(200);
});
router.post('/import', (request, response) => {
if (!request.file) return response.sendStatus(400);
const filename = `${path.parse(sanitize(request.file.originalname)).name}.json`;
let fileContents = null;
if (request.body.convertedData) {
fileContents = request.body.convertedData;
} else {
const pathToUpload = path.join(request.file.destination, request.file.filename);
fileContents = fs.readFileSync(pathToUpload, 'utf8');
fs.unlinkSync(pathToUpload);
}
try {
const worldContent = JSON.parse(fileContents);
if (!('entries' in worldContent)) {
throw new Error('File must contain a world info entries list');
}
} catch (err) {
return response.status(400).send('Is not a valid world info file');
}
const pathToNewFile = path.join(request.user.directories.worlds, filename);
const worldName = path.parse(pathToNewFile).name;
if (!worldName) {
return response.status(400).send('World file must have a name');
}
writeFileAtomicSync(pathToNewFile, fileContents);
return response.send({ name: worldName });
});
router.post('/edit', (request, response) => {
if (!request.body) {
return response.sendStatus(400);
}
if (!request.body.name) {
return response.status(400).send('World file must have a name');
}
try {
if (!('entries' in request.body.data)) {
throw new Error('World info must contain an entries list');
}
} catch (err) {
return response.status(400).send('Is not a valid world info file');
}
const filename = sanitize(`${request.body.name}.json`);
const pathToFile = path.join(request.user.directories.worlds, filename);
writeFileAtomicSync(pathToFile, JSON.stringify(request.body.data, null, 4));
return response.send({ ok: true });
});
|