const fs = require('fs').promises; const fsSync = require('fs'); const path = require('path'); function defaultLogger(level, message) { const stamp = new Date().toISOString(); console.log(`${stamp} [PluginReset] [${level}] ${message}`); } async function pathExists(targetPath) { try { await fs.access(targetPath); return true; } catch { return false; } } async function removeIfExists(targetPath, logger) { const exists = await pathExists(targetPath); if (!exists) return false; try { await fs.rm(targetPath, { recursive: true, force: true }); logger('INFO', `Removed: ${targetPath}`); return true; } catch (err) { if (err && (err.code === 'EACCES' || err.code === 'EPERM')) { logger('WARN', `Permission denied removing ${targetPath} (${err.code})`); return false; } throw err; } } function listPluginDirs(pluginSitesDir) { if (!fsSync.existsSync(pluginSitesDir)) return []; return fsSync .readdirSync(pluginSitesDir, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) .map((entry) => path.join(pluginSitesDir, entry.name)); } function readPluginConfig(pluginDir) { const configPath = path.join(pluginDir, 'config.json'); if (!fsSync.existsSync(configPath)) return null; try { return JSON.parse(fsSync.readFileSync(configPath, 'utf8')); } catch { return null; } } async function resetPluginStorage(options = {}) { const projectRoot = options.projectRoot || path.resolve(__dirname, '..'); const pluginSitesDir = path.join(projectRoot, 'plugin-sites'); const cachePluginSpecDir = path.join(projectRoot, 'cache', 'plugin-spec'); const logger = options.logger || defaultLogger; const pluginDirs = listPluginDirs(pluginSitesDir); const touched = []; const warnings = []; for (const pluginDir of pluginDirs) { const config = readPluginConfig(pluginDir); if (!config || !config.hyperdb) continue; const pluginName = config.domain || path.basename(pluginDir); const removed = { plugin: pluginName, specRemoved: false, dbRemoved: false, cacheSpecRemoved: false }; try { removed.specRemoved = await removeIfExists(path.join(pluginDir, 'spec'), logger); removed.dbRemoved = await removeIfExists(path.join(pluginDir, 'db'), logger); removed.cacheSpecRemoved = await removeIfExists(path.join(cachePluginSpecDir, pluginName), logger); } catch (err) { warnings.push(`${pluginName}: ${err.message}`); logger('WARN', `Failed to fully reset ${pluginName}: ${err.message}`); } touched.push(removed); } const summary = { inspectedPlugins: pluginDirs.length, resetCandidates: touched.length, warnings, touched }; logger( 'INFO', `Plugin storage reset complete (inspected=${summary.inspectedPlugins}, candidates=${summary.resetCandidates})` ); return summary; } module.exports = { resetPluginStorage }; if (require.main === module) { resetPluginStorage().catch((err) => { console.error(`${new Date().toISOString()} [PluginReset] [ERROR] ${err.message}`); process.exit(1); }); }