Add plugin no-auth API routes and WebUI handling

Introduce support for plugin API routes that do not require WebUI authentication. Updates include:

- napcat-onebot: Add apiNoAuth route storage and helpers (apiNoAuth, getNoAuth, postNoAuth, putNoAuth, deleteNoAuth), hasApiNoAuthRoutes, buildApiNoAuthRouter, and clear handling in PluginRouterRegistryImpl.
- napcat-onebot types: Extend PluginRouterRegistry interface with no-auth API methods and document that authenticated APIs remain separate.
- napcat-webui-backend: Mount a new unauthenticated plugin route handler at /plugin/:pluginId/api that looks up the plugin router and dispatches requests to the plugin's no-auth router, returning appropriate errors when context or routes are missing.
- napcat-plugin-builtin: Add example no-auth endpoints (public/info and health) and update logger messages to reflect both auth and no-auth API paths.
- Bump napcat-types version to 0.0.16 and update napcat-plugin-builtin dependency accordingly.

These changes enable plugins to expose public endpoints (e.g. health checks or public metadata) under /plugin/{pluginId}/api/ while keeping existing authenticated APIs under /api/Plugin/ext/{pluginId}/.
This commit is contained in:
手瓜一十雪
2026-02-02 19:13:01 +08:00
parent ba473d57f0
commit 0fd1a32293
7 changed files with 148 additions and 15 deletions

View File

@@ -68,6 +68,7 @@ interface MemoryStaticRoute {
export class PluginRouterRegistryImpl implements PluginRouterRegistry {
private apiRoutes: PluginApiRouteDefinition[] = [];
private apiNoAuthRoutes: PluginApiRouteDefinition[] = [];
private pageDefinitions: PluginPageDefinition[] = [];
private staticRoutes: Array<{ urlPath: string; localPath: string; }> = [];
private memoryStaticRoutes: MemoryStaticRoute[] = [];
@@ -99,6 +100,28 @@ export class PluginRouterRegistryImpl implements PluginRouterRegistry {
this.api('delete', routePath, handler);
}
// ==================== 无认证 API 路由注册 ====================
apiNoAuth (method: HttpMethod, routePath: string, handler: PluginRequestHandler): void {
this.apiNoAuthRoutes.push({ method, path: routePath, handler });
}
getNoAuth (routePath: string, handler: PluginRequestHandler): void {
this.apiNoAuth('get', routePath, handler);
}
postNoAuth (routePath: string, handler: PluginRequestHandler): void {
this.apiNoAuth('post', routePath, handler);
}
putNoAuth (routePath: string, handler: PluginRequestHandler): void {
this.apiNoAuth('put', routePath, handler);
}
deleteNoAuth (routePath: string, handler: PluginRequestHandler): void {
this.apiNoAuth('delete', routePath, handler);
}
// ==================== 页面注册 ====================
page (pageDef: PluginPageDefinition): void {
@@ -184,12 +207,52 @@ export class PluginRouterRegistryImpl implements PluginRouterRegistry {
// ==================== 查询方法 ====================
/**
* 检查是否有注册的 API 路由
* 检查是否有注册的 API 路由(需要认证)
*/
hasApiRoutes (): boolean {
return this.apiRoutes.length > 0;
}
/**
* 检查是否有注册的无认证 API 路由
*/
hasApiNoAuthRoutes (): boolean {
return this.apiNoAuthRoutes.length > 0;
}
/**
* 构建无认证 Express Router用于 /plugin/{pluginId}/api/ 路径)
*/
buildApiNoAuthRouter (): Router {
const router = Router();
for (const route of this.apiNoAuthRoutes) {
const handler = this.wrapHandler(route.handler);
switch (route.method) {
case 'get':
router.get(route.path, handler);
break;
case 'post':
router.post(route.path, handler);
break;
case 'put':
router.put(route.path, handler);
break;
case 'delete':
router.delete(route.path, handler);
break;
case 'patch':
router.patch(route.path, handler);
break;
case 'all':
router.all(route.path, handler);
break;
}
}
return router;
}
/**
* 检查是否有注册的静态资源路由
*/
@@ -244,6 +307,7 @@ export class PluginRouterRegistryImpl implements PluginRouterRegistry {
*/
clear (): void {
this.apiRoutes = [];
this.apiNoAuthRoutes = [];
this.pageDefinitions = [];
this.staticRoutes = [];
this.memoryStaticRoutes = [];

View File

@@ -140,24 +140,42 @@ export interface MemoryStaticFile {
/** 插件路由注册器 */
export interface PluginRouterRegistry {
// ==================== API 路由注册 ====================
// ==================== API 路由注册(需要认证) ====================
/**
* 注册单个 API 路由
* 注册单个 API 路由(需要认证,挂载到 /api/Plugin/ext/{pluginId}/
* @param method HTTP 方法
* @param path 路由路径
* @param handler 请求处理器
*/
api (method: HttpMethod, path: string, handler: PluginRequestHandler): void;
/** 注册 GET API */
/** 注册 GET API(需要认证) */
get (path: string, handler: PluginRequestHandler): void;
/** 注册 POST API */
/** 注册 POST API(需要认证) */
post (path: string, handler: PluginRequestHandler): void;
/** 注册 PUT API */
/** 注册 PUT API(需要认证) */
put (path: string, handler: PluginRequestHandler): void;
/** 注册 DELETE API */
/** 注册 DELETE API(需要认证) */
delete (path: string, handler: PluginRequestHandler): void;
// ==================== 无认证 API 路由注册 ====================
/**
* 注册单个无认证 API 路由(挂载到 /plugin/{pluginId}/api/
* @param method HTTP 方法
* @param path 路由路径
* @param handler 请求处理器
*/
apiNoAuth (method: HttpMethod, path: string, handler: PluginRequestHandler): void;
/** 注册 GET API无认证 */
getNoAuth (path: string, handler: PluginRequestHandler): void;
/** 注册 POST API无认证 */
postNoAuth (path: string, handler: PluginRequestHandler): void;
/** 注册 PUT API无认证 */
putNoAuth (path: string, handler: PluginRequestHandler): void;
/** 注册 DELETE API无认证 */
deleteNoAuth (path: string, handler: PluginRequestHandler): void;
// ==================== 页面注册 ====================
/**