From 27e3485127f14579c5bb5c250eeed6d5509e7ca1 Mon Sep 17 00:00:00 2001 From: BlipRanger <1860540+BlipRanger@users.noreply.github.com> Date: Wed, 12 Jul 2023 01:13:36 -0400 Subject: [PATCH] Still working, added update and version get functions --- public/scripts/extensions.js | 100 +++++++++++++++++++++++++++++------ server.js | 82 +++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 16 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 3e15c4540..dc8dfbe89 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -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"; export { getContext, @@ -376,28 +376,41 @@ function addExtensionScript(name, manifest) { return Promise.resolve(); } -function showExtensionsDetails() { - let html = '

Modules provided by your Extensions API:

'; - html += modules.length ? `

${DOMPurify.sanitize(modules.join(', '))}

` : '

Not connected to the API!

'; - html += '

Available extensions:

'; - Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order).forEach(extension => { + + +function showExtensionsDetails() { + let htmlDefault = '

Default Extensions:

'; + let htmlExternal = '

External Extensions:

'; + + Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order).forEach(extension => async () =>{ const name = extension[0]; const manifest = extension[1]; const isActive = activeExtensions.has(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 iconString = isActive ? "\u2705" : isDisabled ? "\u274C" : ""; + const checkboxClass = isDisabled ? "checkbox_disabled" : ""; - let toggleElement = `Unavailable`; - if (isActive) { - toggleElement = `Disable`; + const displayName = manifest.display_name; + // if external, get the version from a requst to get_extension_verion + 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 = `Enable`; + + + let toggleElement = ``; + if (isActive || isDisabled) { + toggleElement = ``; } - html += `

${iconString} ${DOMPurify.sanitize(manifest.display_name)} ${toggleElement}

`; + + let updateButton = isExternal ? `` : ''; + let extensionHtml = `

${DOMPurify.sanitize(displayName)}${displayVersion} ${toggleElement}${updateButton}

`; if (isActive) { if (Array.isArray(manifest.optional)) { @@ -405,7 +418,7 @@ function showExtensionsDetails() { modules.forEach(x => optional.delete(x)); if (optional.size > 0) { const optionalString = DOMPurify.sanitize([...optional].join(', ')); - html += `

Optional modules: ${optionalString}

`; + extensionHtml += `

Optional modules: ${optionalString}

`; } } } @@ -413,13 +426,69 @@ function showExtensionsDetails() { const requirements = new Set(manifest.requires); modules.forEach(x => requirements.delete(x)); const requirementsString = DOMPurify.sanitize([...requirements].join(', ')); - html += `

Missing modules: ${requirementsString}

` + extensionHtml += `

Missing modules: ${requirementsString}

` + } + + // Append the HTML to the correct section + if (isExternal) { + htmlExternal += extensionHtml; + } else { + htmlDefault += extensionHtml; } }); + let html = '

Modules provided by your Extensions API:

'; + html += modules.length ? `

${DOMPurify.sanitize(modules.join(', '))}

` : '

Not connected to the API!

'; + html += htmlDefault + htmlExternal; + callPopup(`
${html}
`, '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) { if (settings.extension_settings) { Object.assign(extension_settings, settings.extension_settings); @@ -463,4 +532,5 @@ $(document).ready(async function () { $("#extensions_details").on('click', showExtensionsDetails); $(document).on('click', '.toggle_disable', onDisableExtensionClick); $(document).on('click', '.toggle_enable', onEnableExtensionClick); + $(document).on('click', '.btn_update', onUpdateClick); }); diff --git a/server.js b/server.js index 6f7f3a9e8..f857bf9d1 100644 --- a/server.js +++ b/server.js @@ -2731,7 +2731,7 @@ app.get('/discover_extensions', jsonParser, function (_, response) { if (!fs.existsSync(path.join(directories.extensions, 'third-party'))) { return response.send(extensions); } - + const thirdPartyExtensions = fs .readdirSync(path.join(directories.extensions, 'third-party')) .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}`); } }); + +/** + * 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}`); + } + } +); + + +