mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Improve extension type indication
This commit is contained in:
@ -90,7 +90,7 @@ label[for="extensions_autoconnect"] {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 10px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.extensions_info .extension_name {
|
.extensions_info .extension_name {
|
||||||
|
@ -21,7 +21,11 @@ export {
|
|||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
export let extensionNames = [];
|
export let extensionNames = [];
|
||||||
/** @type {Record<string, string>} */
|
/**
|
||||||
|
* Holds the type of each extension.
|
||||||
|
* Don't use this directly, use getExtensionType instead!
|
||||||
|
* @type {Record<string, string>}
|
||||||
|
*/
|
||||||
export let extensionTypes = {};
|
export let extensionTypes = {};
|
||||||
|
|
||||||
let manifests = {};
|
let manifests = {};
|
||||||
@ -198,6 +202,16 @@ function showHideExtensionsMenu() {
|
|||||||
// Periodically check for new extensions
|
// Periodically check for new extensions
|
||||||
const menuInterval = setInterval(showHideExtensionsMenu, 1000);
|
const menuInterval = setInterval(showHideExtensionsMenu, 1000);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of an extension based on its external ID.
|
||||||
|
* @param {string} externalId External ID of the extension (excluding or including the leading 'third-party/')
|
||||||
|
* @returns {string} Type of the extension (global, local, system, or empty string if not found)
|
||||||
|
*/
|
||||||
|
function getExtensionType(externalId) {
|
||||||
|
const id = Object.keys(extensionTypes).find(id => id === externalId || (id.startsWith('third-party') && id.endsWith(externalId)));
|
||||||
|
return id ? extensionTypes[id] : '';
|
||||||
|
}
|
||||||
|
|
||||||
async function doExtrasFetch(endpoint, args) {
|
async function doExtrasFetch(endpoint, args) {
|
||||||
if (!args) {
|
if (!args) {
|
||||||
args = {};
|
args = {};
|
||||||
@ -457,8 +471,17 @@ function updateStatus(success) {
|
|||||||
$('#extensions_status').attr('class', _class);
|
$('#extensions_status').attr('class', _class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a CSS file for an extension.
|
||||||
|
* @param {string} name Extension name
|
||||||
|
* @param {object} manifest Extension manifest
|
||||||
|
* @returns {Promise<void>} When the CSS is loaded
|
||||||
|
*/
|
||||||
function addExtensionStyle(name, manifest) {
|
function addExtensionStyle(name, manifest) {
|
||||||
if (manifest.css) {
|
if (!manifest.css) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const url = `/scripts/extensions/${name}/${manifest.css}`;
|
const url = `/scripts/extensions/${name}/${manifest.css}`;
|
||||||
|
|
||||||
@ -479,11 +502,17 @@ function addExtensionStyle(name, manifest) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a JS file for an extension.
|
||||||
|
* @param {string} name Extension name
|
||||||
|
* @param {object} manifest Extension manifest
|
||||||
|
* @returns {Promise<void>} When the script is loaded
|
||||||
|
*/
|
||||||
|
function addExtensionScript(name, manifest) {
|
||||||
|
if (!manifest.js) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addExtensionScript(name, manifest) {
|
|
||||||
if (manifest.js) {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const url = `/scripts/extensions/${name}/${manifest.js}`;
|
const url = `/scripts/extensions/${name}/${manifest.js}`;
|
||||||
let ready = false;
|
let ready = false;
|
||||||
@ -495,11 +524,10 @@ function addExtensionScript(name, manifest) {
|
|||||||
script.src = url;
|
script.src = url;
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.onerror = function (err) {
|
script.onerror = function (err) {
|
||||||
reject(err, script);
|
reject(err);
|
||||||
};
|
};
|
||||||
script.onload = script.onreadystatechange = function () {
|
script.onload = function () {
|
||||||
// console.log(this.readyState); // uncomment this line to see which ready states are called.
|
if (!ready) {
|
||||||
if (!ready && (!this.readyState || this.readyState == 'complete')) {
|
|
||||||
ready = true;
|
ready = true;
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@ -509,11 +537,6 @@ function addExtensionScript(name, manifest) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates HTML string for displaying an extension in the UI.
|
* Generates HTML string for displaying an extension in the UI.
|
||||||
*
|
*
|
||||||
@ -526,6 +549,22 @@ function addExtensionScript(name, manifest) {
|
|||||||
* @return {string} - The HTML string that represents the extension.
|
* @return {string} - The HTML string that represents the extension.
|
||||||
*/
|
*/
|
||||||
function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal, checkboxClass) {
|
function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal, checkboxClass) {
|
||||||
|
function getExtensionIcon() {
|
||||||
|
const type = getExtensionType(name);
|
||||||
|
switch (type) {
|
||||||
|
case 'global':
|
||||||
|
return '<i class="fa-fw fa-solid fa-server" data-i18n="[title]ext_type_global" title="This is a global extension, available for all users."></i>';
|
||||||
|
case 'local':
|
||||||
|
return '<i class="fa-fw fa-solid fa-user" data-i18n="[title]ext_type_local" title="This is a local extension, available only for you."></i>';
|
||||||
|
case 'system':
|
||||||
|
return '<i class="fa-fw fa-solid fa-cog" data-i18n="[title]ext_type_system" title="This is a built-in extension. It cannot be deleted and updates with the app."></i>';
|
||||||
|
default:
|
||||||
|
return '<i class="fa-fw fa-solid fa-question" title="Unknown extension type."></i>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUserAdmin = isAdmin();
|
||||||
|
const extensionIcon = getExtensionIcon();
|
||||||
const displayName = manifest.display_name;
|
const displayName = manifest.display_name;
|
||||||
let displayVersion = manifest.version ? ` v${manifest.version}` : '';
|
let displayVersion = manifest.version ? ` v${manifest.version}` : '';
|
||||||
const externalId = name.replace('third-party', '');
|
const externalId = name.replace('third-party', '');
|
||||||
@ -540,6 +579,7 @@ function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal,
|
|||||||
|
|
||||||
let deleteButton = isExternal ? `<button class="btn_delete menu_button" data-name="${externalId}" title="Delete"><i class="fa-fw fa-solid fa-trash-can"></i></button>` : '';
|
let deleteButton = isExternal ? `<button class="btn_delete menu_button" data-name="${externalId}" title="Delete"><i class="fa-fw fa-solid fa-trash-can"></i></button>` : '';
|
||||||
let updateButton = isExternal ? `<button class="btn_update menu_button displayNone" data-name="${externalId}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button>` : '';
|
let updateButton = isExternal ? `<button class="btn_update menu_button displayNone" data-name="${externalId}" title="Update available"><i class="fa-solid fa-download fa-fw"></i></button>` : '';
|
||||||
|
let moveButton = isExternal && isUserAdmin ? `<button class="btn_move menu_button" data-name="${externalId}" title="Move"><i class="fa-solid fa-folder-tree fa-fw"></i></button>` : '';
|
||||||
let modulesInfo = '';
|
let modulesInfo = '';
|
||||||
|
|
||||||
if (isActive && Array.isArray(manifest.optional)) {
|
if (isActive && Array.isArray(manifest.optional)) {
|
||||||
@ -565,6 +605,9 @@ function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal,
|
|||||||
<div class="extension_toggle">
|
<div class="extension_toggle">
|
||||||
${toggleElement}
|
${toggleElement}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="extension_icon">
|
||||||
|
${extensionIcon}
|
||||||
|
</div>
|
||||||
<div class="flexGrow">
|
<div class="flexGrow">
|
||||||
${originHtml}
|
${originHtml}
|
||||||
<span class="${isActive ? 'extension_enabled' : isDisabled ? 'extension_disabled' : 'extension_missing'}">
|
<span class="${isActive ? 'extension_enabled' : isDisabled ? 'extension_disabled' : 'extension_missing'}">
|
||||||
@ -577,6 +620,7 @@ function generateExtensionHtml(name, manifest, isActive, isDisabled, isExternal,
|
|||||||
|
|
||||||
<div class="extension_actions flex-container alignItemsCenter">
|
<div class="extension_actions flex-container alignItemsCenter">
|
||||||
${updateButton}
|
${updateButton}
|
||||||
|
${moveButton}
|
||||||
${deleteButton}
|
${deleteButton}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@ -622,6 +666,7 @@ function getModuleInformation() {
|
|||||||
* Generates the HTML strings for all extensions and displays them in a popup.
|
* Generates the HTML strings for all extensions and displays them in a popup.
|
||||||
*/
|
*/
|
||||||
async function showExtensionsDetails() {
|
async function showExtensionsDetails() {
|
||||||
|
const abortController = new AbortController();
|
||||||
let popupPromise;
|
let popupPromise;
|
||||||
try {
|
try {
|
||||||
const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">Built-in Extensions:</h3></div>');
|
const htmlDefault = $('<div class="marginBot10"><h3 class="textAlignCenter">Built-in Extensions:</h3></div>');
|
||||||
@ -688,13 +733,14 @@ async function showExtensionsDetails() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
popupPromise = popup.show();
|
popupPromise = popup.show();
|
||||||
checkForUpdatesManual().finally(() => htmlLoading.remove());
|
checkForUpdatesManual(abortController.signal).finally(() => htmlLoading.remove());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toastr.error('Error loading extensions. See browser console for details.');
|
toastr.error('Error loading extensions. See browser console for details.');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
if (popupPromise) {
|
if (popupPromise) {
|
||||||
await popupPromise;
|
await popupPromise;
|
||||||
|
abortController.abort();
|
||||||
}
|
}
|
||||||
if (requiresReload) {
|
if (requiresReload) {
|
||||||
showLoader();
|
showLoader();
|
||||||
@ -702,7 +748,6 @@ async function showExtensionsDetails() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the click event for the update button of an extension.
|
* Handles the click event for the update button of an extension.
|
||||||
* This function makes a POST request to '/update_extension' with the extension's name.
|
* This function makes a POST request to '/update_extension' with the extension's name.
|
||||||
@ -712,7 +757,7 @@ async function showExtensionsDetails() {
|
|||||||
async function onUpdateClick() {
|
async function onUpdateClick() {
|
||||||
const isCurrentUserAdmin = isAdmin();
|
const isCurrentUserAdmin = isAdmin();
|
||||||
const extensionName = $(this).data('name');
|
const extensionName = $(this).data('name');
|
||||||
const isGlobal = extensionTypes[extensionName] === 'global';
|
const isGlobal = getExtensionType(extensionName) === 'global';
|
||||||
if (isGlobal && !isCurrentUserAdmin) {
|
if (isGlobal && !isCurrentUserAdmin) {
|
||||||
toastr.error(t`You don't have permission to update global extensions.`);
|
toastr.error(t`You don't have permission to update global extensions.`);
|
||||||
return;
|
return;
|
||||||
@ -734,7 +779,7 @@ async function updateExtension(extensionName, quiet) {
|
|||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
extensionName,
|
extensionName,
|
||||||
global: extensionTypes[extensionName] === 'global',
|
global: getExtensionType(extensionName) === 'global',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -765,7 +810,7 @@ async function updateExtension(extensionName, quiet) {
|
|||||||
async function onDeleteClick() {
|
async function onDeleteClick() {
|
||||||
const extensionName = $(this).data('name');
|
const extensionName = $(this).data('name');
|
||||||
const isCurrentUserAdmin = isAdmin();
|
const isCurrentUserAdmin = isAdmin();
|
||||||
const isGlobal = extensionTypes[extensionName] === 'global';
|
const isGlobal = getExtensionType(extensionName) === 'global';
|
||||||
if (isGlobal && !isCurrentUserAdmin) {
|
if (isGlobal && !isCurrentUserAdmin) {
|
||||||
toastr.error(t`You don't have permission to delete global extensions.`);
|
toastr.error(t`You don't have permission to delete global extensions.`);
|
||||||
return;
|
return;
|
||||||
@ -778,6 +823,18 @@ async function onDeleteClick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onMoveClick() {
|
||||||
|
const extensionName = $(this).data('name');
|
||||||
|
const isCurrentUserAdmin = isAdmin();
|
||||||
|
const isGlobal = getExtensionType(extensionName) === 'global';
|
||||||
|
if (isGlobal && !isCurrentUserAdmin) {
|
||||||
|
toastr.error(t`You don't have permission to move extensions.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toastr.info('Not implemented yet');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes an extension via the API.
|
* Deletes an extension via the API.
|
||||||
* @param {string} extensionName Extension name to delete
|
* @param {string} extensionName Extension name to delete
|
||||||
@ -789,7 +846,7 @@ export async function deleteExtension(extensionName) {
|
|||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
extensionName,
|
extensionName,
|
||||||
global: extensionTypes[extensionName] === 'global',
|
global: getExtensionType(extensionName) === 'global',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -806,16 +863,21 @@ export async function deleteExtension(extensionName) {
|
|||||||
* Fetches the version details of a specific extension.
|
* Fetches the version details of a specific extension.
|
||||||
*
|
*
|
||||||
* @param {string} extensionName - The name of the extension.
|
* @param {string} extensionName - The name of the extension.
|
||||||
|
* @param {AbortSignal} [abortSignal] - The signal to abort the operation.
|
||||||
* @return {Promise<object>} - An object containing the extension's version details.
|
* @return {Promise<object>} - An object containing the extension's version details.
|
||||||
* This object includes the currentBranchName, currentCommitHash, isUpToDate, and remoteUrl.
|
* This object includes the currentBranchName, currentCommitHash, isUpToDate, and remoteUrl.
|
||||||
* @throws {error} - If there is an error during the fetch operation, it logs the error to the console.
|
* @throws {error} - If there is an error during the fetch operation, it logs the error to the console.
|
||||||
*/
|
*/
|
||||||
async function getExtensionVersion(extensionName) {
|
async function getExtensionVersion(extensionName, abortSignal) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/extensions/version', {
|
const response = await fetch('/api/extensions/version', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({ extensionName }),
|
body: JSON.stringify({
|
||||||
|
extensionName,
|
||||||
|
global: getExtensionType(extensionName) === 'global',
|
||||||
|
}),
|
||||||
|
signal: abortSignal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@ -900,13 +962,18 @@ export function doDailyExtensionUpdatesCheck() {
|
|||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkForUpdatesManual() {
|
/**
|
||||||
|
* Performs a manual check for updates on all 3rd-party extensions.
|
||||||
|
* @param {AbortSignal} abortSignal Signal to abort the operation
|
||||||
|
* @returns {Promise<any[]>}
|
||||||
|
*/
|
||||||
|
async function checkForUpdatesManual(abortSignal) {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (const id of Object.keys(manifests).filter(x => x.startsWith('third-party'))) {
|
for (const id of Object.keys(manifests).filter(x => x.startsWith('third-party'))) {
|
||||||
const externalId = id.replace('third-party', '');
|
const externalId = id.replace('third-party', '');
|
||||||
const promise = new Promise(async (resolve, reject) => {
|
const promise = new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const data = await getExtensionVersion(externalId);
|
const data = await getExtensionVersion(externalId, abortSignal);
|
||||||
const extensionBlock = document.querySelector(`.extension_block[data-name="${externalId}"]`);
|
const extensionBlock = document.querySelector(`.extension_block[data-name="${externalId}"]`);
|
||||||
if (extensionBlock) {
|
if (extensionBlock) {
|
||||||
if (data.isUpToDate === false) {
|
if (data.isUpToDate === false) {
|
||||||
@ -969,7 +1036,7 @@ async function checkForExtensionUpdates(force) {
|
|||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
for (const [id, manifest] of Object.entries(manifests)) {
|
for (const [id, manifest] of Object.entries(manifests)) {
|
||||||
const isGlobal = extensionTypes[id] === 'global';
|
const isGlobal = getExtensionType(id) === 'global';
|
||||||
if (isGlobal && !isCurrentUserAdmin) {
|
if (isGlobal && !isCurrentUserAdmin) {
|
||||||
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||||
continue;
|
continue;
|
||||||
@ -1012,7 +1079,7 @@ async function autoUpdateExtensions(forceAll) {
|
|||||||
const isCurrentUserAdmin = isAdmin();
|
const isCurrentUserAdmin = isAdmin();
|
||||||
const promises = [];
|
const promises = [];
|
||||||
for (const [id, manifest] of Object.entries(manifests)) {
|
for (const [id, manifest] of Object.entries(manifests)) {
|
||||||
const isGlobal = extensionTypes[id] === 'global';
|
const isGlobal = getExtensionType(id) === 'global';
|
||||||
if (isGlobal && !isCurrentUserAdmin) {
|
if (isGlobal && !isCurrentUserAdmin) {
|
||||||
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
console.debug(`Skipping global extension: ${manifest.display_name} (${id}) for non-admin user`);
|
||||||
continue;
|
continue;
|
||||||
@ -1043,9 +1110,9 @@ async function runGenerationInterceptors(chat, contextSize) {
|
|||||||
|
|
||||||
for (const manifest of Object.values(manifests).sort((a, b) => a.loading_order - b.loading_order)) {
|
for (const manifest of Object.values(manifests).sort((a, b) => a.loading_order - b.loading_order)) {
|
||||||
const interceptorKey = manifest.generate_interceptor;
|
const interceptorKey = manifest.generate_interceptor;
|
||||||
if (typeof window[interceptorKey] === 'function') {
|
if (typeof globalThis[interceptorKey] === 'function') {
|
||||||
try {
|
try {
|
||||||
await window[interceptorKey](chat, contextSize, abort);
|
await globalThis[interceptorKey](chat, contextSize, abort);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed running interceptor for ${manifest.display_name}`, e);
|
console.error(`Failed running interceptor for ${manifest.display_name}`, e);
|
||||||
}
|
}
|
||||||
@ -1124,7 +1191,7 @@ export async function openThirdPartyExtensionMenu(suggestUrl = '') {
|
|||||||
|
|
||||||
let global = false;
|
let global = false;
|
||||||
const installForAllButton = {
|
const installForAllButton = {
|
||||||
text: t`Install for all`,
|
text: t`Install for all users`,
|
||||||
appendAtEnd: false,
|
appendAtEnd: false,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
global = true;
|
global = true;
|
||||||
@ -1153,10 +1220,11 @@ export async function initExtensions() {
|
|||||||
$('#extensions_autoconnect').on('input', autoConnectInputHandler);
|
$('#extensions_autoconnect').on('input', autoConnectInputHandler);
|
||||||
$('#extensions_details').on('click', showExtensionsDetails);
|
$('#extensions_details').on('click', showExtensionsDetails);
|
||||||
$('#extensions_notify_updates').on('input', notifyUpdatesInputHandler);
|
$('#extensions_notify_updates').on('input', notifyUpdatesInputHandler);
|
||||||
$(document).on('click', '.toggle_disable', onDisableExtensionClick);
|
$(document).on('click', '.extensions_info .extension_block .toggle_disable', onDisableExtensionClick);
|
||||||
$(document).on('click', '.toggle_enable', onEnableExtensionClick);
|
$(document).on('click', '.extensions_info .extension_block .toggle_enable', onEnableExtensionClick);
|
||||||
$(document).on('click', '.btn_update', onUpdateClick);
|
$(document).on('click', '.extensions_info .extension_block .btn_update', onUpdateClick);
|
||||||
$(document).on('click', '.btn_delete', onDeleteClick);
|
$(document).on('click', '.extensions_info .extension_block .btn_delete', onDeleteClick);
|
||||||
|
$(document).on('click', '.extensions_info .extension_block .btn_move', onMoveClick);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the click event for the third-party extension import button.
|
* Handles the click event for the third-party extension import button.
|
||||||
|
@ -246,26 +246,28 @@ router.get('/discover', jsonParser, function (request, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get all folders in system extensions folder, excluding third-party
|
// Get all folders in system extensions folder, excluding third-party
|
||||||
const buildInExtensions = fs
|
const builtInExtensions = fs
|
||||||
.readdirSync(PUBLIC_DIRECTORIES.extensions)
|
.readdirSync(PUBLIC_DIRECTORIES.extensions)
|
||||||
.filter(f => fs.statSync(path.join(PUBLIC_DIRECTORIES.extensions, f)).isDirectory())
|
.filter(f => fs.statSync(path.join(PUBLIC_DIRECTORIES.extensions, f)).isDirectory())
|
||||||
.filter(f => f !== 'third-party')
|
.filter(f => f !== 'third-party')
|
||||||
.map(f => ({ type: 'system', name: f }));
|
.map(f => ({ type: 'system', name: f }));
|
||||||
|
|
||||||
// Get all folders in global extensions folder
|
|
||||||
const globalExtensions = fs
|
|
||||||
.readdirSync(PUBLIC_DIRECTORIES.globalExtensions)
|
|
||||||
.filter(f => fs.statSync(path.join(PUBLIC_DIRECTORIES.globalExtensions, f)).isDirectory())
|
|
||||||
.map(f => ({ type: 'global', name: `third-party/${f}` }));
|
|
||||||
|
|
||||||
// Get all folders in local extensions folder
|
// Get all folders in local extensions folder
|
||||||
const userExtensions = fs
|
const userExtensions = fs
|
||||||
.readdirSync(path.join(request.user.directories.extensions))
|
.readdirSync(path.join(request.user.directories.extensions))
|
||||||
.filter(f => fs.statSync(path.join(request.user.directories.extensions, f)).isDirectory())
|
.filter(f => fs.statSync(path.join(request.user.directories.extensions, f)).isDirectory())
|
||||||
.map(f => ({ type: 'local', name: `third-party/${f}` }));
|
.map(f => ({ type: 'local', name: `third-party/${f}` }));
|
||||||
|
|
||||||
|
// Get all folders in global extensions folder
|
||||||
|
// In case of a conflict, the extension will be loaded from the user folder
|
||||||
|
const globalExtensions = fs
|
||||||
|
.readdirSync(PUBLIC_DIRECTORIES.globalExtensions)
|
||||||
|
.filter(f => fs.statSync(path.join(PUBLIC_DIRECTORIES.globalExtensions, f)).isDirectory())
|
||||||
|
.map(f => ({ type: 'global', name: `third-party/${f}` }))
|
||||||
|
.filter(f => !userExtensions.some(e => e.name === f.name));
|
||||||
|
|
||||||
// Combine all extensions
|
// Combine all extensions
|
||||||
const allExtensions = Array.from(new Set([...buildInExtensions, ...globalExtensions, ...userExtensions]));
|
const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];
|
||||||
console.log(allExtensions);
|
console.log(allExtensions);
|
||||||
|
|
||||||
return response.send(allExtensions);
|
return response.send(allExtensions);
|
||||||
|
Reference in New Issue
Block a user