Npplication Store 实现
商店系统概述
Npplication Store 是一个插件发现和管理系统,允许浏览、安装和更新插件。它具有以下特性:
- 支持多个商店源
- 按分类浏览插件 (自动合并相同分类)
- 插件详情展示 (插件细节页: 包括截图、描述、依赖等)
商店源管理
nitaiPage 支持用户在商店管理页添加多个商店源。默认商店源配置如下:
javascript
// 商店源
const storeSourcesDefault = [
'https://nfdb.nitai.us.kg/nitaiPage/store'
];
插件数据获取与处理
核心功能:从商店源获取插件数据并合并相同分类。
javascript
// 加载商店数据
async function loadStoreData() {
// 从所有商店源获取数据
const storeDataArray = await Promise.all(storeSources.map(source => fetchStoreData(source.url)));
// 合并所有商店数据
const mergedData = mergeStoreData(storeDataArray);
// 渲染商店标签
renderStoreTabs(mergedData);
// 渲染插件列表
renderPlugins(mergedData.all);
}
使用到的关键函数
fetchStoreData(url)
: 从商店源获取商店数据mergeStoreData(dataArray)
: 合并多个商店源的分类renderStoreTabs(data)
: 渲染商店分类标签renderPlugins(pluginsArray)
: 渲染插件列表
插件展示
插件展示包括列表视图和详情视图,支持用户浏览和安装插件。
javascript
// 渲染插件列表
async function renderPlugins(pluginsArray) {
const contentContainer = document.getElementById('storeContent');
contentContainer.innerHTML = '';
// 创建中止控制器,用于取消过时的渲染任务
const controller = new AbortController();
window._lastStoreController?.abort();
window._lastStoreController = controller;
pluginsArray.forEach(async (plugin) => {
if (controller.signal.aborted) return;
try {
// 提取插件元数据
const metadata = await extractMetadata(plugin.url);
const pluginWithMetadata = { ...plugin, ...metadata };
// 创建插件项 DOM 元素
const pluginItem = document.createElement('div');
pluginItem.className = 'plugin-item';
pluginItem.innerHTML = `
<img src="default-icon.png" class="plugin-icon">
<div class="plugin-info">
<strong translate="none">${pluginWithMetadata.name || '未命名插件'}</strong>
<div class="plugin-description">${pluginWithMetadata.description || '无描述'}</div>
<div class="plugin-meta">
<span class="plugin-version">v${pluginWithMetadata.version || '0.0.1'}</span>
<span class="plugin-author">${pluginWithMetadata.author || '未知作者'}</span>
</div>
</div>
`;
// 添加点击事件
pluginItem.addEventListener('click', () => {
showPluginDetails(pluginWithMetadata);
});
contentContainer.appendChild(pluginItem);
} catch (error) {
console.error(`加载插件失败: ${plugin.url}`, error);
}
});
}
插件安装
javascript
// 安装 Npplication 插件
async function installNpplication(pluginUrl) {
try {
// 1. 检查依赖
const dependencyCheck = await checkDependencies(pluginUrl);
if (!dependencyCheck.status) {
return;
}
// 2. 验证是否为 JS 文件
if (!await verifyJSUrl(pluginUrl)) {
return;
}
// 3. 提取元数据
const metadata = await extractMetadata(pluginUrl);
const pluginId = metadata.id || generateId(pluginUrl);
// 4. 检查现有版本
const existingPlugin = await getPluginById(pluginId);
if (existingPlugin) {
// 比较版本
const shouldUpdate = compareVersions(metadata.version, existingPlugin.version) > 0;
if (!shouldUpdate) {
showUpdateDialog(pluginUrl, existingPlugin, metadata, true);
return;
}
}
// 5. 保存 JS 文件内容
const jsContent = await saveJSFile(pluginUrl);
// 6. 保存元数据
await savePluginMetadata(pluginId, { ...metadata, url: pluginUrl });
// 7. 刷新提示
showRefreshDialog();
} catch (error) {
console.error('安装插件失败:', error);
}
}
插件存储
插件数据储存在 IndexedDB:
javascript
// 初始化数据库
function initializaNppDB() {
return new Promise((resolve, reject) => {
// 检查数据库状态
const request = indexedDB.open(DB_NAME, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore(NPP_STORE, { keyPath: 'id' });
};
request.onsuccess = (event) => {
const db = event.target.result;
// 验证数据库状态
if (!db.objectStoreNames.contains(NPP_STORE)) {
console.error('缺少必要的对象存储: ' + NPP_STORE);
reject();
return;
}
db.close();
resolve();
};
request.onerror = (event) => {
console.error('数据库初始化失败: ' + event.target.error.message);
reject();
};
});
}