2021-09-28 00:18:09 +08:00
|
|
|
|
import { Application, PluginOptions } from '@nocobase/server';
|
|
|
|
|
import Koa from 'koa';
|
2021-09-30 09:44:23 +08:00
|
|
|
|
import { Model } from '@nocobase/database';
|
2021-10-01 00:50:06 +08:00
|
|
|
|
import path from 'path';
|
2021-09-28 00:18:09 +08:00
|
|
|
|
|
|
|
|
|
function createApp(opts) {
|
|
|
|
|
const { name } = opts;
|
|
|
|
|
const options = {
|
|
|
|
|
database: {
|
|
|
|
|
username: process.env.DB_USER,
|
|
|
|
|
password: process.env.DB_PASSWORD,
|
2021-09-30 15:16:41 +08:00
|
|
|
|
database: name,
|
2021-09-28 00:18:09 +08:00
|
|
|
|
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,
|
|
|
|
|
},
|
2021-10-01 23:31:49 +08:00
|
|
|
|
logging: process.env.DB_LOG_SQL === 'on' ? console.log : false,
|
2021-09-28 00:18:09 +08:00
|
|
|
|
define: {},
|
|
|
|
|
sync: {
|
|
|
|
|
force: false,
|
|
|
|
|
alter: {
|
|
|
|
|
drop: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-09-29 07:38:05 +08:00
|
|
|
|
// 不能再 bodyParser,会卡死
|
2021-09-30 15:16:41 +08:00
|
|
|
|
// bodyParser: false,
|
2021-09-28 00:18:09 +08:00
|
|
|
|
// dataWrapping: false,
|
|
|
|
|
resourcer: {
|
2021-09-30 15:16:41 +08:00
|
|
|
|
prefix: '/api',
|
|
|
|
|
// prefix: `/api/multiapps/${name}`,
|
2021-09-28 00:18:09 +08:00
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
const app = new Application(options);
|
|
|
|
|
|
2021-09-30 15:16:41 +08:00
|
|
|
|
// app.db.sequelize.beforeDefine((model, options) => {
|
|
|
|
|
// options.tableName = `multiapps_${name}_${
|
|
|
|
|
// options.tableName || options.name.plural
|
|
|
|
|
// }`;
|
|
|
|
|
// });
|
2021-09-28 00:18:09 +08:00
|
|
|
|
|
|
|
|
|
const plugins = [
|
2021-09-28 18:20:21 +08:00
|
|
|
|
'@nocobase/plugin-ui-router',
|
2021-10-01 00:50:06 +08:00
|
|
|
|
'@nocobase/plugin-ui-schema',
|
2021-09-28 18:20:21 +08:00
|
|
|
|
'@nocobase/plugin-collections',
|
2021-09-28 00:18:09 +08:00
|
|
|
|
'@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',
|
|
|
|
|
];
|
|
|
|
|
|
2021-10-01 23:31:49 +08:00
|
|
|
|
// console.log('process.cwd()', process.cwd())
|
|
|
|
|
|
2021-09-28 00:18:09 +08:00
|
|
|
|
for (const plugin of plugins) {
|
|
|
|
|
app.plugin(
|
|
|
|
|
require(`${plugin}/${__filename.endsWith('.ts') ? 'src' : 'lib'}/server`)
|
|
|
|
|
.default,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-01 23:31:49 +08:00
|
|
|
|
app.plugin(
|
|
|
|
|
require(`@nocobase/plugin-client/${__filename.endsWith('.ts') ? 'src' : 'lib'}/server`).default, {
|
|
|
|
|
dist: path.resolve(process.cwd(), './dist'),
|
2021-10-07 19:35:53 +08:00
|
|
|
|
importDemo: true,
|
2021-10-01 23:31:49 +08:00
|
|
|
|
});
|
|
|
|
|
|
2021-09-28 00:18:09 +08:00
|
|
|
|
return app;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-01 23:31:49 +08:00
|
|
|
|
// import send from 'koa-send';
|
|
|
|
|
// import serve from 'koa-static';
|
|
|
|
|
|
2021-09-28 00:18:09 +08:00
|
|
|
|
function multiApps({ getAppName }) {
|
|
|
|
|
return async function (ctx: Koa.Context, next) {
|
|
|
|
|
const appName = getAppName(ctx);
|
2021-09-30 15:16:41 +08:00
|
|
|
|
console.log({ appName });
|
2021-09-28 00:18:09 +08:00
|
|
|
|
if (!appName) {
|
|
|
|
|
return next();
|
|
|
|
|
}
|
2021-09-30 15:16:41 +08:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
const App = ctx.app.db.getModel('applications');
|
2021-09-28 00:18:09 +08:00
|
|
|
|
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();
|
2021-10-01 23:31:49 +08:00
|
|
|
|
await app.emitAsync('beforeStart');
|
2021-09-28 00:18:09 +08:00
|
|
|
|
apps.set(appName, app);
|
|
|
|
|
}
|
2021-09-28 18:20:21 +08:00
|
|
|
|
|
2021-09-30 09:44:23 +08:00
|
|
|
|
console.log('..........................start........................');
|
2021-09-28 00:18:09 +08:00
|
|
|
|
// 完全隔离的做法
|
2021-09-28 18:20:21 +08:00
|
|
|
|
const app = apps.get(appName) as Application;
|
2021-09-30 15:16:41 +08:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
console.log(app.db.options);
|
2021-09-28 18:20:21 +08:00
|
|
|
|
const bodyParser = async (ctx2, next) => {
|
|
|
|
|
// @ts-ignore
|
2021-09-30 15:16:41 +08:00
|
|
|
|
// ctx2.request.body = ctx.request.body || {};
|
2021-09-28 18:20:21 +08:00
|
|
|
|
await next();
|
|
|
|
|
};
|
|
|
|
|
app.middleware.unshift(bodyParser);
|
2021-09-28 00:18:09 +08:00
|
|
|
|
const handleRequest = app.callback();
|
|
|
|
|
await handleRequest(ctx.req, ctx.res);
|
2021-09-28 18:20:21 +08:00
|
|
|
|
const index = app.middleware.indexOf(bodyParser);
|
|
|
|
|
app.middleware.splice(index, 1);
|
2021-09-30 09:44:23 +08:00
|
|
|
|
console.log('..........................end........................');
|
2021-10-01 23:31:49 +08:00
|
|
|
|
await next();
|
2021-09-28 00:18:09 +08:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'saas',
|
|
|
|
|
async load() {
|
|
|
|
|
this.app['apps'] = new Map<string, Application>();
|
|
|
|
|
this.app.collection({
|
|
|
|
|
name: 'applications',
|
2021-09-29 07:38:05 +08:00
|
|
|
|
title: '应用',
|
2021-09-28 00:18:09 +08:00
|
|
|
|
fields: [
|
2021-09-29 07:38:05 +08:00
|
|
|
|
{
|
2021-09-30 09:44:23 +08:00
|
|
|
|
type: 'uid',
|
2021-09-29 07:38:05 +08:00
|
|
|
|
name: 'name',
|
2021-09-30 09:44:23 +08:00
|
|
|
|
prefix: 'a',
|
|
|
|
|
interface: 'string',
|
|
|
|
|
unique: true,
|
|
|
|
|
uiSchema: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
title: '应用标识',
|
|
|
|
|
'x-component': 'Input',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'string',
|
|
|
|
|
name: 'title',
|
2021-09-29 07:38:05 +08:00
|
|
|
|
interface: 'string',
|
|
|
|
|
uiSchema: {
|
|
|
|
|
type: 'string',
|
2021-09-30 09:44:23 +08:00
|
|
|
|
title: '应用名称',
|
2021-09-29 07:38:05 +08:00
|
|
|
|
'x-component': 'Input',
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-10-08 23:14:55 +08:00
|
|
|
|
{
|
|
|
|
|
type: 'string',
|
|
|
|
|
name: 'email',
|
|
|
|
|
interface: 'email',
|
|
|
|
|
uiSchema: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
title: '邮箱',
|
|
|
|
|
'x-component': 'Input',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'string',
|
|
|
|
|
name: 'note',
|
|
|
|
|
interface: 'textarea',
|
|
|
|
|
uiSchema: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
title: '你希望用 NocoBase 来做什么',
|
|
|
|
|
'x-component': 'Input.TextArea',
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-09-30 09:44:23 +08:00
|
|
|
|
{
|
|
|
|
|
type: 'string',
|
|
|
|
|
name: 'status',
|
|
|
|
|
interface: 'select',
|
|
|
|
|
uiSchema: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
title: '状态',
|
|
|
|
|
'x-component': 'Select',
|
|
|
|
|
default: 'initializing',
|
|
|
|
|
enum: [
|
|
|
|
|
{ value: 'initializing', label: '正在初始化' },
|
|
|
|
|
{ value: 'running', label: '运行中' },
|
|
|
|
|
{ value: 'stopped', label: '已停止' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-09-28 00:18:09 +08:00
|
|
|
|
],
|
|
|
|
|
});
|
2021-09-30 15:16:41 +08:00
|
|
|
|
this.app.middleware.unshift(multiApps({
|
|
|
|
|
getAppName(ctx) {
|
2021-10-01 23:31:49 +08:00
|
|
|
|
console.log('ctx.hostname', ctx.hostname);
|
|
|
|
|
const hostname = ctx.get('X-Hostname') || ctx.hostname;
|
2021-09-30 15:16:41 +08:00
|
|
|
|
if (!hostname) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const keys = hostname.split('.');
|
|
|
|
|
if (keys.length < 4) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return keys.shift();
|
|
|
|
|
},
|
|
|
|
|
}));
|
2021-09-30 09:44:23 +08:00
|
|
|
|
this.app.db.on('applications.afterCreate', async (model: Model) => {
|
2021-09-29 07:38:05 +08:00
|
|
|
|
const name = model.get('name');
|
|
|
|
|
(async () => {
|
2021-10-01 00:50:06 +08:00
|
|
|
|
await this.app.db.sequelize.query(`CREATE DATABASE "${name}";`);
|
|
|
|
|
const app = createApp({
|
|
|
|
|
name,
|
|
|
|
|
});
|
|
|
|
|
console.log('creating........')
|
2021-09-29 07:38:05 +08:00
|
|
|
|
await app.load();
|
|
|
|
|
await app.db.sync({
|
|
|
|
|
force: true,
|
|
|
|
|
alter: {
|
|
|
|
|
drop: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
2021-10-01 00:50:06 +08:00
|
|
|
|
await app.emitAsync('beforeStart');
|
2021-09-29 07:38:05 +08:00
|
|
|
|
await app.emitAsync('db.init');
|
2021-09-30 09:44:23 +08:00
|
|
|
|
this.app['apps'].set(name, app);
|
|
|
|
|
model.set('status', 'running');
|
|
|
|
|
await model.save({ hooks: false });
|
2021-10-08 23:14:55 +08:00
|
|
|
|
await this.app.db.emitAsync('applications.afterInit', model);
|
2021-09-30 09:44:23 +08:00
|
|
|
|
})();
|
2021-09-29 07:38:05 +08:00
|
|
|
|
});
|
2021-09-28 00:18:09 +08:00
|
|
|
|
this.app
|
2021-10-01 00:50:06 +08:00
|
|
|
|
.command('app:db:sync')
|
2021-09-28 00:18:09 +08:00
|
|
|
|
.argument('<appName>')
|
|
|
|
|
.action(async (appName) => {
|
2021-10-01 00:50:06 +08:00
|
|
|
|
const app = createApp({
|
|
|
|
|
name: appName,
|
|
|
|
|
});
|
|
|
|
|
await app.load();
|
|
|
|
|
await app.emitAsync('beforeStart');
|
|
|
|
|
await app.db.sync();
|
|
|
|
|
await app.destroy();
|
|
|
|
|
await this.app.destroy();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.app
|
|
|
|
|
.command('app:create')
|
|
|
|
|
.argument('<appName>')
|
|
|
|
|
.action(async (name) => {
|
|
|
|
|
await this.app.db.sequelize.query(`CREATE DATABASE "${name}";`);
|
|
|
|
|
const app = createApp({
|
|
|
|
|
name,
|
|
|
|
|
});
|
|
|
|
|
console.log('creating........')
|
|
|
|
|
await app.load();
|
|
|
|
|
await app.db.sync({
|
|
|
|
|
force: true,
|
|
|
|
|
alter: {
|
|
|
|
|
drop: true,
|
2021-09-28 00:18:09 +08:00
|
|
|
|
},
|
|
|
|
|
});
|
2021-10-01 00:50:06 +08:00
|
|
|
|
await app.emitAsync('beforeStart');
|
|
|
|
|
await app.emitAsync('db.init');
|
|
|
|
|
await app.destroy();
|
2021-09-28 00:18:09 +08:00
|
|
|
|
await this.app.destroy();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
} as PluginOptions;
|