Fix multiple apps (#316)

* chore: multiple apps

* fix: multiple apps with application options

* fix: multiple apps AppSelector type

* chore: multiple apps with plugin config

* chore: rename multiple-apps to multiple-apps-manager

* chore: application association

* chore: plugin multi-app manager
This commit is contained in:
ChengLei Shao 2022-04-24 20:22:50 +08:00 committed by GitHub
parent ea0dd6e31a
commit b1086ee728
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 55 additions and 78 deletions

View File

@ -3,7 +3,7 @@ import http, { IncomingMessage } from 'http';
import EventEmitter from 'events';
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
type AppSelector = (ctx) => Application | string;
type AppSelector = (req: IncomingMessage) => Application | string | undefined | null;
export class AppManager extends EventEmitter {
public applications: Map<string, Application> = new Map<string, Application>();
@ -63,7 +63,7 @@ export class AppManager extends EventEmitter {
callback() {
return async (req, res) => {
let handleApp = this.appSelector(req);
let handleApp = this.appSelector(req) || this.app;
if (typeof handleApp === 'string') {
handleApp = (await this.getApplication(handleApp)) || this.app;

View File

@ -1,5 +1,5 @@
{
"name": "@nocobase/plugin-multiple-apps",
"name": "@nocobase/plugin-multi-app-manager",
"version": "0.6.2-alpha.12",
"main": "lib/index.js",
"license": "MIT",

View File

@ -1,7 +1,6 @@
import { Plugin } from '@nocobase/server';
import { ApplicationModel } from '../models/application';
import { Plugin, PluginManager } from '@nocobase/server';
import { mockServer } from '@nocobase/test';
import { PluginMultipleApps } from '../server';
import { PluginMultiAppManager } from '../server';
describe('test with start', () => {
it('should load subApp on create', async () => {
@ -24,11 +23,12 @@ describe('test with start', () => {
const mockGetPluginByName = jest.fn();
mockGetPluginByName.mockReturnValue(TestPlugin);
ApplicationModel.getPluginByName = mockGetPluginByName;
PluginManager.resolvePlugin = mockGetPluginByName;
const app = mockServer();
await app.cleanDb();
app.plugin(PluginMultipleApps);
app.plugin(PluginMultiAppManager);
await app.loadAndInstall();
await app.start();
@ -38,11 +38,9 @@ describe('test with start', () => {
await db.getRepository('applications').create({
values: {
name: 'sub1',
plugins: [
{
name: 'test-package',
},
],
options: {
plugins: ['test-package'],
},
},
});
@ -55,7 +53,7 @@ describe('test with start', () => {
it('should install into difference database', async () => {
const app = mockServer();
await app.cleanDb();
app.plugin(PluginMultipleApps);
app.plugin(PluginMultiAppManager);
await app.loadAndInstall();
await app.start();
@ -65,11 +63,9 @@ describe('test with start', () => {
await db.getRepository('applications').create({
values: {
name: 'sub1',
plugins: [
{
name: '@nocobase/plugin-ui-schema-storage',
},
],
options: {
plugins: ['@nocobase/plugin-ui-schema-storage'],
},
},
});
await app.destroy();
@ -84,7 +80,7 @@ describe('test with start', () => {
let app = mockServer();
await app.cleanDb();
app.plugin(PluginMultipleApps);
app.plugin(PluginMultiAppManager);
await app.loadAndInstall();
await app.start();
@ -93,16 +89,14 @@ describe('test with start', () => {
const mockGetPluginByName = jest.fn();
mockGetPluginByName.mockReturnValue(TestPlugin);
ApplicationModel.getPluginByName = mockGetPluginByName;
PluginManager.resolvePlugin = mockGetPluginByName;
await db.getRepository('applications').create({
values: {
name: 'sub1',
plugins: [
{
name: 'test-package',
},
],
options: {
plugins: ['test-package'],
},
},
});
@ -114,7 +108,7 @@ describe('test with start', () => {
database: app.db,
});
newApp.plugin(PluginMultipleApps);
newApp.plugin(PluginMultiAppManager);
await newApp.db.reconnect();
await newApp.load();

View File

@ -1,6 +1,6 @@
import { mockServer, MockServer } from '@nocobase/test';
import { Database } from '@nocobase/database';
import { PluginMultipleApps } from '../server';
import { PluginMultiAppManager } from '../server';
describe('multiple apps create', () => {
let app: MockServer;
@ -10,7 +10,7 @@ describe('multiple apps create', () => {
app = mockServer({});
db = app.db;
await app.cleanDb();
app.plugin(PluginMultipleApps);
app.plugin(PluginMultiAppManager);
await app.loadAndInstall();
});
@ -51,29 +51,30 @@ describe('multiple apps create', () => {
await db.getRepository('applications').create({
values: {
name: 'miniApp',
plugins: [
{
name: '@nocobase/plugin-ui-schema-storage',
},
],
options: {
plugins: [['@nocobase/plugin-ui-schema-storage', { test: 'B' }]],
},
},
});
const miniApp = app.appManager.applications.get('miniApp');
expect(miniApp).toBeDefined();
expect(miniApp.pm.get('@nocobase/plugin-ui-schema-storage')).toBeDefined();
const plugin = miniApp.pm.get('@nocobase/plugin-ui-schema-storage');
expect(plugin).toBeDefined();
expect(plugin.options).toEqual({
test: 'B',
});
});
it('should lazy load applications', async () => {
await db.getRepository('applications').create({
values: {
name: 'miniApp',
plugins: [
{
name: '@nocobase/plugin-ui-schema-storage',
},
],
options: {
plugins: ['@nocobase/plugin-ui-schema-storage'],
},
},
});

View File

@ -4,21 +4,26 @@ export default defineCollection({
name: 'applications',
model: 'ApplicationModel',
autoGenId: false,
title: '{{t("Applications")}}',
sortable: 'sort',
createdBy: true,
fields: [
{
type: 'string',
type: 'uid',
name: 'name',
primaryKey: true,
prefix: 'a',
interface: 'input',
uiSchema: {
type: 'string',
title: '{{t("Application name")}}',
'x-component': 'Input',
'x-read-pretty': true,
},
},
{
type: 'json',
name: 'options',
},
{
type: 'hasMany',
name: 'plugins',
target: 'applicationPlugins',
foreignKey: 'applicationName',
},
],
});

View File

@ -0,0 +1 @@
export { PluginMultiAppManager as default } from './server';

View File

@ -8,10 +8,6 @@ interface registerAppOptions extends TransactionAble {
}
export class ApplicationModel extends Model {
static getPluginByName(pluginName: string) {
return require(pluginName).default;
}
static getDatabaseConfig(app: Application): IDatabaseOptions {
return lodash.cloneDeep(
lodash.isPlainObject(app.options.database)
@ -23,15 +19,12 @@ export class ApplicationModel extends Model {
async registerToMainApp(mainApp: Application, options: registerAppOptions) {
const { transaction } = options;
const appName = this.get('name') as string;
const app = mainApp.appManager.createApplication(appName, ApplicationModel.initOptions(appName, mainApp));
const appOptions = (this.get('options') as any) || {};
// @ts-ignore
const plugins = await this.getPlugins({ transaction });
for (const pluginInstance of plugins) {
const plugin = ApplicationModel.getPluginByName(pluginInstance.get('name') as string);
app.plugin(plugin);
}
const app = mainApp.appManager.createApplication(appName, {
...ApplicationModel.initOptions(appName, mainApp),
...appOptions,
});
app.on('beforeInstall', async function createDatabase() {
const { host, port, username, password, database, dialect } = ApplicationModel.getDatabaseConfig(app);

View File

@ -3,7 +3,7 @@ import { resolve } from 'path';
import { ApplicationModel } from './models/application';
import { AppManager } from '@nocobase/server';
export class PluginMultipleApps extends Plugin {
export class PluginMultiAppManager extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}

View File

@ -1,17 +0,0 @@
import { defineCollection } from '@nocobase/database';
export default defineCollection({
name: 'applicationPlugins',
timestamps: false,
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'belongsTo',
name: 'application',
foreignKey: 'applicationName',
},
],
});

View File

@ -14,7 +14,7 @@ Promise.all(packageDirs.map((d) => getDirectories(d)))
.then((res) => res.flat())
.then((res) =>
res.forEach((d) => {
exec(`cd ${d} && npm unpublish -f && npm publish`, (error, stdout, stderr) => {
exec(`cd ${d} && npm unpublish -f; npm publish`, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;