mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
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:
32
server/src/api/middleware/AttachCurrentTenantUser.ts
Normal file
32
server/src/api/middleware/AttachCurrentTenantUser.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* Attach user to req.currentUser
|
||||
* @param {Request} req Express req Object
|
||||
* @param {Response} res Express res Object
|
||||
* @param {NextFunction} next Express next Function
|
||||
*/
|
||||
const attachCurrentUser = async (req: Request, res: Response, next: Function) => {
|
||||
const Logger = Container.get('logger');
|
||||
const { systemUserRepository } = Container.get('repositories');
|
||||
|
||||
try {
|
||||
Logger.info('[attach_user_middleware] finding system user by id.');
|
||||
const user = await systemUserRepository.getById(req.token.id);
|
||||
|
||||
if (!user) {
|
||||
Logger.info('[attach_user_middleware] the system user not found.');
|
||||
return res.boom.unauthorized();
|
||||
}
|
||||
// Delete password property from user object.
|
||||
Reflect.deleteProperty(user, 'password');
|
||||
req.user = user;
|
||||
return next();
|
||||
} catch (e) {
|
||||
Logger.error('[attach_user_middleware] error attaching user to req: %o', e);
|
||||
return next(e);
|
||||
}
|
||||
};
|
||||
|
||||
export default attachCurrentUser;
|
||||
14
server/src/api/middleware/ConfiguredMiddleware.ts
Normal file
14
server/src/api/middleware/ConfiguredMiddleware.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { Option } = req.models;
|
||||
const option = await Option.query().where('key', 'app_configured');
|
||||
|
||||
if (option.getMeta('app_configured', false)) {
|
||||
return res.res(400).send({
|
||||
errors: [{ type: 'TENANT.NOT.CONFIGURED', code: 700 }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
27
server/src/api/middleware/EnsureTenantIsInitialized.ts
Normal file
27
server/src/api/middleware/EnsureTenantIsInitialized.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
|
||||
export default (req: Request, res: Response, next: Function) => {
|
||||
const Logger = Container.get('logger');
|
||||
|
||||
if (!req.tenant) {
|
||||
Logger.info('[ensure_tenant_intialized_middleware] no tenant model.');
|
||||
throw new Error('Should load this middleware after `TenancyMiddleware`.');
|
||||
}
|
||||
if (!req.tenant.initializedAt) {
|
||||
Logger.info('[ensure_tenant_initialized_middleware] tenant database not initalized.');
|
||||
return res.boom.badRequest(
|
||||
'Tenant database is not migrated with application schema yut.',
|
||||
{ errors: [{ type: 'TENANT.DATABASE.NOT.INITALIZED' }] },
|
||||
);
|
||||
}
|
||||
if (!req.tenant.seededAt) {
|
||||
Logger.info('[ensure_tenant_initialized_middleware] tenant databae not seeded.');
|
||||
return res.boom.badRequest(
|
||||
'Tenant database is not seeded with initial data yet.',
|
||||
{ errors: [{ type: 'TENANT.DATABASE.NOT.SEED' }] },
|
||||
);
|
||||
}
|
||||
next();
|
||||
};
|
||||
15
server/src/api/middleware/I18nMiddleware.ts
Normal file
15
server/src/api/middleware/I18nMiddleware.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import i18n from 'i18n';
|
||||
|
||||
export default (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
let language = req.headers['accept-language'] || 'en';
|
||||
|
||||
if (req.user && req.user.language) {
|
||||
language = req.user.language;
|
||||
}
|
||||
Logger.info('[i18n_middleware] set locale language to i18n.', { language, user: req.user });
|
||||
i18n.setLocale(req, language);
|
||||
next();
|
||||
};
|
||||
11
server/src/api/middleware/LoggerMiddleware.ts
Normal file
11
server/src/api/middleware/LoggerMiddleware.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NextFunction, Request } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
function loggerMiddleware(request: Request, response: Response, next: NextFunction) {
|
||||
const Logger = Container.get('logger');
|
||||
|
||||
Logger.info(`[routes] ${request.method} ${request.path}`);
|
||||
next();
|
||||
}
|
||||
|
||||
export default loggerMiddleware;
|
||||
31
server/src/api/middleware/SettingsMiddleware.ts
Normal file
31
server/src/api/middleware/SettingsMiddleware.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
import SettingsStore from 'services/Settings/SettingsStore';
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { tenantId } = req.user;
|
||||
const { knex } = req;
|
||||
|
||||
console.log(knex);
|
||||
|
||||
const Logger = Container.get('logger');
|
||||
const tenantContainer = Container.of(`tenant-${tenantId}`);
|
||||
|
||||
if (tenantContainer && !tenantContainer.has('settings')) {
|
||||
Logger.info('[settings_middleware] initialize settings store.');
|
||||
const settings = new SettingsStore(knex);
|
||||
|
||||
Logger.info('[settings_middleware] load settings from storage or cache.');
|
||||
await settings.load();
|
||||
|
||||
tenantContainer.set('settings', settings);
|
||||
}
|
||||
Logger.info('[settings_middleware] get settings instance from container.');
|
||||
const settings = tenantContainer.get('settings');
|
||||
req.settings = settings;
|
||||
|
||||
res.on('finish', async () => {
|
||||
await settings.save();
|
||||
});
|
||||
next();
|
||||
}
|
||||
31
server/src/api/middleware/SubscriptionMiddleware.ts
Normal file
31
server/src/api/middleware/SubscriptionMiddleware.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
export default (subscriptionSlug = 'main') => async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { tenant, tenantId } = req;
|
||||
const Logger = Container.get('logger');
|
||||
const { subscriptionRepository } = Container.get('repositories');
|
||||
|
||||
if (!tenant) {
|
||||
throw new Error('Should load `TenancyMiddlware` before this middleware.');
|
||||
}
|
||||
Logger.info('[subscription_middleware] trying get tenant main subscription.');
|
||||
const subscription = await subscriptionRepository.getBySlugInTenant(subscriptionSlug, tenantId);
|
||||
|
||||
// Validate in case there is no any already subscription.
|
||||
if (!subscription) {
|
||||
Logger.info('[subscription_middleware] tenant has no subscription.', { tenantId });
|
||||
return res.boom.badRequest(
|
||||
'Tenant has no subscription.',
|
||||
{ errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }] }
|
||||
);
|
||||
}
|
||||
// Validate in case the subscription is inactive.
|
||||
else if (subscription.inactive()) {
|
||||
Logger.info('[subscription_middleware] tenant main subscription is expired.', { tenantId });
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }],
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
36
server/src/api/middleware/TenancyMiddleware.ts
Normal file
36
server/src/api/middleware/TenancyMiddleware.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Container } from 'typedi';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import tenantDependencyInjection from 'api/middleware/TenantDependencyInjection'
|
||||
|
||||
export default async (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
const organizationId = req.headers['organization-id'] || req.query.organization;
|
||||
|
||||
const notFoundOrganization = () => {
|
||||
Logger.info('[tenancy_middleware] organization id not found.');
|
||||
return res.boom.unauthorized(
|
||||
'Organization identication not found.',
|
||||
{ errors: [{ type: 'ORGANIZATION.ID.NOT.FOUND', code: 100 }] },
|
||||
);
|
||||
}
|
||||
// In case the given organization not found.
|
||||
if (!organizationId) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
const { tenantRepository } = Container.get('repositories');
|
||||
|
||||
Logger.info('[tenancy_middleware] trying get tenant by org. id from storage.');
|
||||
const tenant = await tenantRepository.getByOrgId(organizationId);
|
||||
|
||||
// When the given organization id not found on the system storage.
|
||||
if (!tenant) {
|
||||
return notFoundOrganization();
|
||||
}
|
||||
// When user tenant not match the given organization id.
|
||||
if (tenant.id !== req.user.tenantId) {
|
||||
Logger.info('[tenancy_middleware] authorized user not match org. tenant.');
|
||||
return res.boom.unauthorized();
|
||||
}
|
||||
tenantDependencyInjection(req, tenant);
|
||||
next();
|
||||
}
|
||||
27
server/src/api/middleware/TenantDependencyInjection.ts
Normal file
27
server/src/api/middleware/TenantDependencyInjection.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Container } from 'typedi';
|
||||
import { ITenant } from 'interfaces';
|
||||
import { Request } from 'express';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import TenantsManagerService from 'services/Tenancy/TenantsManager';
|
||||
|
||||
export default (req: Request, tenant: ITenant) => {
|
||||
const { id: tenantId, organizationId } = tenant;
|
||||
const tenantServices = Container.get(TenancyService);
|
||||
const tenantsManager = Container.get(TenantsManagerService);
|
||||
|
||||
// Initialize the knex instance.
|
||||
tenantsManager.setupKnexInstance(tenant);
|
||||
|
||||
const knexInstance = tenantServices.knex(tenantId);
|
||||
const models = tenantServices.models(tenantId);
|
||||
const repositories = tenantServices.repositories(tenantId)
|
||||
const cacheInstance = tenantServices.cache(tenantId);
|
||||
|
||||
req.knex = knexInstance;
|
||||
req.organizationId = organizationId;
|
||||
req.tenant = tenant;
|
||||
req.tenantId = tenant.id;
|
||||
req.models = models;
|
||||
req.repositories = repositories;
|
||||
req.cache = cacheInstance;
|
||||
}
|
||||
17
server/src/api/middleware/asyncMiddleware.ts
Normal file
17
server/src/api/middleware/asyncMiddleware.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import logger from "src/loaders/logger";
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
export default (
|
||||
fn: (rq: Request, rs: Response, next?: NextFunction) => {}) =>
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
|
||||
Promise.resolve(fn(req, res, next))
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
Logger.error('[async_middleware] error.', { error });
|
||||
next(error);
|
||||
});
|
||||
};
|
||||
32
server/src/api/middleware/jwtAuth.ts
Normal file
32
server/src/api/middleware/jwtAuth.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { Container } from 'typedi';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from 'config';
|
||||
|
||||
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
||||
const Logger = Container.get('logger');
|
||||
const token = req.headers['x-access-token'] || req.query.token;
|
||||
|
||||
const onError = () => {
|
||||
Logger.info('[auth_middleware] jwt verify error.');
|
||||
res.boom.unauthorized();
|
||||
};
|
||||
const onSuccess = (decoded) => {
|
||||
req.token = decoded;
|
||||
Logger.info('[auth_middleware] jwt verify success.');
|
||||
next();
|
||||
};
|
||||
if (!token) { return onError(); }
|
||||
|
||||
const verify = new Promise((resolve, reject) => {
|
||||
jwt.verify(token, config.jwtSecret, async (error, decoded) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(decoded);
|
||||
}
|
||||
});
|
||||
});
|
||||
verify.then(onSuccess).catch(onError);
|
||||
};
|
||||
export default authMiddleware;
|
||||
13
server/src/api/middleware/validateMiddleware.js
Normal file
13
server/src/api/middleware/validateMiddleware.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { validationResult } from 'express-validator';
|
||||
|
||||
export default (req, res, next) => {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error',
|
||||
...validationErrors,
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
Reference in New Issue
Block a user