added updateall btn to marketplace

This commit is contained in:
2025-12-20 00:13:10 +01:00
parent a26f03f024
commit 90231f6608
12 changed files with 340 additions and 152 deletions

View File

@@ -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<string, string> = {
'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() };

View File

@@ -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);

View File

@@ -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 = `<div class="error-msg">Error al cargar el marketplace.</div>`;
@@ -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() {

View File

@@ -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) {

View File

@@ -97,6 +97,9 @@
</div>
<div class="filter-controls">
<button id="btn-update-all" class="btn-blur hidden" style="margin-right: 10px; width: auto; padding: 0 15px;">
Update All
</button>
<label for="extension-filter" class="filter-label">Filter by Type:</label>
<select id="extension-filter" class="filter-select">
<option value="All">All Categories</option>
@@ -124,6 +127,7 @@
</div>
</main>
<script src="/src/scripts/utils/notification-utils.js"></script>
<script src="/src/scripts/updateNotifier.js"></script>
<script src="/src/scripts/marketplace.js"></script>
<script src="/src/scripts/titlebar.js"></script>