diff --git a/packages/server/package.json b/packages/server/package.json index 07329cb60..817247e8c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -69,9 +69,8 @@ "is-my-json-valid": "^2.20.5", "js-money": "^0.6.3", "jsonwebtoken": "^8.5.1", - "knex": "^0.95.15", + "knex": "^3.1.0", "knex-cleaner": "^1.3.0", - "knex-db-manager": "^0.6.1", "libphonenumber-js": "^1.9.6", "lodash": "^4.17.15", "lru-cache": "^6.0.0", diff --git a/packages/server/src/config/index.ts b/packages/server/src/config/index.ts index a7ef51e93..128da484f 100644 --- a/packages/server/src/config/index.ts +++ b/packages/server/src/config/index.ts @@ -32,7 +32,7 @@ module.exports = { */ tenant: { db_client: process.env.TENANT_DB_CLIENT || process.env.DB_CLIENT || 'mysql', - db_name_prefix: process.env.TENANT_DB_NAME_PERFIX, + db_name_prefix: process.env.TENANT_DB_NAME_PERFIX || 'bigcapital_tenant_', db_host: process.env.TENANT_DB_HOST || process.env.DB_HOST, db_user: process.env.TENANT_DB_USER || process.env.DB_USER, db_password: process.env.TENANT_DB_PASSWORD || process.env.DB_PASSWORD, diff --git a/packages/server/src/interfaces/Tenancy.ts b/packages/server/src/interfaces/Tenancy.ts index 423812b55..3c8ae2f71 100644 --- a/packages/server/src/interfaces/Tenancy.ts +++ b/packages/server/src/interfaces/Tenancy.ts @@ -51,5 +51,4 @@ export interface ISystemService { cache(); repositories(); knex(); - dbManager(); } \ No newline at end of file diff --git a/packages/server/src/jobs/ComputeItemCost.ts b/packages/server/src/jobs/ComputeItemCost.ts index 07479258d..af6a19cbe 100644 --- a/packages/server/src/jobs/ComputeItemCost.ts +++ b/packages/server/src/jobs/ComputeItemCost.ts @@ -21,7 +21,7 @@ export default class ComputeItemCostJob { agenda.define( 'compute-item-cost', - { priority: 'high', concurrency: 1 }, + { priority: 'high', concurrency: 20 }, this.handler.bind(this) ); this.agenda.on('start:compute-item-cost', this.onJobStart.bind(this)); diff --git a/packages/server/src/jobs/OrganizationSetup.ts b/packages/server/src/jobs/OrganizationSetup.ts index 1945b6e2b..0d94d69e7 100644 --- a/packages/server/src/jobs/OrganizationSetup.ts +++ b/packages/server/src/jobs/OrganizationSetup.ts @@ -8,7 +8,7 @@ export default class OrganizationSetupJob { constructor(agenda) { agenda.define( 'organization-setup', - { priority: 'high', concurrency: 1 }, + { priority: 'high', concurrency: 20 }, this.handler ); } diff --git a/packages/server/src/jobs/WriteInvoicesJEntries.ts b/packages/server/src/jobs/WriteInvoicesJEntries.ts index d7a57d63a..738c47eee 100644 --- a/packages/server/src/jobs/WriteInvoicesJEntries.ts +++ b/packages/server/src/jobs/WriteInvoicesJEntries.ts @@ -15,7 +15,7 @@ export default class WriteInvoicesJournalEntries { agenda.define( eventName, - { priority: 'normal', concurrency: 1 }, + { priority: 'normal', concurrency: 20 }, this.handler.bind(this) ); agenda.on(`complete:${eventName}`, this.onJobCompleted.bind(this)); diff --git a/packages/server/src/loaders/dbManager.ts b/packages/server/src/loaders/dbManager.ts deleted file mode 100644 index 6aaf31ed2..000000000 --- a/packages/server/src/loaders/dbManager.ts +++ /dev/null @@ -1,7 +0,0 @@ -import knexManager from 'knex-db-manager'; -import { systemKnexConfig, systemDbManager } from 'config/knexConfig'; - -export default () => knexManager.databaseManagerFactory({ - knex: systemKnexConfig, - dbManager: systemDbManager, -}); \ No newline at end of file diff --git a/packages/server/src/loaders/dependencyInjector.ts b/packages/server/src/loaders/dependencyInjector.ts index 2c42690dc..7befb88c5 100644 --- a/packages/server/src/loaders/dependencyInjector.ts +++ b/packages/server/src/loaders/dependencyInjector.ts @@ -3,7 +3,6 @@ import LoggerInstance from '@/loaders/logger'; import agendaFactory from '@/loaders/agenda'; import SmsClientLoader from '@/loaders/smsClient'; import mailInstance from '@/loaders/mail'; -import dbManagerFactory from '@/loaders/dbManager'; import i18n from '@/loaders/i18n'; import repositoriesLoader from '@/loaders/systemRepositories'; import Cache from '@/services/Cache'; @@ -16,7 +15,6 @@ export default ({ mongoConnection, knex }) => { try { const agendaInstance = agendaFactory({ mongoConnection }); const smsClientInstance = SmsClientLoader(config.easySMSGateway.api_key); - const dbManager = dbManagerFactory(knex); const cacheInstance = new Cache(); Container.set('logger', LoggerInstance); @@ -24,7 +22,6 @@ export default ({ mongoConnection, knex }) => { Container.set('SMSClient', smsClientInstance); Container.set('mail', mailInstance); - Container.set('dbManager', dbManager); LoggerInstance.info( '[DI] Database manager has been injected into container.' ); diff --git a/packages/server/src/services/Tenancy/SystemService.ts b/packages/server/src/services/Tenancy/SystemService.ts index dac5a4320..d03aec13c 100644 --- a/packages/server/src/services/Tenancy/SystemService.ts +++ b/packages/server/src/services/Tenancy/SystemService.ts @@ -18,8 +18,4 @@ export default class HasSystemService implements SystemService { cache() { return this.container('cache'); } - - dbManager() { - return this.container('dbManager'); - } } diff --git a/packages/server/src/services/Tenancy/TenantDBManager.ts b/packages/server/src/services/Tenancy/TenantDBManager.ts index 981256f9c..a234e5c3b 100644 --- a/packages/server/src/services/Tenancy/TenantDBManager.ts +++ b/packages/server/src/services/Tenancy/TenantDBManager.ts @@ -1,18 +1,16 @@ import { Container } from 'typedi'; -import Knex from 'knex'; +import { Knex, knex } from 'knex'; import { knexSnakeCaseMappers } from 'objection'; import { tenantKnexConfig, tenantSeedConfig } from '@/config/knexConfig'; import config from '@/config'; import { ITenant, ITenantDBManager } from '@/interfaces'; import SystemService from '@/services/Tenancy/SystemService'; import { TenantDBAlreadyExists } from '@/exceptions'; +import { sanitizeDatabaseName } from '@/utils/sanitizers'; export default class TenantDBManager implements ITenantDBManager { static knexCache: { [key: string]: Knex } = {}; - // System database manager. - dbManager: any; - // System knex instance. sysKnex: Knex; @@ -23,7 +21,6 @@ export default class TenantDBManager implements ITenantDBManager { constructor() { const systemService = Container.get(SystemService); - this.dbManager = systemService.dbManager(); this.sysKnex = systemService.knex(); } @@ -32,7 +29,9 @@ export default class TenantDBManager implements ITenantDBManager { * @return {string} */ private getDatabaseName(tenant: ITenant) { - return `${config.tenant.db_name_prefix}${tenant.organizationId}`; + return sanitizeDatabaseName( + `${config.tenant.db_name_prefix}${tenant.organizationId}` + ); } /** @@ -59,7 +58,9 @@ export default class TenantDBManager implements ITenantDBManager { await this.throwErrorIfTenantDBExists(tenant); const databaseName = this.getDatabaseName(tenant); - await this.dbManager.createDb(databaseName); + await this.sysKnex.raw( + `CREATE DATABASE ${databaseName} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci` + ); } /** @@ -72,7 +73,6 @@ export default class TenantDBManager implements ITenantDBManager { if (!isExists) { return; } - await this.dropDatabase(tenant); } @@ -83,7 +83,7 @@ export default class TenantDBManager implements ITenantDBManager { public async dropDatabase(tenant: ITenant) { const databaseName = this.getDatabaseName(tenant); - await this.dbManager.dropDb(databaseName); + await this.sysKnex.raw(`DROP DATABASE IF EXISTS ${databaseName}`); } /** @@ -118,7 +118,7 @@ export default class TenantDBManager implements ITenantDBManager { let knexInstance = TenantDBManager.knexCache[key]; if (!knexInstance) { - knexInstance = Knex({ + knexInstance = knex({ ...tenantKnexConfig(tenant), ...knexSnakeCaseMappers({ upperCase: true }), }); diff --git a/packages/server/src/utils/sanitizers.ts b/packages/server/src/utils/sanitizers.ts new file mode 100644 index 000000000..4ca07a5d8 --- /dev/null +++ b/packages/server/src/utils/sanitizers.ts @@ -0,0 +1,4 @@ +export function sanitizeDatabaseName(dbName: string) { + // Replace any character that is not alphanumeric or an underscore with an underscore + return dbName.replace(/[^a-zA-Z0-9_]/g, ''); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bc9ae24a..12042e0a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,14 +183,11 @@ importers: specifier: ^8.5.1 version: 8.5.1 knex: - specifier: ^0.95.15 - version: 0.95.15(mysql2@1.7.0)(mysql@2.18.1) + specifier: ^3.1.0 + version: 3.1.0(mysql2@1.7.0)(mysql@2.18.1) knex-cleaner: specifier: ^1.3.0 version: 1.3.1 - knex-db-manager: - specifier: ^0.6.1 - version: 0.6.2(knex@0.95.15) libphonenumber-js: specifier: ^1.9.6 version: 1.11.1 @@ -256,7 +253,7 @@ importers: version: 2.2.0 objection: specifier: ^3.0.0 - version: 3.1.4(knex@0.95.15) + version: 3.1.4(knex@3.1.0) objection-filter: specifier: ^4.0.1 version: 4.4.0(objection@3.1.4) @@ -9507,6 +9504,10 @@ packages: resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==} dev: false + /colorette@2.0.19: + resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + dev: false + /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -9531,6 +9532,11 @@ packages: dependencies: delayed-stream: 1.0.0 + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: false + /commander@2.15.1: resolution: {integrity: sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==} dev: true @@ -13154,7 +13160,6 @@ packages: /getopts@2.3.0: resolution: {integrity: sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==} - dev: true /getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} @@ -16477,19 +16482,6 @@ packages: lodash: 4.17.21 dev: false - /knex-db-manager@0.6.2(knex@0.95.15): - resolution: {integrity: sha512-IQ7kmjzT1/V129QasorbVAksY5QOYhtf1i9ExTaOrMa2k8aiViv/IR4n33aBXRK2JwLeHNZMVOJOvM2iP2BYQg==} - engines: {node: '>=8.0.0'} - deprecated: 'Tests for #88 are failing' - peerDependencies: - knex: 0.x - dependencies: - bluebird: 3.7.2 - glob: 7.2.3 - knex: 0.95.15(mysql2@1.7.0)(mysql@2.18.1) - lodash: 4.17.21 - dev: false - /knex-factory@0.0.6: resolution: {integrity: sha512-K8VAu9E1IZzmJ1XEuH1sGm9SP4vqQgRsE4yRfs7bSMVuVyI8yqbiv3I1J+HvtA//c6yp6EYikSpvtKfR0mi/Og==} dependencies: @@ -16540,6 +16532,54 @@ packages: - supports-color dev: false + /knex@3.1.0(mysql2@1.7.0)(mysql@2.18.1): + resolution: {integrity: sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + better-sqlite3: '*' + mysql: '*' + mysql2: '*' + pg: '*' + pg-native: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql: + optional: true + mysql2: + optional: true + pg: + optional: true + pg-native: + optional: true + sqlite3: + optional: true + tedious: + optional: true + dependencies: + colorette: 2.0.19 + commander: 10.0.1 + debug: 4.3.4(supports-color@5.5.0) + escalade: 3.1.2 + esm: 3.2.25 + get-package-type: 0.1.0 + getopts: 2.3.0 + interpret: 2.2.0 + lodash: 4.17.21 + mysql: 2.18.1 + mysql2: 1.7.0 + pg-connection-string: 2.6.2 + rechoir: 0.8.0 + resolve-from: 5.0.0 + tarn: 3.0.2 + tildify: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} dev: false @@ -18788,7 +18828,7 @@ packages: dependencies: debug: 4.3.4(supports-color@5.5.0) lodash: 4.17.21 - objection: 3.1.4(knex@0.95.15) + objection: 3.1.4(knex@3.1.0) transitivePeerDependencies: - supports-color dev: false @@ -18798,7 +18838,7 @@ packages: peerDependencies: objection: '>= 0.8' dependencies: - objection: 3.1.4(knex@0.95.15) + objection: 3.1.4(knex@3.1.0) dev: false /objection-unique@1.2.2(objection@3.1.4): @@ -18810,10 +18850,10 @@ packages: lodash.castarray: 4.4.0 lodash.compact: 3.0.1 lodash.isempty: 4.4.0 - objection: 3.1.4(knex@0.95.15) + objection: 3.1.4(knex@3.1.0) dev: false - /objection@3.1.4(knex@0.95.15): + /objection@3.1.4(knex@3.1.0): resolution: {integrity: sha512-BI1YQ18JicfoODgCdKxmw4W8f24/e9hCEQpOTux0xmyd8hOidOzDd1WopOMxqxo7FA+Jfw8XTfZIUaqDnS7r0g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -18822,7 +18862,7 @@ packages: ajv: 8.13.0 ajv-formats: 2.1.1(ajv@8.13.0) db-errors: 0.2.3 - knex: 0.95.15(mysql2@1.7.0)(mysql@2.18.1) + knex: 3.1.0(mysql2@1.7.0)(mysql@2.18.1) dev: false /oblivious-set@1.0.0: @@ -19438,6 +19478,10 @@ packages: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} dev: false + /pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + dev: false + /picocolors@0.2.1: resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} dev: false @@ -22065,6 +22109,13 @@ packages: dependencies: resolve: 1.22.8 + /rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + dependencies: + resolve: 1.22.8 + dev: false + /recursive-readdir@2.2.2: resolution: {integrity: sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==} engines: {node: '>=0.10.0'}