Files
NapCatQQ/packages/napcat-webui-backend/src/router/Plugin.ts
手瓜一十雪 77bdcfd249 Disable local plugin import and upload handling
Commented out ImportLocalPluginHandler import and the multer upload configuration (50MB limit and .zip file filter). Temporarily disables local plugin .zip uploads, likely for debugging or while refactoring the upload/import flow; can be re-enabled when upload handling is restored.
2026-02-22 13:33:06 +08:00

169 lines
5.5 KiB
TypeScript

import { Router } from 'express';
// import multer from 'multer';
import path from 'path';
import fs from 'fs';
import os from 'os';
import {
GetPluginListHandler,
SetPluginStatusHandler,
UninstallPluginHandler,
GetPluginConfigHandler,
SetPluginConfigHandler,
RegisterPluginManagerHandler,
PluginConfigSSEHandler,
PluginConfigChangeHandler,
// ImportLocalPluginHandler,
GetPluginIconHandler
} from '@/napcat-webui-backend/src/api/Plugin';
import {
GetPluginStoreListHandler,
GetPluginStoreDetailHandler,
InstallPluginFromStoreHandler,
InstallPluginFromStoreSSEHandler
} from '@/napcat-webui-backend/src/api/PluginStore';
import { WebUiDataRuntime } from '@/napcat-webui-backend/src/helper/Data';
import { NapCatOneBot11Adapter } from '@/napcat-onebot/index';
import { OB11PluginMangerAdapter } from '@/napcat-onebot/network/plugin-manger';
// 配置 multer 用于文件上传
const uploadDir = path.join(os.tmpdir(), 'napcat-plugin-uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// const storage = multer.diskStorage({
// destination: (_req, _file, cb) => {
// cb(null, uploadDir);
// },
// filename: (_req, file, cb) => {
// const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
// cb(null, uniqueSuffix + '-' + file.originalname);
// }
// });
// const upload = multer({
// storage,
// limits: {
// fileSize: 50 * 1024 * 1024, // 50MB 限制
// },
// fileFilter: (_req, file, cb) => {
// // 只允许 .zip 文件
// if (file.mimetype === 'application/zip' ||
// file.mimetype === 'application/x-zip-compressed' ||
// file.originalname.endsWith('.zip')) {
// cb(null, true);
// } else {
// cb(new Error('Only .zip files are allowed'));
// }
// }
// });
const router: Router = Router();
router.get('/List', GetPluginListHandler);
router.post('/SetStatus', SetPluginStatusHandler);
router.post('/Uninstall', UninstallPluginHandler);
router.get('/Config', GetPluginConfigHandler);
router.post('/Config', SetPluginConfigHandler);
router.get('/Config/SSE', PluginConfigSSEHandler);
router.post('/Config/Change', PluginConfigChangeHandler);
router.post('/RegisterManager', RegisterPluginManagerHandler);
// router.post('/Import', upload.single('plugin'), ImportLocalPluginHandler); // 禁用插件上传
router.get('/Icon/:pluginId', GetPluginIconHandler);
// 插件商店相关路由
router.get('/Store/List', GetPluginStoreListHandler);
router.get('/Store/Detail/:id', GetPluginStoreDetailHandler);
router.post('/Store/Install', InstallPluginFromStoreHandler);
router.get('/Store/Install/SSE', InstallPluginFromStoreSSEHandler);
// 插件扩展路由 - 动态挂载插件注册的 API 路由
router.use('/ext/:pluginId', (req, res, next): void => {
const { pluginId } = req.params;
if (!pluginId) {
res.status(400).json({ code: -1, message: 'Plugin ID is required' });
return;
}
// 获取插件管理器
const ob11 = WebUiDataRuntime.getOneBotContext() as NapCatOneBot11Adapter;
if (!ob11) {
res.status(503).json({ code: -1, message: 'OneBot context not available' });
return;
}
const pluginManager = ob11.networkManager.findSomeAdapter('plugin_manager') as OB11PluginMangerAdapter;
if (!pluginManager) {
res.status(503).json({ code: -1, message: 'Plugin manager not available' });
return;
}
// 获取插件路由
const routerRegistry = pluginManager.getPluginRouter(pluginId);
if (!routerRegistry || !routerRegistry.hasApiRoutes()) {
res.status(404).json({ code: -1, message: `Plugin '${pluginId}' has no registered API routes` });
return;
}
// 构建并执行插件路由
const pluginRouter = routerRegistry.buildApiRouter();
pluginRouter(req, res, next);
});
// 插件页面路由 - 服务插件注册的 HTML 页面
router.get('/page/:pluginId/:pagePath', (req, res): void => {
const { pluginId, pagePath } = req.params;
if (!pluginId) {
res.status(400).json({ code: -1, message: 'Plugin ID is required' });
return;
}
// 获取插件管理器
const ob11 = WebUiDataRuntime.getOneBotContext() as NapCatOneBot11Adapter;
if (!ob11) {
res.status(503).json({ code: -1, message: 'OneBot context not available' });
return;
}
const pluginManager = ob11.networkManager.findSomeAdapter('plugin_manager') as OB11PluginMangerAdapter;
if (!pluginManager) {
res.status(503).json({ code: -1, message: 'Plugin manager not available' });
return;
}
// 获取插件路由
const routerRegistry = pluginManager.getPluginRouter(pluginId);
if (!routerRegistry || !routerRegistry.hasPages()) {
res.status(404).json({ code: -1, message: `Plugin '${pluginId}' has no registered pages` });
return;
}
// 查找匹配的页面
const pages = routerRegistry.getPages();
const page = pages.find(p => p.path === '/' + pagePath || p.path === pagePath);
if (!page) {
res.status(404).json({ code: -1, message: `Page '${pagePath}' not found in plugin '${pluginId}'` });
return;
}
// 获取插件路径
const pluginPath = routerRegistry.getPluginPath();
if (!pluginPath) {
res.status(500).json({ code: -1, message: 'Plugin path not available' });
return;
}
// 构建 HTML 文件路径并发送
const htmlFilePath = path.join(pluginPath, page.htmlFile);
if (!fs.existsSync(htmlFilePath)) {
res.status(404).json({ code: -1, message: `HTML file not found: ${page.htmlFile}` });
return;
}
res.sendFile(htmlFilePath);
});
export { router as PluginRouter };