mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
feat: support restoring applications from backup (#5685)
This commit is contained in:
parent
a63a19c8dd
commit
5518015347
@ -7,15 +7,18 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Gateway } from '@nocobase/server';
|
import { Gateway, runPluginStaticImports } from '@nocobase/server';
|
||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
|
|
||||||
getConfig()
|
async function initializeGateway() {
|
||||||
.then((config) => {
|
await runPluginStaticImports();
|
||||||
return Gateway.getInstance().run({
|
const config = await getConfig();
|
||||||
mainAppOptions: config,
|
await Gateway.getInstance().run({
|
||||||
});
|
mainAppOptions: config,
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
// console.error(e);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeGateway().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
@ -241,6 +241,12 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static staticCommands = [];
|
||||||
|
|
||||||
|
static addCommand(callback: (app: Application) => void) {
|
||||||
|
this.staticCommands.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@ -438,6 +444,10 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
return packageJson.version;
|
return packageJson.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPackageVersion() {
|
||||||
|
return packageJson.version;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is deprecated and should not be used.
|
* This method is deprecated and should not be used.
|
||||||
* Use {@link #this.pm.addPreset()} instead.
|
* Use {@link #this.pm.addPreset()} instead.
|
||||||
@ -1187,6 +1197,10 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
|||||||
registerCli(this);
|
registerCli(this);
|
||||||
|
|
||||||
this._version = new ApplicationVersion(this);
|
this._version = new ApplicationVersion(this);
|
||||||
|
|
||||||
|
for (const callback of Application.staticCommands) {
|
||||||
|
callback(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createMainDataSource(options: ApplicationOptions) {
|
protected createMainDataSource(options: ApplicationOptions) {
|
||||||
|
@ -7,13 +7,24 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from './app-supervisor';
|
||||||
export * from './application';
|
export * from './application';
|
||||||
export { Application as default } from './application';
|
export { Application as default } from './application';
|
||||||
|
export * from './gateway';
|
||||||
export * as middlewares from './middlewares';
|
export * as middlewares from './middlewares';
|
||||||
export * from './migration';
|
export * from './migration';
|
||||||
export * from './plugin';
|
export * from './plugin';
|
||||||
export * from './plugin-manager';
|
export * from './plugin-manager';
|
||||||
export * from './gateway';
|
|
||||||
export * from './app-supervisor';
|
|
||||||
export * from './sync-manager';
|
export * from './sync-manager';
|
||||||
export const OFFICIAL_PLUGIN_PREFIX = '@nocobase/plugin-';
|
export const OFFICIAL_PLUGIN_PREFIX = '@nocobase/plugin-';
|
||||||
|
|
||||||
|
export {
|
||||||
|
appendToBuiltInPlugins,
|
||||||
|
findAllPlugins,
|
||||||
|
findBuiltInPlugins,
|
||||||
|
findLocalPlugins,
|
||||||
|
packageNameTrim,
|
||||||
|
} from './plugin-manager/findPackageNames';
|
||||||
|
|
||||||
|
export { runPluginStaticImports } from './run-plugin-static-imports';
|
||||||
|
143
packages/core/server/src/plugin-manager/findPackageNames.ts
Normal file
143
packages/core/server/src/plugin-manager/findPackageNames.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import fg from 'fast-glob';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import path from 'path';
|
||||||
|
import { PluginManager } from './';
|
||||||
|
|
||||||
|
function splitNames(name: string) {
|
||||||
|
return (name || '').split(',').filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function trim(packageNames: string[]) {
|
||||||
|
const nameOrPkgs = _.uniq(packageNames).filter(Boolean);
|
||||||
|
const names = [];
|
||||||
|
for (const nameOrPkg of nameOrPkgs) {
|
||||||
|
const { name, packageName } = await PluginManager.parseName(nameOrPkg);
|
||||||
|
try {
|
||||||
|
await PluginManager.getPackageJson(packageName);
|
||||||
|
names.push(name);
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
const excludes = [
|
||||||
|
'@nocobase/plugin-audit-logs',
|
||||||
|
'@nocobase/plugin-backup-restore',
|
||||||
|
'@nocobase/plugin-charts',
|
||||||
|
'@nocobase/plugin-disable-pm-add',
|
||||||
|
'@nocobase/plugin-mobile-client',
|
||||||
|
'@nocobase/plugin-mock-collections',
|
||||||
|
'@nocobase/plugin-multi-app-share-collection',
|
||||||
|
'@nocobase/plugin-notifications',
|
||||||
|
'@nocobase/plugin-snapshot-field',
|
||||||
|
'@nocobase/plugin-workflow-test',
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function findPackageNames() {
|
||||||
|
const patterns = [
|
||||||
|
'./packages/plugins/*/package.json',
|
||||||
|
'./packages/plugins/*/*/package.json',
|
||||||
|
'./packages/pro-plugins/*/*/package.json',
|
||||||
|
'./storage/plugins/*/package.json',
|
||||||
|
'./storage/plugins/*/*/package.json',
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
const packageJsonPaths = await fg(patterns, {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
absolute: true,
|
||||||
|
ignore: ['**/external-db-data-source/**'],
|
||||||
|
});
|
||||||
|
const packageNames = await Promise.all(
|
||||||
|
packageJsonPaths.map(async (packageJsonPath) => {
|
||||||
|
const packageJson = await fs.readJson(packageJsonPath);
|
||||||
|
return packageJson.name;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const nocobasePlugins = await findNocobasePlugins();
|
||||||
|
const { APPEND_PRESET_BUILT_IN_PLUGINS = '', APPEND_PRESET_LOCAL_PLUGINS = '' } = process.env;
|
||||||
|
return trim(
|
||||||
|
packageNames
|
||||||
|
.filter((pkg) => pkg && !excludes.includes(pkg))
|
||||||
|
.concat(nocobasePlugins)
|
||||||
|
.concat(splitNames(APPEND_PRESET_BUILT_IN_PLUGINS))
|
||||||
|
.concat(splitNames(APPEND_PRESET_LOCAL_PLUGINS)),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPackageJson() {
|
||||||
|
const packageJson = await fs.readJson(
|
||||||
|
path.resolve(process.env.NODE_MODULES_PATH, '@nocobase/preset-nocobase/package.json'),
|
||||||
|
);
|
||||||
|
return packageJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findNocobasePlugins() {
|
||||||
|
try {
|
||||||
|
const packageJson = await getPackageJson();
|
||||||
|
const pluginNames = Object.keys(packageJson.dependencies).filter((name) => name.startsWith('@nocobase/plugin-'));
|
||||||
|
return trim(pluginNames.filter((pkg) => pkg && !excludes.includes(pkg)));
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findBuiltInPlugins() {
|
||||||
|
const { APPEND_PRESET_BUILT_IN_PLUGINS = '' } = process.env;
|
||||||
|
try {
|
||||||
|
const packageJson = await getPackageJson();
|
||||||
|
return trim(packageJson.builtIn.concat(splitNames(APPEND_PRESET_BUILT_IN_PLUGINS)));
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findLocalPlugins() {
|
||||||
|
const { APPEND_PRESET_LOCAL_PLUGINS = '' } = process.env;
|
||||||
|
const plugins1 = await findNocobasePlugins();
|
||||||
|
const plugins2 = await findPackageNames();
|
||||||
|
const builtInPlugins = await findBuiltInPlugins();
|
||||||
|
const packageJson = await getPackageJson();
|
||||||
|
const items = await trim(
|
||||||
|
_.difference(
|
||||||
|
plugins1.concat(plugins2).concat(splitNames(APPEND_PRESET_LOCAL_PLUGINS)),
|
||||||
|
builtInPlugins.concat(await trim(packageJson.deprecated)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findAllPlugins() {
|
||||||
|
const builtInPlugins = await findBuiltInPlugins();
|
||||||
|
const localPlugins = await findLocalPlugins();
|
||||||
|
return _.uniq(builtInPlugins.concat(localPlugins));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const packageNameTrim = trim;
|
||||||
|
|
||||||
|
export async function appendToBuiltInPlugins(nameOrPkg: string) {
|
||||||
|
const APPEND_PRESET_BUILT_IN_PLUGINS = process.env.APPEND_PRESET_BUILT_IN_PLUGINS || '';
|
||||||
|
const keys = APPEND_PRESET_BUILT_IN_PLUGINS.split(',');
|
||||||
|
const { name, packageName } = await PluginManager.parseName(nameOrPkg);
|
||||||
|
if (keys.includes(packageName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keys.includes(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.env.APPEND_PRESET_BUILT_IN_PLUGINS += ',' + nameOrPkg;
|
||||||
|
}
|
24
packages/core/server/src/run-plugin-static-imports.ts
Normal file
24
packages/core/server/src/run-plugin-static-imports.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
import { findAllPlugins, PluginManager } from '@nocobase/server';
|
||||||
|
|
||||||
|
export async function runPluginStaticImports() {
|
||||||
|
const packages = await findAllPlugins();
|
||||||
|
for (const name of packages) {
|
||||||
|
const { packageName } = await PluginManager.parseName(name);
|
||||||
|
try {
|
||||||
|
const plugin = require(packageName);
|
||||||
|
if (plugin && plugin.staticImport) {
|
||||||
|
await plugin.staticImport();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,10 @@ import lodash from 'lodash';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { ApplicationModel } from '../server';
|
import { ApplicationModel } from '../server';
|
||||||
|
|
||||||
export type AppDbCreator = (app: Application, options?: Transactionable & { context?: any }) => Promise<void>;
|
export type AppDbCreator = (
|
||||||
|
app: Application,
|
||||||
|
options?: Transactionable & { context?: any; applicationModel?: ApplicationModel },
|
||||||
|
) => Promise<void>;
|
||||||
export type AppOptionsFactory = (appName: string, mainApp: Application) => any;
|
export type AppOptionsFactory = (appName: string, mainApp: Application) => any;
|
||||||
export type SubAppUpgradeHandler = (mainApp: Application) => Promise<void>;
|
export type SubAppUpgradeHandler = (mainApp: Application) => Promise<void>;
|
||||||
|
|
||||||
@ -61,7 +64,7 @@ const defaultSubAppUpgradeHandle: SubAppUpgradeHandler = async (mainApp: Applica
|
|||||||
|
|
||||||
const defaultDbCreator = async (app: Application) => {
|
const defaultDbCreator = async (app: Application) => {
|
||||||
const databaseOptions = app.options.database as any;
|
const databaseOptions = app.options.database as any;
|
||||||
const { host, port, username, password, dialect, database } = databaseOptions;
|
const { host, port, username, password, dialect, database, schema } = databaseOptions;
|
||||||
|
|
||||||
if (dialect === 'mysql') {
|
if (dialect === 'mysql') {
|
||||||
const mysql = require('mysql2/promise');
|
const mysql = require('mysql2/promise');
|
||||||
@ -77,7 +80,7 @@ const defaultDbCreator = async (app: Application) => {
|
|||||||
await connection.end();
|
await connection.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialect === 'postgres') {
|
if (['postgres', 'kingbase'].includes(dialect)) {
|
||||||
const { Client } = require('pg');
|
const { Client } = require('pg');
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
@ -85,13 +88,17 @@ const defaultDbCreator = async (app: Application) => {
|
|||||||
port,
|
port,
|
||||||
user: username,
|
user: username,
|
||||||
password,
|
password,
|
||||||
database: 'postgres',
|
database: dialect,
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.query(`CREATE DATABASE "${database}"`);
|
if (process.env.USE_DB_SCHEMA_IN_SUBAPP === 'true') {
|
||||||
|
await client.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
|
||||||
|
} else {
|
||||||
|
await client.query(`CREATE DATABASE "${database}"`);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
@ -109,6 +116,11 @@ const defaultAppOptionsFactory = (appName: string, mainApp: Application) => {
|
|||||||
const mainStorageDir = path.dirname(mainAppStorage);
|
const mainStorageDir = path.dirname(mainAppStorage);
|
||||||
rawDatabaseOptions.storage = path.join(mainStorageDir, `${appName}.sqlite`);
|
rawDatabaseOptions.storage = path.join(mainStorageDir, `${appName}.sqlite`);
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
process.env.USE_DB_SCHEMA_IN_SUBAPP === 'true' &&
|
||||||
|
['postgres', 'kingbase'].includes(rawDatabaseOptions.dialect)
|
||||||
|
) {
|
||||||
|
rawDatabaseOptions.schema = appName;
|
||||||
} else {
|
} else {
|
||||||
rawDatabaseOptions.database = appName;
|
rawDatabaseOptions.database = appName;
|
||||||
}
|
}
|
||||||
@ -180,13 +192,18 @@ export class PluginMultiAppManagerServer extends Plugin {
|
|||||||
appOptionsFactory: this.appOptionsFactory,
|
appOptionsFactory: this.appOptionsFactory,
|
||||||
});
|
});
|
||||||
|
|
||||||
// create database
|
const quickstart = async () => {
|
||||||
await this.appDbCreator(subApp, {
|
// create database
|
||||||
transaction,
|
await this.appDbCreator(subApp, {
|
||||||
context: options.context,
|
transaction,
|
||||||
});
|
applicationModel: model,
|
||||||
|
context: options.context,
|
||||||
|
});
|
||||||
|
|
||||||
const startPromise = subApp.runCommand('start', '--quickstart');
|
await subApp.runCommand('start', '--quickstart');
|
||||||
|
};
|
||||||
|
|
||||||
|
const startPromise = quickstart();
|
||||||
|
|
||||||
if (options?.context?.waitSubAppInstall) {
|
if (options?.context?.waitSubAppInstall) {
|
||||||
await startPromise;
|
await startPromise;
|
||||||
|
@ -67,6 +67,59 @@
|
|||||||
"@nocobase/server": "1.3.50-beta",
|
"@nocobase/server": "1.3.50-beta",
|
||||||
"cronstrue": "^2.11.0"
|
"cronstrue": "^2.11.0"
|
||||||
},
|
},
|
||||||
|
"deprecated": [
|
||||||
|
"@nocobase/plugin-audit-logs",
|
||||||
|
"@nocobase/plugin-charts",
|
||||||
|
"@nocobase/plugin-mobile-client",
|
||||||
|
"@nocobase/plugin-snapshot-field"
|
||||||
|
],
|
||||||
|
"builtIn": [
|
||||||
|
"@nocobase/plugin-acl",
|
||||||
|
"@nocobase/plugin-action-bulk-edit",
|
||||||
|
"@nocobase/plugin-action-bulk-update",
|
||||||
|
"@nocobase/plugin-action-custom-request",
|
||||||
|
"@nocobase/plugin-action-duplicate",
|
||||||
|
"@nocobase/plugin-action-export",
|
||||||
|
"@nocobase/plugin-action-import",
|
||||||
|
"@nocobase/plugin-action-print",
|
||||||
|
"@nocobase/plugin-auth",
|
||||||
|
"@nocobase/plugin-block-iframe",
|
||||||
|
"@nocobase/plugin-block-workbench",
|
||||||
|
"@nocobase/plugin-calendar",
|
||||||
|
"@nocobase/plugin-client",
|
||||||
|
"@nocobase/plugin-collection-sql",
|
||||||
|
"@nocobase/plugin-collection-tree",
|
||||||
|
"@nocobase/plugin-data-source-main",
|
||||||
|
"@nocobase/plugin-data-source-manager",
|
||||||
|
"@nocobase/plugin-data-visualization",
|
||||||
|
"@nocobase/plugin-error-handler",
|
||||||
|
"@nocobase/plugin-field-china-region",
|
||||||
|
"@nocobase/plugin-field-formula",
|
||||||
|
"@nocobase/plugin-field-sequence",
|
||||||
|
"@nocobase/plugin-file-manager",
|
||||||
|
"@nocobase/plugin-gantt",
|
||||||
|
"@nocobase/plugin-kanban",
|
||||||
|
"@nocobase/plugin-logger",
|
||||||
|
"@nocobase/plugin-notification-manager",
|
||||||
|
"@nocobase/plugin-notification-in-app-message",
|
||||||
|
"@nocobase/plugin-mobile",
|
||||||
|
"@nocobase/plugin-system-settings",
|
||||||
|
"@nocobase/plugin-ui-schema-storage",
|
||||||
|
"@nocobase/plugin-user-data-sync",
|
||||||
|
"@nocobase/plugin-users",
|
||||||
|
"@nocobase/plugin-verification",
|
||||||
|
"@nocobase/plugin-workflow",
|
||||||
|
"@nocobase/plugin-workflow-action-trigger",
|
||||||
|
"@nocobase/plugin-workflow-aggregate",
|
||||||
|
"@nocobase/plugin-workflow-delay",
|
||||||
|
"@nocobase/plugin-workflow-dynamic-calculation",
|
||||||
|
"@nocobase/plugin-workflow-loop",
|
||||||
|
"@nocobase/plugin-workflow-manual",
|
||||||
|
"@nocobase/plugin-workflow-parallel",
|
||||||
|
"@nocobase/plugin-workflow-request",
|
||||||
|
"@nocobase/plugin-workflow-sql",
|
||||||
|
"@nocobase/plugin-workflow-notification"
|
||||||
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/nocobase/nocobase.git",
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user