Fix absolute paths for data root. Allow setting data root via console args.

This commit is contained in:
Cohee
2024-04-12 19:53:46 +03:00
parent 3e1ff9bc25
commit dcbeab0aef
6 changed files with 112 additions and 56 deletions

View File

@ -102,6 +102,10 @@ const cliArguments = yargs(hideBin(process.argv))
type: 'boolean', type: 'boolean',
default: false, default: false,
describe: 'Enables whitelist mode', describe: 'Enables whitelist mode',
}).option('dataRoot', {
type: 'string',
default: null,
describe: 'Root directory for data storage',
}).parseSync(); }).parseSync();
// change all relative paths // change all relative paths
@ -121,6 +125,7 @@ const autorun = (cliArguments.autorun ?? getConfigValue('autorun', DEFAULT_AUTOR
const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN); const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN);
const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY); const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY);
const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST); const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST);
const dataRoot = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');
const basicAuthMode = getConfigValue('basicAuthMode', false); const basicAuthMode = getConfigValue('basicAuthMode', false);
const enableAccounts = getConfigValue('enableUserAccounts', false); const enableAccounts = getConfigValue('enableUserAccounts', false);
@ -526,7 +531,7 @@ const setupTasks = async function () {
// TODO: do endpoint init functions depend on certain directories existing or not existing? They should be callable // TODO: do endpoint init functions depend on certain directories existing or not existing? They should be callable
// in any order for encapsulation reasons, but right now it's unknown if that would break anything. // in any order for encapsulation reasons, but right now it's unknown if that would break anything.
await userModule.initUserStorage(); await userModule.initUserStorage(dataRoot);
await settingsEndpoint.init(); await settingsEndpoint.init();
const directories = await userModule.ensurePublicDirectoriesExist(); const directories = await userModule.ensurePublicDirectoriesExist();
await userModule.migrateUserData(); await userModule.migrateUserData();

View File

@ -1,5 +1,6 @@
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const mime = require('mime-types');
const express = require('express'); const express = require('express');
const sanitize = require('sanitize-filename'); const sanitize = require('sanitize-filename');
const fetch = require('node-fetch').default; const fetch = require('node-fetch').default;
@ -216,9 +217,11 @@ router.post('/download', jsonParser, async (request, response) => {
await finished(res.body.pipe(fileStream)); await finished(res.body.pipe(fileStream));
if (category === 'character') { if (category === 'character') {
response.sendFile(temp_path, { root: process.cwd() }, () => { const fileContent = fs.readFileSync(temp_path);
const contentType = mime.lookup(temp_path) || 'application/octet-stream';
response.setHeader('Content-Type', contentType);
response.send(fileContent);
fs.rmSync(temp_path); fs.rmSync(temp_path);
});
return; return;
} }

View File

@ -1,11 +1,13 @@
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const fsPromises = require('fs').promises;
const readline = require('readline'); const readline = require('readline');
const express = require('express'); const express = require('express');
const sanitize = require('sanitize-filename'); const sanitize = require('sanitize-filename');
const writeFileAtomicSync = require('write-file-atomic').sync; const writeFileAtomicSync = require('write-file-atomic').sync;
const yaml = require('yaml'); const yaml = require('yaml');
const _ = require('lodash'); const _ = require('lodash');
const mime = require('mime-types');
const jimp = require('jimp'); const jimp = require('jimp');
@ -1078,6 +1080,7 @@ router.post('/duplicate', jsonParser, async function (request, response) {
}); });
router.post('/export', jsonParser, async function (request, response) { router.post('/export', jsonParser, async function (request, response) {
try {
if (!request.body.format || !request.body.avatar_url) { if (!request.body.format || !request.body.avatar_url) {
return response.sendStatus(400); return response.sendStatus(400);
} }
@ -1089,8 +1092,13 @@ router.post('/export', jsonParser, async function (request, response) {
} }
switch (request.body.format) { switch (request.body.format) {
case 'png': case 'png': {
return response.sendFile(filename, { root: process.cwd() }); const fileContent = await fsPromises.readFile(filename);
const contentType = mime.lookup(filename) || 'image/png';
response.setHeader('Content-Type', contentType);
response.setHeader('Content-Disposition', `attachment; filename=${path.basename(filename)}`);
return response.send(fileContent);
}
case 'json': { case 'json': {
try { try {
let json = await readCharacterData(filename); let json = await readCharacterData(filename);
@ -1105,6 +1113,10 @@ router.post('/export', jsonParser, async function (request, response) {
} }
return response.sendStatus(400); return response.sendStatus(400);
} catch (err) {
console.error('Character export failed', err);
response.sendStatus(500);
}
}); });
module.exports = { router }; module.exports = { router };

View File

@ -122,7 +122,7 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
} }
const basePath = path.parse(contentItem.filename).base; const basePath = path.parse(contentItem.filename).base;
const targetPath = path.join(process.cwd(), contentTarget, basePath); const targetPath = path.join(contentTarget, basePath);
if (fs.existsSync(targetPath)) { if (fs.existsSync(targetPath)) {
console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`); console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`);

View File

@ -1,5 +1,7 @@
const fs = require('fs'); const fs = require('fs');
const fsPromises = require('fs').promises;
const path = require('path'); const path = require('path');
const mime = require('mime-types');
const express = require('express'); const express = require('express');
const sanitize = require('sanitize-filename'); const sanitize = require('sanitize-filename');
const jimp = require('jimp'); const jimp = require('jimp');
@ -165,7 +167,10 @@ const router = express.Router();
// Important: This route must be mounted as '/thumbnail'. It is used in the client code and saved to chat files. // Important: This route must be mounted as '/thumbnail'. It is used in the client code and saved to chat files.
router.get('/', jsonParser, async function (request, response) { router.get('/', jsonParser, async function (request, response) {
if (typeof request.query.file !== 'string' || typeof request.query.type !== 'string') return response.sendStatus(400); try{
if (typeof request.query.file !== 'string' || typeof request.query.type !== 'string') {
return response.sendStatus(400);
}
const type = request.query.type; const type = request.query.type;
const file = sanitize(request.query.file); const file = sanitize(request.query.file);
@ -183,11 +188,22 @@ router.get('/', jsonParser, async function (request, response) {
return response.sendStatus(403); return response.sendStatus(403);
} }
if (getConfigValue('disableThumbnails', false) == true) { const thumbnailsDisabled = getConfigValue('disableThumbnails', false);
let folder = getOriginalFolder(request.user.directories, type); if (thumbnailsDisabled) {
if (folder === undefined) return response.sendStatus(400); const folder = getOriginalFolder(request.user.directories, type);
if (folder === undefined) {
return response.sendStatus(400);
}
const pathToOriginalFile = path.join(folder, file); const pathToOriginalFile = path.join(folder, file);
return response.sendFile(pathToOriginalFile, { root: process.cwd() }); if (!fs.existsSync(pathToOriginalFile)) {
return response.sendStatus(404);
}
const contentType = mime.lookup(pathToOriginalFile) || 'image/png';
const originalFile = await fsPromises.readFile(pathToOriginalFile);
response.setHeader('Content-Type', contentType);
return response.send(originalFile);
} }
const pathToCachedFile = await generateThumbnail(request.user.directories, type, file); const pathToCachedFile = await generateThumbnail(request.user.directories, type, file);
@ -196,7 +212,18 @@ router.get('/', jsonParser, async function (request, response) {
return response.sendStatus(404); return response.sendStatus(404);
} }
return response.sendFile(pathToCachedFile, { root: process.cwd() }); if (!fs.existsSync(pathToCachedFile)) {
return response.sendStatus(404);
}
const contentType = mime.lookup(pathToCachedFile) || 'image/jpeg';
const cachedFile = await fsPromises.readFile(pathToCachedFile);
response.setHeader('Content-Type', contentType);
return response.send(cachedFile);
} catch (error) {
console.error('Failed getting thumbnail', error);
return response.sendStatus(500);
}
}); });
module.exports = { module.exports = {

View File

@ -15,10 +15,15 @@ const { getConfigValue, color, delay, setConfigValue, generateTimestamp } = requ
const { readSecret, writeSecret } = require('./endpoints/secrets'); const { readSecret, writeSecret } = require('./endpoints/secrets');
const KEY_PREFIX = 'user:'; const KEY_PREFIX = 'user:';
const DATA_ROOT = getConfigValue('dataRoot', './data');
const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false); const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64'); const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');
/**
* The root directory for user data.
* @type {string}
*/
let DATA_ROOT = './data';
/** /**
* Cache for user directories. * Cache for user directories.
* @type {Map<string, UserDirectoryList>} * @type {Map<string, UserDirectoryList>}
@ -312,9 +317,13 @@ function toKey(handle) {
/** /**
* Initializes the user storage. Currently a no-op. * Initializes the user storage. Currently a no-op.
* @param {string} dataRoot The root directory for user data
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function initUserStorage() { async function initUserStorage(dataRoot) {
DATA_ROOT = dataRoot;
console.log('Using data root:', color.green(DATA_ROOT));
console.log();
await storage.init({ await storage.init({
dir: path.join(DATA_ROOT, '_storage'), dir: path.join(DATA_ROOT, '_storage'),
ttl: true, ttl: true,