feat: add saas plugin

This commit is contained in:
chenos 2021-09-28 00:18:09 +08:00
parent 60bbbb5f35
commit 897169a613
4 changed files with 149 additions and 7 deletions

View File

@ -180,7 +180,8 @@ export class Table {
public modelInit(reinitialize: Reinitialize = false) {
if (reinitialize || !this.Model) {
this.Model = this.defaultModel || class extends Model { };
let DefaultModel = this.defaultModel;
this.Model = DefaultModel ? (class extends DefaultModel {}) : (class extends Model { });
this.Model.database = this.database;
// 关系的建立是在 model.init 之后在配置中表字段Column和关系Relation都在 fields
// 所以需要单独提炼出 associations 字段,并在 Model.init 之后执行 Model.associate

View File

@ -10,8 +10,5 @@
"deepmerge": "^4.2.2",
"flat-to-nested": "^1.1.1"
},
"devDependencies": {
"@nocobase/actions": "^0.4.0-alpha.7"
},
"gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e"
}

View File

@ -0,0 +1,146 @@
import compose from 'koa-compose';
import { Application, PluginOptions } from '@nocobase/server';
import Koa from 'koa';
function createApp(opts) {
const { name } = opts;
const options = {
database: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
host: process.env.DB_HOST,
port: process.env.DB_PORT as any,
dialect: process.env.DB_DIALECT as any,
dialectOptions: {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
},
pool: {
max: 5,
min: 0,
acquire: 60000,
idle: 10000,
},
logging: process.env.DB_LOG_SQL === 'on' ? console.log : false,
define: {},
sync: {
force: false,
alter: {
drop: false,
},
},
},
// dataWrapping: false,
resourcer: {
prefix: `/api/saas/${name}`,
},
};
const app = new Application(options);
app.db.sequelize.beforeDefine((model, options) => {
options.tableName = `saas_${name}_${
options.tableName || options.name.plural
}`;
});
app.resource({
name: 'saas',
actions: {
async getInfo(ctx, next) {
ctx.body = {
m: Object.values(ctx.db.sequelize.models).map((m: any) => m.tableName),
};
await next();
},
},
});
const plugins = [
'@nocobase/plugin-collections',
'@nocobase/plugin-ui-router',
'@nocobase/plugin-ui-schema',
'@nocobase/plugin-users',
'@nocobase/plugin-action-logs',
'@nocobase/plugin-file-manager',
'@nocobase/plugin-permissions',
'@nocobase/plugin-export',
'@nocobase/plugin-system-settings',
'@nocobase/plugin-china-region',
];
for (const plugin of plugins) {
app.plugin(
require(`${plugin}/${__filename.endsWith('.ts') ? 'src' : 'lib'}/server`)
.default,
);
}
return app;
}
function multiApps({ getAppName }) {
return async function (ctx: Koa.Context, next) {
const appName = getAppName(ctx);
if (!appName) {
return next();
}
const App = ctx.db.getModel('applications');
const model = await App.findOne({
where: { name: appName },
});
if (!model) {
return next();
}
const apps = ctx.app['apps'];
if (!apps.has(appName)) {
const app = createApp({
name: appName,
});
await app.load();
apps.set(appName, app);
}
const app = apps.get(appName);
// 完全隔离的做法
const handleRequest = app.callback();
await handleRequest(ctx.req, ctx.res);
};
}
export default {
name: 'saas',
async load() {
this.app['apps'] = new Map<string, Application>();
this.app.collection({
name: 'applications',
fields: [
{ type: 'string', name: 'name', unique: true },
{ type: 'belongsTo', name: 'user' },
],
});
this.app.use(
multiApps({
getAppName(ctx) {
return ctx.path.split('/')[3];
},
}),
);
this.app
.command('app:create')
.argument('<appName>')
.action(async (appName) => {
const App = this.app.db.getModel('applications');
const model = await App.findOne({
where: {
name: appName,
},
});
if (!model) {
await App.create({
name: appName,
});
}
await this.app.destroy();
});
},
} as PluginOptions;

View File

@ -104,7 +104,6 @@ export class Application<
console.log('db sync...');
const cli = args.pop();
const force = cli.opts()?.force;
await this.load();
await this.db.sync(
force
? {
@ -123,7 +122,6 @@ export class Application<
// .option('-f, --force')
.action(async (...args) => {
const cli = args.pop();
await this.load();
await this.db.sync({
force: true,
alter: {
@ -141,7 +139,6 @@ export class Application<
const cli = args.pop();
console.log(args);
const opts = cli.opts();
await this.load();
await this.emitAsync('beforeStart');
this.listen(opts.port || 3000);
console.log(`http://localhost:${opts.port || 3000}/`);
@ -274,6 +271,7 @@ export class Application<
}
async parse(argv = process.argv) {
await this.load();
return this.cli.parseAsync(argv);
}