You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
3.8 KiB
100 lines
3.8 KiB
import fs from "fs"
|
|
import path from "path"
|
|
import { logger } from "@/logger.js"
|
|
import compose from "koa-compose"
|
|
import { Next, ParameterizedContext } from "koa"
|
|
|
|
async function scanControllers(rootDir: string) {
|
|
const routers = []
|
|
const stack: string[] = [rootDir]
|
|
while (stack.length) {
|
|
const dir = stack.pop()
|
|
if (!dir) continue
|
|
let files
|
|
try {
|
|
files = fs.readdirSync(dir)
|
|
} catch (error: any) {
|
|
logger.error(`[控制器注册] ❌ 读取目录失败 ${dir}: ${error.message}`)
|
|
continue
|
|
}
|
|
|
|
for (const file of files) {
|
|
if (file.startsWith("_")) continue
|
|
const fullPath = path.join(dir, file)
|
|
let stat
|
|
try {
|
|
stat = fs.statSync(fullPath)
|
|
} catch (error: any) {
|
|
logger.error(`[控制器注册] ❌ 读取文件信息失败 ${fullPath}: ${error.message}`)
|
|
continue
|
|
}
|
|
|
|
if (stat.isDirectory()) {
|
|
stack.push(fullPath)
|
|
continue
|
|
}
|
|
|
|
if (!fullPath.replace(/\\/g, "/").includes("/controller/")) continue
|
|
|
|
let fileName = fullPath.replace(rootDir + path.sep, "")
|
|
|
|
try {
|
|
const controllerModule = await import(fullPath)
|
|
const controller = controllerModule.default || controllerModule
|
|
if (!controller) {
|
|
logger.warn(`[控制器注册] ${fileName} - 缺少默认导出,跳过注册`)
|
|
continue
|
|
}
|
|
|
|
let routesFactory = controller.createRoutes || controller.default?.createRoutes || controller.default || controller
|
|
if (typeof routesFactory === "function") {
|
|
routesFactory = routesFactory.bind(controller)
|
|
}
|
|
if (typeof routesFactory !== "function") {
|
|
logger.warn(`[控制器注册] ⚠️ ${fileName} - 未找到 createRoutes 方法或导出对象`)
|
|
continue
|
|
}
|
|
|
|
let routerResult
|
|
try {
|
|
routerResult = routesFactory()
|
|
} catch (error: any) {
|
|
logger.error(`[控制器注册] ❌ ${fileName} - createRoutes() 执行失败: ${error.message}`)
|
|
continue
|
|
}
|
|
|
|
const list = Array.isArray(routerResult) ? routerResult : [routerResult]
|
|
let added = 0
|
|
for (const r of list) {
|
|
if (r && typeof r.middleware === "function") {
|
|
routers.push(r)
|
|
added++
|
|
} else {
|
|
logger.warn(`[控制器注册] ⚠️ ${fileName} - createRoutes() 返回的部分路由器对象无效`)
|
|
}
|
|
}
|
|
if (added > 0) logger.debug(`[控制器注册] ✅ ${fileName} - 创建成功 (${added})`)
|
|
} catch (importError: any) {
|
|
logger.error(`[控制器注册] ❌ ${fileName} - 模块导入失败: ${importError.message}`)
|
|
logger.error(importError)
|
|
}
|
|
}
|
|
}
|
|
return routers
|
|
}
|
|
|
|
export default async function (options: { root: string, handleBeforeEachRequest: Function }) {
|
|
const { root, handleBeforeEachRequest } = options
|
|
if (!root) {
|
|
throw new Error("controller root is required")
|
|
}
|
|
const routers = await scanControllers(root)
|
|
const allRouters: any[] = []
|
|
for (let i = 0; i < routers.length; i++) {
|
|
const router = routers[i]
|
|
allRouters.push(router.middleware((options = {}) => handleBeforeEachRequest(options)))
|
|
}
|
|
return async function (ctx: ParameterizedContext, next: Next) {
|
|
return await compose(allRouters)(ctx, next)
|
|
}
|
|
}
|
|
|