diff --git a/desktop/src/api/extensions/extensions.controller.ts b/desktop/src/api/extensions/extensions.controller.ts index c62a4eb..005e0b9 100644 --- a/desktop/src/api/extensions/extensions.controller.ts +++ b/desktop/src/api/extensions/extensions.controller.ts @@ -1,6 +1,59 @@ import { FastifyReply, FastifyRequest } from 'fastify'; import { getExtension, getExtensionsList, getGalleryExtensionsMap, getBookExtensionsMap, getAnimeExtensionsMap, saveExtensionFile, deleteExtensionFile } from '../../shared/extensions'; import { ExtensionNameRequest } from '../types'; +import path from 'path'; +import fs from 'fs/promises'; + +const TYPE_MAP: Record = { + 'anime-board': 'anime', + 'image-board': 'image', + 'book-board': 'book', +}; + +function extractProp(source: string, prop: string): string | null { + const m = source.match(new RegExp(`this\\.${prop}\\s*=\\s*["']([^"']+)["']`)); + return m ? m[1] : null; +} + +function isNewer(remote: string, local?: string | null) { + if (!local) return true; + return remote !== local; +} + +export async function updateExtensions(req: any, reply: FastifyReply) { + const updated: string[] = []; + + for (const name of getExtensionsList()) { + const ext = getExtension(name); + if (!ext) continue; + + const type = ext.type; + if (!TYPE_MAP[type]) continue; + + const fileName = ext.__fileName; + const remoteUrl = `https://git.waifuboard.app/ItsSkaiya/WaifuBoard-Extensions/raw/branch/main/${TYPE_MAP[type]}/${fileName}`; + + let remoteSrc: string; + try { + const res = await fetch(remoteUrl); + if (!res.ok) continue; + remoteSrc = await res.text(); + } catch { + continue; + } + + const remoteVersion = extractProp(remoteSrc, 'version'); + const localVersion = ext.version ?? null; + if (!remoteVersion) continue; + + if (isNewer(remoteVersion, localVersion)) { + await saveExtensionFile(fileName, remoteUrl); + updated.push(name); + } + } + + return { updated }; +} export async function getExtensions(req: FastifyRequest, reply: FastifyReply) { return { extensions: getExtensionsList() }; diff --git a/desktop/src/api/extensions/extensions.routes.ts b/desktop/src/api/extensions/extensions.routes.ts index fd8e0c2..5720792 100644 --- a/desktop/src/api/extensions/extensions.routes.ts +++ b/desktop/src/api/extensions/extensions.routes.ts @@ -4,6 +4,7 @@ import * as controller from './extensions.controller'; async function extensionsRoutes(fastify: FastifyInstance) { fastify.get('/extensions', controller.getExtensions); fastify.get('/extensions/anime', controller.getAnimeExtensions); + fastify.post('/extensions/update', controller.updateExtensions); fastify.get('/extensions/book', controller.getBookExtensions); fastify.get('/extensions/gallery', controller.getGalleryExtensions); fastify.get('/extensions/:name/settings', controller.getExtensionSettings); diff --git a/desktop/src/scripts/marketplace.js b/desktop/src/scripts/marketplace.js index 2e784fe..abc71bf 100644 --- a/desktop/src/scripts/marketplace.js +++ b/desktop/src/scripts/marketplace.js @@ -1,9 +1,11 @@ const ORIGINAL_MARKETPLACE_URL = 'https://git.waifuboard.app/ItsSkaiya/WaifuBoard-Extensions/raw/branch/main/marketplace.json'; const MARKETPLACE_JSON_URL = `/api/proxy?url=${encodeURIComponent(ORIGINAL_MARKETPLACE_URL)}`; const INSTALLED_EXTENSIONS_API = '/api/extensions'; +const UPDATE_EXTENSIONS_API = '/api/extensions/update'; const marketplaceContent = document.getElementById('marketplace-content'); const filterSelect = document.getElementById('extension-filter'); +const updateAllBtn = document.getElementById('btn-update-all'); const modal = document.getElementById('customModal'); const modalTitle = document.getElementById('modalTitle'); @@ -32,6 +34,10 @@ async function loadMarketplace() { if (filterSelect) { filterSelect.addEventListener('change', () => renderGroupedView()); } + + if (updateAllBtn) { + updateAllBtn.onclick = handleUpdateAll; + } } catch (error) { console.error('Error loading marketplace:', error); marketplaceContent.innerHTML = `
Error al cargar el marketplace.
`; @@ -45,11 +51,49 @@ function initTabs() { tabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); currentTab = tab.dataset.tab; + + if (updateAllBtn) { + if (currentTab === 'installed') { + updateAllBtn.classList.remove('hidden'); + } else { + updateAllBtn.classList.add('hidden'); + } + } + renderGroupedView(); }; }); } +async function handleUpdateAll() { + const originalText = updateAllBtn.innerText; + try { + updateAllBtn.disabled = true; + updateAllBtn.innerText = 'Updating...'; + + const res = await fetch(UPDATE_EXTENSIONS_API, { method: 'POST' }); + if (!res.ok) throw new Error('Update failed'); + + const data = await res.json(); + + if (data.updated && data.updated.length > 0) { + + const list = data.updated.join(', '); + window.NotificationUtils.success(`Updated: ${list}`); + + await loadMarketplace(); + } else { + window.NotificationUtils.info('Everything is up to date.'); + } + } catch (error) { + console.error('Update All Error:', error); + window.NotificationUtils.error('Failed to perform bulk update.'); + } finally { + updateAllBtn.disabled = false; + updateAllBtn.innerText = originalText; + } +} + function renderGroupedView() { marketplaceContent.innerHTML = ''; const activeFilter = filterSelect.value; @@ -58,7 +102,6 @@ function renderGroupedView() { let listToRender = []; if (currentTab === 'marketplace') { - for (const [id, data] of Object.entries(marketplaceMetadata)) { listToRender.push({ id, @@ -67,7 +110,6 @@ function renderGroupedView() { }); } } else { - for (const [id, data] of Object.entries(marketplaceMetadata)) { if (installedExtensions.includes(id.toLowerCase())) { listToRender.push({ id, ...data, isInstalled: true }); @@ -90,9 +132,7 @@ function renderGroupedView() { listToRender.forEach(ext => { const type = ext.type || 'Other'; - if (activeFilter !== 'All' && type !== activeFilter) return; - if (!groups[type]) groups[type] = []; groups[type].push(ext); }); @@ -127,7 +167,7 @@ function createCard(ext) { const card = document.createElement('div'); card.className = `extension-card ${ext.nsfw ? 'nsfw-ext' : ''} ${ext.broken ? 'broken-ext' : ''}`; - const iconUrl = `https://www.google.com/s2/favicons?domain=${ext.domain}&sz=128` + const iconUrl = `https://www.google.com/s2/favicons?domain=${ext.domain}&sz=128`; let buttonHtml = ''; if (ext.isInstalled) { @@ -187,9 +227,9 @@ async function handleInstall(ext) { if (res.ok) { installedExtensions.push(ext.id.toLowerCase()); renderGroupedView(); - showModal('Success', `${ext.name} installed!`); + window.NotificationUtils.success(`${ext.name} installed!`); } - } catch (e) { showModal('Error', 'Install failed.'); } + } catch (e) { window.NotificationUtils.error('Install failed.'); } } function promptUninstall(ext) { @@ -206,8 +246,9 @@ async function handleUninstall(ext) { if (res.ok) { installedExtensions = installedExtensions.filter(id => id !== ext.id.toLowerCase()); renderGroupedView(); + window.NotificationUtils.info(`${ext.name} uninstalled.`); } - } catch (e) { showModal('Error', 'Uninstall failed.'); } + } catch (e) { window.NotificationUtils.error('Uninstall failed.'); } } function showSkeletons() { diff --git a/desktop/src/shared/extensions.js b/desktop/src/shared/extensions.js index d59b4a6..7704896 100644 --- a/desktop/src/shared/extensions.js +++ b/desktop/src/shared/extensions.js @@ -46,7 +46,6 @@ async function loadExtensions() { } } - async function loadExtension(fileName) { const homeDir = os.homedir(); const extensionsDir = path.join(homeDir, 'WaifuBoards', 'extensions'); @@ -77,6 +76,7 @@ async function loadExtension(fileName) { } const name = instance.constructor.name; + instance.__fileName = fileName; instance.scrape = scrape; instance.cheerio = cheerio; extensions.set(name, instance); @@ -114,6 +114,14 @@ async function saveExtensionFile(fileName, downloadUrl) { file.on('finish', async () => { file.close(async () => { try { + const extName = fileName.replace('.js', ''); + + for (const key of extensions.keys()) { + if (key.toLowerCase() === extName.toLowerCase()) { + extensions.delete(key); + break; + } + } await loadExtension(fileName); resolve(); } catch (err) { diff --git a/desktop/views/marketplace.html b/desktop/views/marketplace.html index 3a18cf1..066fb42 100644 --- a/desktop/views/marketplace.html +++ b/desktop/views/marketplace.html @@ -97,6 +97,9 @@
+ @@ -112,9 +115,9 @@
+ - \ No newline at end of file