feat: remove path alias.

feat: remove Webpack and depend on nodemon.
feat: refactoring expenses.
feat: optimize system users with caching.
feat: architecture tenant optimize.
This commit is contained in:
Ahmed Bouhuolia
2020-09-15 00:51:39 +02:00
parent ad00f140d1
commit a22c8395f3
293 changed files with 3391 additions and 1637 deletions

View File

@@ -1,54 +1,94 @@
import { Service, Inject, Container } from 'typedi';
import { Tenant, SystemUser } from '@/system/models';
import TenantsManager from '@/system/TenantsManager';
import { ServiceError } from '@/exceptions';
import { ITenant } from '@/interfaces';
import { Service, Inject } from 'typedi';
import { ServiceError } from 'exceptions';
import { ITenant } from 'interfaces';
import {
EventDispatcher,
EventDispatcherInterface,
} from '@/decorators/eventDispatcher';
import events from '@/subscribers/events';
} from 'decorators/eventDispatcher';
import events from 'subscribers/events';
import {
TenantAlreadyInitialized,
TenantAlreadySeeded,
TenantDatabaseNotBuilt
} from 'exceptions';
import TenantsManager from 'services/Tenancy/TenantsManager';
const ERRORS = {
TENANT_NOT_FOUND: 'tenant_not_found',
TENANT_ALREADY_INITIALIZED: 'tenant_already_initialized',
TENANT_ALREADY_SEEDED: 'tenant_already_seeded',
TENANT_DB_NOT_BUILT: 'tenant_db_not_built',
};
@Service()
export default class OrganizationService {
@EventDispatcher()
eventDispatcher: EventDispatcherInterface;
@Inject()
tenantsManager: TenantsManager;
@Inject('dbManager')
dbManager: any;
@Inject('logger')
logger: any;
@Inject('repositories')
sysRepositories: any;
@Inject()
tenantsManager: TenantsManager;
/**
* Builds the database schema and seed data of the given organization id.
* @param {srting} organizationId
* @param {srting} organizationId
* @return {Promise<void>}
*/
async build(organizationId: string): Promise<void> {
const tenant = await Tenant.query().findOne('organization_id', organizationId);
this.throwIfTenantNotExists(tenant);
const tenant = await this.getTenantByOrgIdOrThrowError(organizationId);
this.throwIfTenantInitizalized(tenant);
this.logger.info('[tenant_db_build] tenant DB creating.', { tenant });
await this.dbManager.createDb(`bigcapital_tenant_${tenant.organizationId}`);
const tenantDb = this.tenantsManager.knexInstance(tenant.organizationId);
const tenantHasDB = await this.tenantsManager.hasDatabase(tenant);
this.logger.info('[tenant_db_build] tenant DB migrating to latest version.', { tenant });
await tenantDb.migrate.latest();
try {
if (!tenantHasDB) {
this.logger.info('[organization] trying to create tenant database.', { organizationId });
await this.tenantsManager.createDatabase(tenant);
}
this.logger.info('[organization] trying to migrate tenant database.', { organizationId });
await this.tenantsManager.migrateTenant(tenant);
this.logger.info('[tenant_db_build] mark tenant as initialized.', { tenant });
await tenant.$query().update({ initialized: true });
// Throws `onOrganizationBuild` event.
this.eventDispatcher.dispatch(events.organization.build, { tenant });
// Retrieve the tenant system user.
const user = await SystemUser.query().findOne('tenant_id', tenant.id);
} catch (error) {
if (error instanceof TenantAlreadyInitialized) {
throw new ServiceError(ERRORS.TENANT_ALREADY_INITIALIZED);
} else {
throw error;
}
}
}
// Throws `onOrganizationBuild` event.
this.eventDispatcher.dispatch(events.organization.build, { tenant, user });
/**
* Seeds initial core data to the given organization tenant.
* @param {number} organizationId
* @return {Promise<void>}
*/
async seed(organizationId: string): Promise<void> {
const tenant = await this.getTenantByOrgIdOrThrowError(organizationId);
this.throwIfTenantSeeded(tenant);
try {
this.logger.info('[organization] trying to seed tenant database.', { organizationId });
await this.tenantsManager.seedTenant(tenant);
// Throws `onOrganizationBuild` event.
this.eventDispatcher.dispatch(events.organization.seeded, { tenant });
} catch (error) {
if (error instanceof TenantAlreadySeeded) {
throw new ServiceError(ERRORS.TENANT_ALREADY_SEEDED);
} else if (error instanceof TenantDatabaseNotBuilt) {
throw new ServiceError(ERRORS.TENANT_DB_NOT_BUILT);
} else {
throw error;
}
}
}
/**
@@ -58,7 +98,7 @@ export default class OrganizationService {
private throwIfTenantNotExists(tenant: ITenant) {
if (!tenant) {
this.logger.info('[tenant_db_build] organization id not found.');
throw new ServiceError('tenant_not_found');
throw new ServiceError(ERRORS.TENANT_NOT_FOUND);
}
}
@@ -67,8 +107,32 @@ export default class OrganizationService {
* @param {ITenant} tenant
*/
private throwIfTenantInitizalized(tenant: ITenant) {
if (tenant.initialized) {
throw new ServiceError('tenant_initialized');
if (tenant.initializedAt) {
throw new ServiceError(ERRORS.TENANT_ALREADY_INITIALIZED);
}
}
/**
* Throws service if the tenant already seeded.
* @param {ITenant} tenant
*/
private throwIfTenantSeeded(tenant: ITenant) {
if (tenant.seededAt) {
throw new ServiceError(ERRORS.TENANT_ALREADY_SEEDED);
}
}
/**
* Retrieve tenant model by the given organization id or throw not found
* error if the tenant not exists on the storage.
* @param {string} organizationId
* @return {ITenant}
*/
private async getTenantByOrgIdOrThrowError(organizationId: string) {
const { tenantRepository } = this.sysRepositories;
const tenant = await tenantRepository.getByOrgId(organizationId);
this.throwIfTenantNotExists(tenant);
return tenant;
}
}