import { FastifyReply, FastifyRequest } from 'fastify'; import { getExtension, getExtensionsList, getGalleryExtensionsMap, getBookExtensionsMap, getMangaExtensionsMap, getNovelExtensionsMap, getAnimeExtensionsMap, saveExtensionFile, deleteExtensionFile } from '../../shared/extensions'; import { ExtensionNameRequest } from '../types'; 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() }; } export async function getAnimeExtensions(req: FastifyRequest, reply: FastifyReply) { const animeExtensions = getAnimeExtensionsMap(); return { extensions: Array.from(animeExtensions.keys()) }; } export async function getBookExtensions(req: FastifyRequest, reply: FastifyReply) { const bookExtensions = getBookExtensionsMap(); return { extensions: Array.from(bookExtensions.keys()) }; } export async function getMangaExtensions(req: FastifyRequest, reply: FastifyReply) { const bookExtensions = getMangaExtensionsMap(); return { extensions: Array.from(bookExtensions.keys()) }; } export async function getNovelExtensions(req: FastifyRequest, reply: FastifyReply) { const bookExtensions = getNovelExtensionsMap(); return { extensions: Array.from(bookExtensions.keys()) }; } export async function getGalleryExtensions(req: FastifyRequest, reply: FastifyReply) { const galleryExtensions = getGalleryExtensionsMap(); return { extensions: Array.from(galleryExtensions.keys()) }; } export async function getExtensionSettings(req: ExtensionNameRequest, reply: FastifyReply) { const { name } = req.params; const ext = getExtension(name); if (!ext) { return { error: "Extension not found" }; } if (!ext.getSettings) { return { episodeServers: ["default"], supportsDub: false }; } return ext.getSettings(); } export async function installExtension(req: any, reply: FastifyReply) { const { url } = req.body; if (!url || typeof url !== 'string' || !url.endsWith('.js')) { return reply.code(400).send({ error: "Invalid extension URL provided" }); } try { const fileName = url.split('/').pop(); if (!fileName) { return reply.code(400).send({ error: "Could not determine file name from URL" }); } await saveExtensionFile(fileName, url); req.server.log.info(`Extension installed: ${fileName}`); return reply.code(200).send({ success: true, message: `Extension ${fileName} installed successfully.`, }); } catch (error) { req.server.log.error(`Failed to install extension from ${url}:`, error); return reply.code(500).send({ success: false, error: "Failed to install extension.", }); } } export async function uninstallExtension(req: any, reply: FastifyReply) { const { fileName } = req.body; if (!fileName || !fileName.endsWith('.js')) { return reply.code(400).send({ error: "Invalid extension fileName provided" }); } try { await deleteExtensionFile(fileName); req.server.log.info(`Extension uninstalled: ${fileName}`); return reply.code(200).send({ success: true, message: `Extension ${fileName} uninstalled successfully.` }); } catch (error) { // @ts-ignore if (error.code === 'ENOENT') { return reply.code(200).send({ success: true, message: `Extension ${fileName} already uninstalled (file not found).` }); } req.server.log.error(`Failed to uninstall extension ${fileName}:`, error); return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` }); } }