diff --git a/src/routes/micro-app/lib/app-file.ts b/src/routes/micro-app/lib/app-file.ts index f7965b4..c68d853 100644 --- a/src/routes/micro-app/lib/app-file.ts +++ b/src/routes/micro-app/lib/app-file.ts @@ -23,7 +23,7 @@ export const loadAppInfo = async (): Promise => { const basePath = path.dirname(pkgs); if (!configFile) { configFile = path.join(basePath, 'apps.config.json'); - fs.writeFileSync(configFile, JSON.stringify({})); + fs.writeFileSync(configFile, JSON.stringify({ list: [] })); return { list: [] }; } const config = fs.readFileSync(configFile, 'utf-8'); diff --git a/src/routes/micro-app/list.ts b/src/routes/micro-app/list.ts index 3d88792..8e4c73c 100644 --- a/src/routes/micro-app/list.ts +++ b/src/routes/micro-app/list.ts @@ -1,6 +1,6 @@ import { app } from '@/app.ts'; import { MicroAppModel } from './models.ts'; -import { appPathCheck, installApp } from './module/install-app.ts'; +import { appPathCheck, getAppPathKeys, installApp, installAppFromKey } from './module/install-app.ts'; import { loadApp } from './module/load-app.ts'; import { manager } from './manager-app.ts'; @@ -10,6 +10,7 @@ app path: 'micro-app', key: 'upload', middleware: ['auth'], + description: 'Upload micro app', }) .define(async (ctx) => { const { files, collection } = ctx.query?.data; @@ -48,6 +49,7 @@ app .route({ path: 'micro-app', key: 'deploy', + description: 'Deploy micro app', }) .define(async (ctx) => { // const { id, key} = ctx.query?.data; @@ -80,6 +82,7 @@ app .route({ path: 'micro-app', key: 'load', + description: 'Load micro app, no use', }) .define(async (ctx) => { // const { key } = ctx.query?.data; @@ -95,8 +98,7 @@ app } catch (e) { ctx.throw(400, e.message); } - }) - .addTo(app); + }); // curl http://localhost:4002/api/router?path=micro-app&key=unload app @@ -115,3 +117,39 @@ app ctx.body = main; }) .addTo(app); + +app + .route({ + path: 'micro-app', + key: 'detect', + description: 'Detect micro app,检测apps的没有加载进来的app模块', + }) + .define(async (ctx) => { + const list = manager.getAllAppShowInfo(); + const appPathKeys = await getAppPathKeys(); + const notIn = appPathKeys.filter((key) => !list.find((item) => item.key === key)); + console.log('Not in', notIn); + const loadInfo = []; + if (notIn.length <= 0) { + loadInfo.push('ok'); + ctx.body = { + data: loadInfo, + }; + ctx.message = 'All apps are loaded'; + return; + } + for (const key of notIn) { + try { + const { showAppInfo } = await installAppFromKey(key); + await manager.add(showAppInfo); + loadInfo.push(`Load ${key} success`); + } catch (e) { + loadInfo.push(`Load ${key} error`); + } + } + ctx.body = { + data: loadInfo, + notIn, + }; + }) + .addTo(app); diff --git a/src/routes/micro-app/module/install-app.ts b/src/routes/micro-app/module/install-app.ts index 7854795..9f63d23 100644 --- a/src/routes/micro-app/module/install-app.ts +++ b/src/routes/micro-app/module/install-app.ts @@ -47,7 +47,14 @@ export const installApp = async (opts: InstallAppOpts) => { file: filePath, cwd: extractPath, }); - const pkgs = path.join(extractPath, 'package.json'); + return installAppFromKey(key); +}; +export const installAppFromKey = async (key: string) => { + const directory = path.join(appsPath, key); + if (!fileIsExist(directory)) { + throw new Error('App not found'); + } + const pkgs = path.join(directory, 'package.json'); if (!fileIsExist(pkgs)) { throw new Error('Invalid package.json'); } @@ -57,8 +64,7 @@ export const installApp = async (opts: InstallAppOpts) => { if (!name || !version || !app) { throw new Error('Invalid package.json'); } - - const readmeFile = path.join(extractPath, 'README.md'); + const readmeFile = path.join(directory, 'README.md'); let readmeDesc = ''; if (fileIsExist(readmeFile)) { readmeDesc = fs.readFileSync(readmeFile, 'utf-8'); @@ -71,11 +77,19 @@ export const installApp = async (opts: InstallAppOpts) => { version, // entry: app?.entry || '', - path: extractPath, + path: directory, origin: app, }; app.key = key; fs.writeFileSync(pkgs, JSON.stringify(pkg, null, 2)); - // fs.unlinkSync(filePath); - return { path: filePath, pkg, showAppInfo }; + return { pkg, showAppInfo }; +}; +export const getAppPathKeys = async () => { + let files = fs.readdirSync(appsPath); + files = files.filter((file) => { + const stat = fs.statSync(path.join(appsPath, file)); + if (file === 'node_modules') return false; + return stat.isDirectory(); + }); + return files; }; diff --git a/src/routes/micro-app/module/manager.ts b/src/routes/micro-app/module/manager.ts index 4a90528..d8eade6 100644 --- a/src/routes/micro-app/module/manager.ts +++ b/src/routes/micro-app/module/manager.ts @@ -4,6 +4,7 @@ import { fork } from 'child_process'; import { merge } from 'lodash-es'; import { deleteFileAppInfo } from './load-app.ts'; import { fileIsExist } from '@kevisual/use-config'; +import path from 'path'; // 共享 export const existDenpend = [ 'sequelize', // commonjs @@ -82,8 +83,13 @@ export class Manager { if (app.status !== 'running') { return; } - if (!fileIsExist(app.entry)) { - console.error('file not found'); + if (!fileIsExist(app.path)) { + console.error('app is not found'); + return; + } + const pathEntry = path.join(app.path, app.entry); + if (!fileIsExist(pathEntry)) { + console.error('file entry not found'); return; } const entry = app.entry + `?timestamp=${app?.timestamp}`; @@ -102,6 +108,7 @@ export class Manager { } else if (app.type === AppType.GatewayApp) { console.log('gateway app not support'); } + return true; } /** * create new app info @@ -124,10 +131,21 @@ export class Manager { async load() { // 从apps文件夹列表当中中加载app信息 const appInfos = await loadAppInfo(); + this.appInfo = appInfos; const list = appInfos?.list || []; for (const app of list) { try { - this.loadApp(app); + const loaded = await this.loadApp(app); + if (!loaded) { + // 加载失败,如果是running状态,设置为error + if (app.status === 'running') { + app.status = 'error'; + console.log('load app error', app); // save app error info + await this.saveAppInfo(app); + } + } else { + // console.log('load app success', app); + } } catch (e) { console.error('load app', e); } diff --git a/src/routes/micro-app/test/path-keys.test.ts b/src/routes/micro-app/test/path-keys.test.ts new file mode 100644 index 0000000..b134664 --- /dev/null +++ b/src/routes/micro-app/test/path-keys.test.ts @@ -0,0 +1,8 @@ +import { getAppPathKeys } from '../module/install-app.ts'; + +const main = () => { + const keys = getAppPathKeys(); + console.log('Keys', keys); +}; + +main();