tachybase_todo/packages/plugins/duplicator/src/server/app-migrator.ts
ChengLei Shao 0832a56868
feat: multiple apps (#1540)
* chore: skip yarn install in pm command

* feat: dump sub app by sub app name

* feat: dump & restore by sub app

* chore: enable application name to edit

* chore: field belongsTo uiSchema

* test: drop schema

* feat: uiSchema migrator

* fix: test

* fix: remove uiSchema

* fix: rerun migration

* chore: migrate fieldsHistory uiSchema

* fix: set uiSchema options

* chore: transaction params

* fix: sql error in mysql

* fix: sql compatibility

* feat: collection group api

* chore: restore & dump action template

* chore: tmp commit

* chore: collectionGroupAction

* feat: dumpableCollection api

* refactor: dump command

* fix: remove uiSchemaUid

* chore: get uiSchemaUid from tmp field

* feat: return dumped file url in dumper.dump

* feat: dump api

* refactor: collection groyoup

* chore: comment

* feat: restore command force option

* feat: dump with collection groups

* refactor: restore command

* feat: restore http api

* fix: test

* fix: test

* fix: restore test

* chore: volta pin

* fix: sub app load collection options

* fix: stop sub app

* feat: add stopped status to application to prevent duplicate application stop

* chore: tmp commit

* test: upgrade

* feat: pass upgrade event to sub app

* fix: app manager client

* fix: remove stopped status

* fix: emit beforeStop event

* feat: support dump & restore subApp through api

* chore: dumpable collections api

* refactor: getTableNameWithSchema

* fix: schema name

* feat:  cname

* refactor: collection 同步实现方式

* refactor: move collection group manager to database

* fix: test

* fix: remove uiSchema

* fix: uiSchema

* fix: remove settings

* chore: plugin enable & disable event

* feat: modal warning

* fix: users_jobs namespace

* fix: rolesUischemas namespace

* fix: am snippet

* feat: beforeSubAppInstall event

* fix: improve NOCOBASE_LOCALE_KEY & NOCOBASE_ROLE_KEY

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
2023-03-10 19:16:00 +08:00

95 lines
2.7 KiB
TypeScript

import { Application } from '@nocobase/server';
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
import crypto from 'crypto';
import EventEmitter from 'events';
import fsPromises from 'fs/promises';
import lodash from 'lodash';
import * as os from 'os';
import path from 'path';
import { CollectionGroupManager } from './collection-group-manager';
export type AppMigratorOptions = {
workDir?: string;
};
abstract class AppMigrator extends EventEmitter {
protected workDir: string;
public app: Application;
abstract direction: 'restore' | 'dump';
declare emitAsync: (event: string | symbol, ...args: any[]) => Promise<boolean>;
constructor(app, options?: AppMigratorOptions) {
super();
this.app = app;
this.workDir = options?.workDir || this.tmpDir();
}
tmpDir() {
return path.resolve(os.tmpdir(), `nocobase-${crypto.randomUUID()}`);
}
getPluginCollections(plugins: string | string[]) {
return lodash
.castArray(plugins)
.map((pluginName) => {
return this.app.db.importedFrom.get(pluginName) || [];
})
.flat();
}
async getAppPlugins() {
const plugins = await this.app.db.getCollection('applicationPlugins').repository.find();
return lodash.uniq(['core', ...this.app.pm.plugins.keys(), ...plugins.map((plugin) => plugin.get('name'))]);
}
async getAppPluginCollectionGroups() {
const plugins = await this.getAppPlugins();
return CollectionGroupManager.collectionGroups.filter((collectionGroup) =>
plugins.includes(collectionGroup.namespace),
);
}
async getCustomCollections() {
const collections = await this.app.db.getCollection('collections').repository.find();
return collections.filter((collection) => !collection.get('isThrough')).map((collection) => collection.get('name'));
}
async rmDir(dir: string) {
await fsPromises.rm(dir, { recursive: true, force: true });
}
async clearWorkDir() {
await this.rmDir(this.workDir);
}
findThroughCollections(collections: string[]) {
return [
...new Set(
collections
.map((collectionName) => this.app.db.getCollection(collectionName))
.map((collection) =>
[...collection.fields.values()].filter((field) => field.through).map((field) => field.through),
)
.flat(),
),
];
}
findSequenceFields(collections: string[]) {
return [
...new Set(
collections
.map((collectionName) => this.app.db.getCollection(collectionName))
.map((collection) => [...collection.fields.values()].filter((field) => field.type === 'sequence'))
.flat(),
),
];
}
}
applyMixins(AppMigrator, [AsyncEmitter]);
export { AppMigrator };