mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 11:36:42 +00:00
feat: add saas plugin
This commit is contained in:
parent
60bbbb5f35
commit
897169a613
@ -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
|
||||
|
@ -10,8 +10,5 @@
|
||||
"deepmerge": "^4.2.2",
|
||||
"flat-to-nested": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nocobase/actions": "^0.4.0-alpha.7"
|
||||
},
|
||||
"gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e"
|
||||
}
|
||||
|
146
packages/plugin-saas/src/server.ts
Normal file
146
packages/plugin-saas/src/server.ts
Normal 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;
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user