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.
|
||||
*/
|
||||
|
||||
import { Gateway } from '@nocobase/server';
|
||||
import { Gateway, runPluginStaticImports } from '@nocobase/server';
|
||||
import { getConfig } from './config';
|
||||
|
||||
getConfig()
|
||||
.then((config) => {
|
||||
return Gateway.getInstance().run({
|
||||
mainAppOptions: config,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
// console.error(e);
|
||||
async function initializeGateway() {
|
||||
await runPluginStaticImports();
|
||||
const config = await getConfig();
|
||||
await Gateway.getInstance().run({
|
||||
mainAppOptions: config,
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
@ -438,6 +444,10 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
getPackageVersion() {
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is deprecated and should not be used.
|
||||
* Use {@link #this.pm.addPreset()} instead.
|
||||
@ -1187,6 +1197,10 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
registerCli(this);
|
||||
|
||||
this._version = new ApplicationVersion(this);
|
||||
|
||||
for (const callback of Application.staticCommands) {
|
||||
callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected createMainDataSource(options: ApplicationOptions) {
|
||||
|
@ -7,13 +7,24 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export * from './app-supervisor';
|
||||
export * from './application';
|
||||
export { Application as default } from './application';
|
||||
export * from './gateway';
|
||||
export * as middlewares from './middlewares';
|
||||
export * from './migration';
|
||||
export * from './plugin';
|
||||
export * from './plugin-manager';
|
||||
export * from './gateway';
|
||||
export * from './app-supervisor';
|
||||
|
||||
export * from './sync-manager';
|
||||
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 { 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 SubAppUpgradeHandler = (mainApp: Application) => Promise<void>;
|
||||
|
||||
@ -61,7 +64,7 @@ const defaultSubAppUpgradeHandle: SubAppUpgradeHandler = async (mainApp: Applica
|
||||
|
||||
const defaultDbCreator = async (app: Application) => {
|
||||
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') {
|
||||
const mysql = require('mysql2/promise');
|
||||
@ -77,7 +80,7 @@ const defaultDbCreator = async (app: Application) => {
|
||||
await connection.end();
|
||||
}
|
||||
|
||||
if (dialect === 'postgres') {
|
||||
if (['postgres', 'kingbase'].includes(dialect)) {
|
||||
const { Client } = require('pg');
|
||||
|
||||
const client = new Client({
|
||||
@ -85,13 +88,17 @@ const defaultDbCreator = async (app: Application) => {
|
||||
port,
|
||||
user: username,
|
||||
password,
|
||||
database: 'postgres',
|
||||
database: dialect,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
|
||||
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) {
|
||||
console.log(e);
|
||||
}
|
||||
@ -109,6 +116,11 @@ const defaultAppOptionsFactory = (appName: string, mainApp: Application) => {
|
||||
const mainStorageDir = path.dirname(mainAppStorage);
|
||||
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 {
|
||||
rawDatabaseOptions.database = appName;
|
||||
}
|
||||
@ -180,13 +192,18 @@ export class PluginMultiAppManagerServer extends Plugin {
|
||||
appOptionsFactory: this.appOptionsFactory,
|
||||
});
|
||||
|
||||
// create database
|
||||
await this.appDbCreator(subApp, {
|
||||
transaction,
|
||||
context: options.context,
|
||||
});
|
||||
const quickstart = async () => {
|
||||
// create database
|
||||
await this.appDbCreator(subApp, {
|
||||
transaction,
|
||||
applicationModel: model,
|
||||
context: options.context,
|
||||
});
|
||||
|
||||
const startPromise = subApp.runCommand('start', '--quickstart');
|
||||
await subApp.runCommand('start', '--quickstart');
|
||||
};
|
||||
|
||||
const startPromise = quickstart();
|
||||
|
||||
if (options?.context?.waitSubAppInstall) {
|
||||
await startPromise;
|
||||
|
@ -67,6 +67,59 @@
|
||||
"@nocobase/server": "1.3.50-beta",
|
||||
"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": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nocobase/nocobase.git",
|
||||
|
Loading…
x
Reference in New Issue
Block a user