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:
parent
ea0dd6e31a
commit
b1086ee728
@ -3,7 +3,7 @@ import http, { IncomingMessage } from 'http';
|
|||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
|
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
|
||||||
|
|
||||||
type AppSelector = (ctx) => Application | string;
|
type AppSelector = (req: IncomingMessage) => Application | string | undefined | null;
|
||||||
|
|
||||||
export class AppManager extends EventEmitter {
|
export class AppManager extends EventEmitter {
|
||||||
public applications: Map<string, Application> = new Map<string, Application>();
|
public applications: Map<string, Application> = new Map<string, Application>();
|
||||||
@ -63,7 +63,7 @@ export class AppManager extends EventEmitter {
|
|||||||
|
|
||||||
callback() {
|
callback() {
|
||||||
return async (req, res) => {
|
return async (req, res) => {
|
||||||
let handleApp = this.appSelector(req);
|
let handleApp = this.appSelector(req) || this.app;
|
||||||
|
|
||||||
if (typeof handleApp === 'string') {
|
if (typeof handleApp === 'string') {
|
||||||
handleApp = (await this.getApplication(handleApp)) || this.app;
|
handleApp = (await this.getApplication(handleApp)) || this.app;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@nocobase/plugin-multiple-apps",
|
"name": "@nocobase/plugin-multi-app-manager",
|
||||||
"version": "0.6.2-alpha.12",
|
"version": "0.6.2-alpha.12",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
@ -1,7 +1,6 @@
|
|||||||
import { Plugin } from '@nocobase/server';
|
import { Plugin, PluginManager } from '@nocobase/server';
|
||||||
import { ApplicationModel } from '../models/application';
|
|
||||||
import { mockServer } from '@nocobase/test';
|
import { mockServer } from '@nocobase/test';
|
||||||
import { PluginMultipleApps } from '../server';
|
import { PluginMultiAppManager } from '../server';
|
||||||
|
|
||||||
describe('test with start', () => {
|
describe('test with start', () => {
|
||||||
it('should load subApp on create', async () => {
|
it('should load subApp on create', async () => {
|
||||||
@ -24,11 +23,12 @@ describe('test with start', () => {
|
|||||||
|
|
||||||
const mockGetPluginByName = jest.fn();
|
const mockGetPluginByName = jest.fn();
|
||||||
mockGetPluginByName.mockReturnValue(TestPlugin);
|
mockGetPluginByName.mockReturnValue(TestPlugin);
|
||||||
ApplicationModel.getPluginByName = mockGetPluginByName;
|
PluginManager.resolvePlugin = mockGetPluginByName;
|
||||||
|
|
||||||
const app = mockServer();
|
const app = mockServer();
|
||||||
await app.cleanDb();
|
await app.cleanDb();
|
||||||
app.plugin(PluginMultipleApps);
|
|
||||||
|
app.plugin(PluginMultiAppManager);
|
||||||
|
|
||||||
await app.loadAndInstall();
|
await app.loadAndInstall();
|
||||||
await app.start();
|
await app.start();
|
||||||
@ -38,11 +38,9 @@ describe('test with start', () => {
|
|||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
name: 'sub1',
|
name: 'sub1',
|
||||||
plugins: [
|
options: {
|
||||||
{
|
plugins: ['test-package'],
|
||||||
name: 'test-package',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,7 +53,7 @@ describe('test with start', () => {
|
|||||||
it('should install into difference database', async () => {
|
it('should install into difference database', async () => {
|
||||||
const app = mockServer();
|
const app = mockServer();
|
||||||
await app.cleanDb();
|
await app.cleanDb();
|
||||||
app.plugin(PluginMultipleApps);
|
app.plugin(PluginMultiAppManager);
|
||||||
|
|
||||||
await app.loadAndInstall();
|
await app.loadAndInstall();
|
||||||
await app.start();
|
await app.start();
|
||||||
@ -65,11 +63,9 @@ describe('test with start', () => {
|
|||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
name: 'sub1',
|
name: 'sub1',
|
||||||
plugins: [
|
options: {
|
||||||
{
|
plugins: ['@nocobase/plugin-ui-schema-storage'],
|
||||||
name: '@nocobase/plugin-ui-schema-storage',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.destroy();
|
await app.destroy();
|
||||||
@ -84,7 +80,7 @@ describe('test with start', () => {
|
|||||||
|
|
||||||
let app = mockServer();
|
let app = mockServer();
|
||||||
await app.cleanDb();
|
await app.cleanDb();
|
||||||
app.plugin(PluginMultipleApps);
|
app.plugin(PluginMultiAppManager);
|
||||||
|
|
||||||
await app.loadAndInstall();
|
await app.loadAndInstall();
|
||||||
await app.start();
|
await app.start();
|
||||||
@ -93,16 +89,14 @@ describe('test with start', () => {
|
|||||||
|
|
||||||
const mockGetPluginByName = jest.fn();
|
const mockGetPluginByName = jest.fn();
|
||||||
mockGetPluginByName.mockReturnValue(TestPlugin);
|
mockGetPluginByName.mockReturnValue(TestPlugin);
|
||||||
ApplicationModel.getPluginByName = mockGetPluginByName;
|
PluginManager.resolvePlugin = mockGetPluginByName;
|
||||||
|
|
||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
name: 'sub1',
|
name: 'sub1',
|
||||||
plugins: [
|
options: {
|
||||||
{
|
plugins: ['test-package'],
|
||||||
name: 'test-package',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -114,7 +108,7 @@ describe('test with start', () => {
|
|||||||
database: app.db,
|
database: app.db,
|
||||||
});
|
});
|
||||||
|
|
||||||
newApp.plugin(PluginMultipleApps);
|
newApp.plugin(PluginMultiAppManager);
|
||||||
await newApp.db.reconnect();
|
await newApp.db.reconnect();
|
||||||
|
|
||||||
await newApp.load();
|
await newApp.load();
|
@ -1,6 +1,6 @@
|
|||||||
import { mockServer, MockServer } from '@nocobase/test';
|
import { mockServer, MockServer } from '@nocobase/test';
|
||||||
import { Database } from '@nocobase/database';
|
import { Database } from '@nocobase/database';
|
||||||
import { PluginMultipleApps } from '../server';
|
import { PluginMultiAppManager } from '../server';
|
||||||
|
|
||||||
describe('multiple apps create', () => {
|
describe('multiple apps create', () => {
|
||||||
let app: MockServer;
|
let app: MockServer;
|
||||||
@ -10,7 +10,7 @@ describe('multiple apps create', () => {
|
|||||||
app = mockServer({});
|
app = mockServer({});
|
||||||
db = app.db;
|
db = app.db;
|
||||||
await app.cleanDb();
|
await app.cleanDb();
|
||||||
app.plugin(PluginMultipleApps);
|
app.plugin(PluginMultiAppManager);
|
||||||
|
|
||||||
await app.loadAndInstall();
|
await app.loadAndInstall();
|
||||||
});
|
});
|
||||||
@ -51,29 +51,30 @@ describe('multiple apps create', () => {
|
|||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
name: 'miniApp',
|
name: 'miniApp',
|
||||||
plugins: [
|
options: {
|
||||||
{
|
plugins: [['@nocobase/plugin-ui-schema-storage', { test: 'B' }]],
|
||||||
name: '@nocobase/plugin-ui-schema-storage',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const miniApp = app.appManager.applications.get('miniApp');
|
const miniApp = app.appManager.applications.get('miniApp');
|
||||||
expect(miniApp).toBeDefined();
|
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 () => {
|
it('should lazy load applications', async () => {
|
||||||
await db.getRepository('applications').create({
|
await db.getRepository('applications').create({
|
||||||
values: {
|
values: {
|
||||||
name: 'miniApp',
|
name: 'miniApp',
|
||||||
plugins: [
|
options: {
|
||||||
{
|
plugins: ['@nocobase/plugin-ui-schema-storage'],
|
||||||
name: '@nocobase/plugin-ui-schema-storage',
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -4,21 +4,26 @@ export default defineCollection({
|
|||||||
name: 'applications',
|
name: 'applications',
|
||||||
model: 'ApplicationModel',
|
model: 'ApplicationModel',
|
||||||
autoGenId: false,
|
autoGenId: false,
|
||||||
|
title: '{{t("Applications")}}',
|
||||||
|
sortable: 'sort',
|
||||||
|
createdBy: true,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'uid',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
prefix: 'a',
|
||||||
|
interface: 'input',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Application name")}}',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'json',
|
type: 'json',
|
||||||
name: 'options',
|
name: 'options',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'hasMany',
|
|
||||||
name: 'plugins',
|
|
||||||
target: 'applicationPlugins',
|
|
||||||
foreignKey: 'applicationName',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
1
packages/plugins/multi-app-manager/src/index.ts
Normal file
1
packages/plugins/multi-app-manager/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { PluginMultiAppManager as default } from './server';
|
@ -8,10 +8,6 @@ interface registerAppOptions extends TransactionAble {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ApplicationModel extends Model {
|
export class ApplicationModel extends Model {
|
||||||
static getPluginByName(pluginName: string) {
|
|
||||||
return require(pluginName).default;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDatabaseConfig(app: Application): IDatabaseOptions {
|
static getDatabaseConfig(app: Application): IDatabaseOptions {
|
||||||
return lodash.cloneDeep(
|
return lodash.cloneDeep(
|
||||||
lodash.isPlainObject(app.options.database)
|
lodash.isPlainObject(app.options.database)
|
||||||
@ -23,15 +19,12 @@ export class ApplicationModel extends Model {
|
|||||||
async registerToMainApp(mainApp: Application, options: registerAppOptions) {
|
async registerToMainApp(mainApp: Application, options: registerAppOptions) {
|
||||||
const { transaction } = options;
|
const { transaction } = options;
|
||||||
const appName = this.get('name') as string;
|
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 app = mainApp.appManager.createApplication(appName, {
|
||||||
const plugins = await this.getPlugins({ transaction });
|
...ApplicationModel.initOptions(appName, mainApp),
|
||||||
|
...appOptions,
|
||||||
for (const pluginInstance of plugins) {
|
});
|
||||||
const plugin = ApplicationModel.getPluginByName(pluginInstance.get('name') as string);
|
|
||||||
app.plugin(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.on('beforeInstall', async function createDatabase() {
|
app.on('beforeInstall', async function createDatabase() {
|
||||||
const { host, port, username, password, database, dialect } = ApplicationModel.getDatabaseConfig(app);
|
const { host, port, username, password, database, dialect } = ApplicationModel.getDatabaseConfig(app);
|
@ -3,7 +3,7 @@ import { resolve } from 'path';
|
|||||||
import { ApplicationModel } from './models/application';
|
import { ApplicationModel } from './models/application';
|
||||||
import { AppManager } from '@nocobase/server';
|
import { AppManager } from '@nocobase/server';
|
||||||
|
|
||||||
export class PluginMultipleApps extends Plugin {
|
export class PluginMultiAppManager extends Plugin {
|
||||||
getName(): string {
|
getName(): string {
|
||||||
return this.getPackageName(__dirname);
|
return this.getPackageName(__dirname);
|
||||||
}
|
}
|
@ -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',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
@ -14,7 +14,7 @@ Promise.all(packageDirs.map((d) => getDirectories(d)))
|
|||||||
.then((res) => res.flat())
|
.then((res) => res.flat())
|
||||||
.then((res) =>
|
.then((res) =>
|
||||||
res.forEach((d) => {
|
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) {
|
if (error) {
|
||||||
console.log(`error: ${error.message}`);
|
console.log(`error: ${error.message}`);
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user