mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Still working, added update and version get functions
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced } from "../script.js";
|
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders } from "../script.js";
|
||||||
import { isSubsetOf, debounce } from "./utils.js";
|
import { isSubsetOf, debounce } from "./utils.js";
|
||||||
export {
|
export {
|
||||||
getContext,
|
getContext,
|
||||||
@@ -376,28 +376,41 @@ function addExtensionScript(name, manifest) {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showExtensionsDetails() {
|
|
||||||
let html = '<h3>Modules provided by your Extensions API:</h3>';
|
|
||||||
html += modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">Not connected to the API!</p>';
|
|
||||||
html += '<h3>Available extensions:</h3>';
|
|
||||||
|
|
||||||
Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order).forEach(extension => {
|
|
||||||
|
|
||||||
|
function showExtensionsDetails() {
|
||||||
|
let htmlDefault = '<h3>Default Extensions:</h3>';
|
||||||
|
let htmlExternal = '<h3>External Extensions:</h3>';
|
||||||
|
|
||||||
|
Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order).forEach(extension => async () =>{
|
||||||
const name = extension[0];
|
const name = extension[0];
|
||||||
const manifest = extension[1];
|
const manifest = extension[1];
|
||||||
const isActive = activeExtensions.has(name);
|
const isActive = activeExtensions.has(name);
|
||||||
const isDisabled = extension_settings.disabledExtensions.includes(name);
|
const isDisabled = extension_settings.disabledExtensions.includes(name);
|
||||||
|
const isExternal = name.startsWith('third-party');
|
||||||
|
|
||||||
const titleClass = isActive ? "extension_enabled" : isDisabled ? "extension_disabled" : "extension_missing";
|
const titleClass = isActive ? "extension_enabled" : isDisabled ? "extension_disabled" : "extension_missing";
|
||||||
const iconString = isActive ? "\u2705" : isDisabled ? "\u274C" : "";
|
const checkboxClass = isDisabled ? "checkbox_disabled" : "";
|
||||||
|
|
||||||
let toggleElement = `<span title="Cannot enable extension" data-name="${name}" class="extension_missing">Unavailable</span>`;
|
const displayName = manifest.display_name;
|
||||||
if (isActive) {
|
// if external, get the version from a requst to get_extension_verion
|
||||||
toggleElement = `<a href="javascript:void" title="Click to disable" data-name="${name}" class="toggle_disable">Disable</a>`;
|
let displayVersion = manifest.version ? ` v${manifest.version}` : "";
|
||||||
|
if(isExternal) {
|
||||||
|
let data = await getExtensionVersion(name.replace('third-party', ''));
|
||||||
|
let branch = data.currentBranchName;
|
||||||
|
let commitHash = data.currentCommitHash;
|
||||||
|
displayVersion = ` v${branch}-${commitHash}`;
|
||||||
}
|
}
|
||||||
else if (isDisabled) {
|
|
||||||
toggleElement = `<a href="javascript:void" title="Click to enable" data-name="${name}" class="toggle_enable">Enable</a>`;
|
|
||||||
|
let toggleElement = `<input type="checkbox" title="Cannot enable extension" data-name="${name}" class="extension_missing ${checkboxClass}" disabled>`;
|
||||||
|
if (isActive || isDisabled) {
|
||||||
|
toggleElement = `<input type="checkbox" title="Click to toggle" data-name="${name}" class="${isActive ? 'toggle_disable' : 'toggle_enable'} ${checkboxClass}" ${isActive ? 'checked' : ''}>`;
|
||||||
}
|
}
|
||||||
html += `<hr><h4>${iconString} <span class="${titleClass}">${DOMPurify.sanitize(manifest.display_name)}</span> <span style="float:right;">${toggleElement}<span></h4>`;
|
|
||||||
|
let updateButton = isExternal ? `<button class="btn_update" data-name= ${name.replace('third-party', '')} + ">Update</button>` : '';
|
||||||
|
let extensionHtml = `<hr><h4><span class="${titleClass}">${DOMPurify.sanitize(displayName)}${displayVersion}</span> <span style="float:right;">${toggleElement}<span>${updateButton}</h4>`;
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
if (Array.isArray(manifest.optional)) {
|
if (Array.isArray(manifest.optional)) {
|
||||||
@@ -405,7 +418,7 @@ function showExtensionsDetails() {
|
|||||||
modules.forEach(x => optional.delete(x));
|
modules.forEach(x => optional.delete(x));
|
||||||
if (optional.size > 0) {
|
if (optional.size > 0) {
|
||||||
const optionalString = DOMPurify.sanitize([...optional].join(', '));
|
const optionalString = DOMPurify.sanitize([...optional].join(', '));
|
||||||
html += `<p>Optional modules: <span class="optional">${optionalString}</span></p>`;
|
extensionHtml += `<p>Optional modules: <span class="optional">${optionalString}</span></p>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,13 +426,69 @@ function showExtensionsDetails() {
|
|||||||
const requirements = new Set(manifest.requires);
|
const requirements = new Set(manifest.requires);
|
||||||
modules.forEach(x => requirements.delete(x));
|
modules.forEach(x => requirements.delete(x));
|
||||||
const requirementsString = DOMPurify.sanitize([...requirements].join(', '));
|
const requirementsString = DOMPurify.sanitize([...requirements].join(', '));
|
||||||
html += `<p>Missing modules: <span class="failure">${requirementsString}</span></p>`
|
extensionHtml += `<p>Missing modules: <span class="failure">${requirementsString}</span></p>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the HTML to the correct section
|
||||||
|
if (isExternal) {
|
||||||
|
htmlExternal += extensionHtml;
|
||||||
|
} else {
|
||||||
|
htmlDefault += extensionHtml;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let html = '<h3>Modules provided by your Extensions API:</h3>';
|
||||||
|
html += modules.length ? `<p>${DOMPurify.sanitize(modules.join(', '))}</p>` : '<p class="failure">Not connected to the API!</p>';
|
||||||
|
html += htmlDefault + htmlExternal;
|
||||||
|
|
||||||
callPopup(`<div class="extensions_info">${html}</div>`, 'text');
|
callPopup(`<div class="extensions_info">${html}</div>`, 'text');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function onUpdateClick() {
|
||||||
|
const extensionName = $(this).data('name');
|
||||||
|
try {
|
||||||
|
const response = await fetch('/update_extension', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({ extensionName })
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Response', response);
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('Data', data);
|
||||||
|
if (data.isUpToDate) {
|
||||||
|
console.log('Extension is up to date');
|
||||||
|
toastr.success('Extension is already up to date');
|
||||||
|
} else {
|
||||||
|
console.log('Extension updated');
|
||||||
|
toastr.success(`Extension updated to ${data.shortCommitHash}`);
|
||||||
|
}
|
||||||
|
$(this).text(data.shortCommitHash);
|
||||||
|
console.log(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getExtensionVersion(extensionName) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/get_extension_version', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({ extensionName })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function loadExtensionSettings(settings) {
|
async function loadExtensionSettings(settings) {
|
||||||
if (settings.extension_settings) {
|
if (settings.extension_settings) {
|
||||||
Object.assign(extension_settings, settings.extension_settings);
|
Object.assign(extension_settings, settings.extension_settings);
|
||||||
@@ -463,4 +532,5 @@ $(document).ready(async function () {
|
|||||||
$("#extensions_details").on('click', showExtensionsDetails);
|
$("#extensions_details").on('click', showExtensionsDetails);
|
||||||
$(document).on('click', '.toggle_disable', onDisableExtensionClick);
|
$(document).on('click', '.toggle_disable', onDisableExtensionClick);
|
||||||
$(document).on('click', '.toggle_enable', onEnableExtensionClick);
|
$(document).on('click', '.toggle_enable', onEnableExtensionClick);
|
||||||
|
$(document).on('click', '.btn_update', onUpdateClick);
|
||||||
});
|
});
|
||||||
|
82
server.js
82
server.js
@@ -2731,7 +2731,7 @@ app.get('/discover_extensions', jsonParser, function (_, response) {
|
|||||||
if (!fs.existsSync(path.join(directories.extensions, 'third-party'))) {
|
if (!fs.existsSync(path.join(directories.extensions, 'third-party'))) {
|
||||||
return response.send(extensions);
|
return response.send(extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const thirdPartyExtensions = fs
|
const thirdPartyExtensions = fs
|
||||||
.readdirSync(path.join(directories.extensions, 'third-party'))
|
.readdirSync(path.join(directories.extensions, 'third-party'))
|
||||||
.filter(f => fs.statSync(path.join(directories.extensions, 'third-party', f)).isDirectory());
|
.filter(f => fs.statSync(path.join(directories.extensions, 'third-party', f)).isDirectory());
|
||||||
@@ -4426,3 +4426,83 @@ app.post('/get_extension', jsonParser, async (request, response) => {
|
|||||||
return response.status(500).send(`Server Error: ${error.message}`);
|
return response.status(500).send(`Server Error: ${error.message}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP POST handler function to pull the latest updates from a given git repository
|
||||||
|
* based on the extension name and return the latest commit hash.
|
||||||
|
*
|
||||||
|
* @param {Object} request - HTTP Request object, expects a JSON body with an 'extensionName' property.
|
||||||
|
* @param {Object} response - HTTP Response object used to respond to the HTTP request.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
app.post('/update_extension', jsonParser, async (request, response) => {
|
||||||
|
if (!request.body.extensionName) {
|
||||||
|
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const extensionName = request.body.extensionName;
|
||||||
|
const extensionPath = path.join(directories.extensions, 'third-party', extensionName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(extensionPath)) {
|
||||||
|
return response.status(404).send(`Directory does not exist at ${extensionPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentBranch = await git.cwd(extensionPath).branch();
|
||||||
|
const currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
|
||||||
|
const isUpToDate = await git.cwd(extensionPath).log({
|
||||||
|
from: currentCommitHash,
|
||||||
|
to: `origin/${currentBranch.current}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isUpToDate.total === 0) {
|
||||||
|
await git.cwd(extensionPath).pull('origin', currentBranch.current);
|
||||||
|
console.log(`Extension has been updated at ${extensionPath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Extension is up to date at ${extensionPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
|
||||||
|
const shortCommitHash = fullCommitHash.slice(0, 7);
|
||||||
|
|
||||||
|
return response.send({ shortCommitHash, extensionPath, isUpToDate: isUpToDate.total === 0 });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Updating custom content failed', error);
|
||||||
|
return response.status(500).send(`Server Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to get current git commit hash and branch name for a given extension.
|
||||||
|
*/
|
||||||
|
app.post('/get_extension_version', jsonParser, async (request, response) => {
|
||||||
|
if (!request.body.extensionName) {
|
||||||
|
return response.status(400).send('Bad Request: extensionName is required in the request body.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const extensionName = request.body.extensionName;
|
||||||
|
const extensionPath = path.join(directories.extensions, 'third-party', extensionName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(extensionPath)) {
|
||||||
|
return response.status(404).send(`Directory does not exist at ${extensionPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentBranch = await git.cwd(extensionPath).branch();
|
||||||
|
// get only the working branch
|
||||||
|
const currentBranchName = currentBranch.current;
|
||||||
|
const currentCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
|
||||||
|
console.log(currentBranch, currentCommitHash);
|
||||||
|
return response.send({ currentBranchName, currentCommitHash });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Getting extension version failed', error);
|
||||||
|
return response.status(500).send(`Server Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user