153 lines
5.4 KiB
JavaScript
153 lines
5.4 KiB
JavaScript
const modulename = 'WebServer:Resources';
|
|
import path from 'node:path';
|
|
import slash from 'slash';
|
|
import slug from 'slug';
|
|
import consoleFactory from '@lib/console';
|
|
import { SYM_SYSTEM_AUTHOR } from '@lib/symbols';
|
|
const console = consoleFactory(modulename);
|
|
|
|
//Helper functions
|
|
const isUndefined = (x) => (x === undefined);
|
|
const breakPath = (inPath) => slash(path.normalize(inPath)).split('/').filter(String);
|
|
const dynamicSort = (prop) => {
|
|
let sortOrder = 1;
|
|
if (prop[0] === '-') {
|
|
sortOrder = -1;
|
|
prop = prop.substr(1);
|
|
}
|
|
return function (a, b) {
|
|
const result = (a[prop] < b[prop]) ? -1 : (a[prop] > b[prop]) ? 1 : 0;
|
|
return result * sortOrder;
|
|
};
|
|
};
|
|
const getResourceSubPath = (resPath) => {
|
|
if (resPath.indexOf('system_resources') >= 0) return 'system_resources';
|
|
if (!path.isAbsolute(resPath)) return resPath;
|
|
|
|
let serverDataPathArr = breakPath(`${txConfig.server.dataPath}/resources`);
|
|
let resPathArr = breakPath(resPath);
|
|
for (let i = 0; i < serverDataPathArr.length; i++) {
|
|
if (isUndefined(resPathArr[i])) break;
|
|
if (serverDataPathArr[i].toLowerCase() == resPathArr[i].toLowerCase()) {
|
|
delete resPathArr[i];
|
|
}
|
|
}
|
|
resPathArr.pop();
|
|
resPathArr = resPathArr.filter(String);
|
|
|
|
if (resPathArr.length) {
|
|
return resPathArr.join('/');
|
|
} else {
|
|
return 'root';
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the resources list
|
|
* @param {object} ctx
|
|
*/
|
|
export default async function Resources(ctx) {
|
|
if (!txCore.fxRunner.child?.isAlive) {
|
|
return ctx.utils.render('main/message', {
|
|
message: '<strong>The resources list is only available when the server is running.</strong>',
|
|
});
|
|
}
|
|
|
|
const timeoutMessage = `<strong>Couldn't load the resources list.</strong> <br>
|
|
- Make sure the server is online (try to join it). <br>
|
|
- Make sure you don't have more than 1000 resources. <br>
|
|
- Make sure you are not running the FXServer outside of txAdmin. <br>
|
|
- Check the <strong>Live Console</strong> for any errors which may indicate that some resource has a malformed <code>fxmanifest.lua</code> file.`;
|
|
|
|
//Send command request
|
|
const cmdSuccess = txCore.fxRunner.sendCommand('txaReportResources', [], SYM_SYSTEM_AUTHOR);
|
|
if (!cmdSuccess) {
|
|
return ctx.utils.render('main/message', {message: timeoutMessage});
|
|
}
|
|
|
|
//Timer fot list delivery
|
|
let tListTimer;
|
|
let tErrorTimer;
|
|
const tList = new Promise((resolve, reject) => {
|
|
tListTimer = setInterval(() => {
|
|
if (
|
|
txCore.fxResources.resourceReport
|
|
&& (new Date() - txCore.fxResources.resourceReport.ts) <= 1000
|
|
&& Array.isArray(txCore.fxResources.resourceReport.resources)
|
|
) {
|
|
clearTimeout(tListTimer);
|
|
clearTimeout(tErrorTimer);
|
|
const resGroups = processResources(txCore.fxResources.resourceReport.resources);
|
|
const renderData = {
|
|
headerTitle: 'Resources',
|
|
resGroupsJS: JSON.stringify(resGroups),
|
|
resGroups,
|
|
disableActions: (ctx.admin.hasPermission('commands.resources')) ? '' : 'disabled',
|
|
};
|
|
resolve(['main/resources', renderData]);
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
//Timer for timing out
|
|
const tError = new Promise((resolve, reject) => {
|
|
tErrorTimer = setTimeout(() => {
|
|
clearTimeout(tListTimer);
|
|
resolve(['main/message', {message: timeoutMessage}]);
|
|
}, 1000);
|
|
});
|
|
|
|
//Start race and give output
|
|
const [view, renderData] = await Promise.race([tList, tError]);
|
|
return ctx.utils.render(view, renderData);
|
|
};
|
|
|
|
|
|
//================================================================
|
|
/**
|
|
* Returns the Processed Resource list.
|
|
* @param {array} resList
|
|
*/
|
|
function processResources(resList) {
|
|
//Clean resource data and add it so an object separated by subpaths
|
|
const resGroupList = {};
|
|
resList.forEach((resource) => {
|
|
if (isUndefined(resource.name) || isUndefined(resource.status) || isUndefined(resource.path) || resource.path === '') {
|
|
return;
|
|
}
|
|
const subPath = getResourceSubPath(resource.path);
|
|
const resData = {
|
|
name: resource.name,
|
|
divName: slug(resource.name),
|
|
status: resource.status,
|
|
statusClass: (resource.status === 'started') ? 'success' : 'danger',
|
|
// path: slash(path.normalize(resource.path)),
|
|
version: (resource.version) ? `(${resource.version.trim()})` : '',
|
|
author: (resource.author) ? `${resource.author.trim()}` : '',
|
|
description: (resource.description) ? resource.description.trim() : '',
|
|
};
|
|
|
|
if (resGroupList.hasOwnProperty(subPath)) {
|
|
resGroupList[subPath].push(resData);
|
|
} else {
|
|
resGroupList[subPath] = [resData];
|
|
}
|
|
});
|
|
|
|
//Generate final array with subpaths and div ids
|
|
const finalList = [];
|
|
Object.keys(resGroupList).forEach((subPath) => {
|
|
const subPathData = {
|
|
subPath: subPath,
|
|
divName: slug(subPath),
|
|
resources: resGroupList[subPath].sort(dynamicSort('name')),
|
|
};
|
|
finalList.push(subPathData);
|
|
});
|
|
|
|
// finalList = JSON.stringify(finalList, null, 2)
|
|
// console.log(finalList)
|
|
// return []
|
|
return finalList;
|
|
}
|