tachybase_todo/packages/plugins/duplicator/src/server/collection-group-manager.ts
ChengLei Shao 6b0ed79f51
feat: duplicator plugin (#1265)
* chore: dump plugin

* chore: rename plugin

* chore: add duplicator into preset

* chore: tmp commit

* feat: restore & dump action

* feat: collection dump & restore

* feat: collection dump & restore

* fix: dump with json type

* fix: dump uischema

* chore: tmp commit

* chore: tmp commit

* feat: restore custom collections

* chore: code

* fix: build

* chore: tmp commit

* fix: pm.generateClientFile

* feat: dump with user plugins

* feat: restore ignore collection

* feat: ignore user with rolesUsers

* chore: client plugins

* refactor: restore insert sql

* chore: code format

* feat: restore with sequelize insert query

* fix: restore json field

* fix: json restore

* refactor: dumper

* refactor: restorer

* chore: dump file name

* chore: dump file name

* chore: dump message

* fix: restore with jsonb fields

* feat: field data writer

* chore: code

* feat: collection group manager

* feat: duplicator client

* feat: duplicator panel

* chore: disable duplicator ui

* feat: dump with inquirer

* chore: dumper

* chore: collection group manager

* feat: restore with inquirer

* chore: comment

* chore: inquirer page size

* feat: warning before restore

* feat: sync postgres sequence id after import collection

* chore: restore checked

* feat: dump with through table

* feat: restore with through table

* feat: restore with sequence field

* chore: graph collection manager collection group

* fix: dump with no column tables

* fix: dump empty table

* fix: force remove workdir

* chore: disable throw error when sync empty table

* feat: support map field restore

* fix: restore from pg dumped file

* fix: dump with logic field

* chore: console.log

* chore: collection group

* chore: handle import collection error

* fix: dump migrations table

* feat: display custom collection title

* fix: restore collection title display

* fix: dump iframe html

* fix: dump with postgres inhertitance

* fix: dump sql

* chore: export snapshot field

* fix: import with sequences

* fix: import sequences

* fix: storage

Co-authored-by: chenos <chenlinxh@gmail.com>
2023-01-08 12:45:02 +08:00

257 lines
6.8 KiB
TypeScript

import { Application } from '@nocobase/server';
import lodash from 'lodash';
import { Restorer } from './restorer';
interface CollectionGroup {
pluginName: string;
collections: string[];
function: string;
dumpable: 'required' | 'optional' | 'skip';
delayRestore?: any;
}
export class CollectionGroupManager {
static collectionGroups: CollectionGroup[] = [];
static registerCollectionGroup(collectionGroup: CollectionGroup) {
this.collectionGroups.push(collectionGroup);
}
static getGroupsCollections(groups: string[] | CollectionGroup[]) {
if (groups.length == 0) {
return [];
}
if (lodash.isPlainObject(groups[0])) {
groups = (groups as CollectionGroup[]).map(
(collectionGroup) => `${collectionGroup.pluginName}.${collectionGroup.function}`,
);
}
return this.collectionGroups
.filter((collectionGroup) => {
const groupKey = `${collectionGroup.pluginName}.${collectionGroup.function}`;
return (groups as string[]).includes(groupKey);
})
.map((collectionGroup) => collectionGroup.collections)
.flat();
}
static classifyCollectionGroups(collectionGroups: CollectionGroup[]) {
const requiredGroups = collectionGroups.filter((collectionGroup) => collectionGroup.dumpable === 'required');
const optionalGroups = collectionGroups.filter((collectionGroup) => collectionGroup.dumpable === 'optional');
return {
requiredGroups,
optionalGroups,
};
}
static getDelayRestoreCollectionGroups() {
return this.collectionGroups.filter((collectionGroup) => collectionGroup.delayRestore);
}
}
CollectionGroupManager.registerCollectionGroup({
pluginName: 'core',
function: 'migration',
collections: ['migrations'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'multi-app-manager',
function: 'multi apps',
collections: ['applications'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'collection-manager',
function: 'collections',
collections: ['collections', 'fields'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'ui-schema-storage',
function: 'uiSchemas',
collections: ['uiSchemas', 'uiSchemaServerHooks', 'uiSchemaTemplates', 'uiSchemaTreePath'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'ui-routes-storage',
function: 'uiRoutes',
collections: ['uiRoutes'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'acl',
function: 'acl',
collections: ['roles', 'rolesResources', 'rolesResourcesActions', 'rolesResourcesScopes', 'rolesUischemas'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'workflow',
function: 'workflowConfig',
collections: ['workflows', 'flow_nodes'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'snapshot-field',
function: 'snapshot-field',
collections: ['collectionsHistory', 'fieldsHistory'],
dumpable: 'required',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'workflow',
function: 'executionLogs',
collections: ['executions', 'jobs'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'sequence-field',
function: 'sequences',
collections: ['sequences'],
dumpable: 'required',
async delayRestore(restorer: Restorer) {
const app = restorer.app;
const importedCollections = restorer.importedCollections;
const sequenceFields = importedCollections
.map((collection) =>
[...app.db.getCollection(collection).fields.values()].filter((field) => field.type === 'sequence'),
)
.flat()
.filter(Boolean);
// a single sequence field refers to a single row in sequences table
const sequencesAttributes = sequenceFields
.map((field) => {
const patterns = field.get('patterns').filter((pattern) => pattern.type === 'integer');
return patterns.map((pattern) => {
return {
collection: field.collection.name,
field: field.name,
key: pattern.options.key,
};
});
})
.flat();
if (sequencesAttributes.length > 0) {
await app.db.getRepository('sequences').destroy({
filter: {
$or: sequencesAttributes,
},
});
}
await restorer.importCollection({
name: 'sequences',
clear: false,
rowCondition(row) {
const results = sequencesAttributes.some((attributes) => {
return (
row.collection === attributes.collection && row.field === attributes.field && row.key === attributes.key
);
});
return results;
},
});
},
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'users',
function: 'users',
collections: ['users', 'rolesUsers'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'file-manager',
function: 'storageSetting',
collections: ['storages'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'file-manager',
function: 'attachmentRecords',
collections: ['attachments'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'system-settings',
function: 'systemSettings',
collections: ['systemSettings'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'verification',
function: 'verificationProviders',
collections: ['verifications_providers'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'verification',
function: 'verificationData',
collections: ['verifications'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'oidc',
function: 'oidcProviders',
collections: ['oidcProviders'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'saml',
function: 'samlProviders',
collections: ['samlProviders'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'map',
function: 'mapConfiguration',
collections: ['mapConfiguration'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'audit-logs',
function: 'auditLogs',
collections: ['auditLogs', 'auditChanges'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'graph-collection-manager',
function: 'graphCollectionPositions',
collections: ['graphPositions'],
dumpable: 'optional',
});
CollectionGroupManager.registerCollectionGroup({
pluginName: 'iframe-block',
function: 'iframe html storage',
collections: ['iframeHtml'],
dumpable: 'optional',
});