tachybase_todo/packages/plugin-multi-apps/src/server.ts

272 lines
7.1 KiB
TypeScript
Raw Normal View History

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';
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 = [
'@nocobase/plugin-ui-router',
'@nocobase/plugin-ui-schema',
'@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'),
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-30 09:44:23 +08:00
console.log('..........................start........................');
2021-09-28 00:18:09 +08:00
// 完全隔离的做法
const app = apps.get(appName) as Application;
2021-09-30 15:16:41 +08:00
// @ts-ignore
console.log(app.db.options);
const bodyParser = async (ctx2, next) => {
// @ts-ignore
2021-09-30 15:16:41 +08:00
// ctx2.request.body = ctx.request.body || {};
await next();
};
app.middleware.unshift(bodyParser);
2021-09-28 00:18:09 +08:00
const handleRequest = app.callback();
await handleRequest(ctx.req, ctx.res);
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 () => {
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,
},
});
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
.command('app:db:sync')
2021-09-28 00:18:09 +08:00
.argument('<appName>')
.action(async (appName) => {
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
},
});
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;