added updateall btn to marketplace
This commit is contained in:
@@ -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() };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user