Skip to content

Commit

Permalink
module9-task1: correct static and upload files, add cors middleware a…
Browse files Browse the repository at this point in the history
…nd fix exception filters
  • Loading branch information
JIlyaS committed Nov 5, 2024
1 parent d863696 commit 2b0df5d
Show file tree
Hide file tree
Showing 45 changed files with 330 additions and 82 deletions.
4 changes: 3 additions & 1 deletion .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ DB_USER=admin
DB_PASSWORD=test
DB_PORT=27017
DB_NAME=six-cities
UPLOAD_DIRECTORY=/Users/
UPLOAD_DIRECTORY=upload
JWT_SECRET=secret
JWT_EXPIRED=2d
HOST=localhost
STATIC_DIRECTORY_PATH=static
2 changes: 2 additions & 0 deletions Workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,7 @@ DB_NAME=collection_name - Имя базы данных
UPLOAD_DIRECTORY=directory_path - Путь до хранения файлов в корне сервера
JWT_SECRET=secret - Секрет для аутентификации пользователя по JWT токену
JWT_EXPIRED=2d - Время жизни токена аутенификации
HOST=localhost - Имя хоста, на котором запущен сервис
STATIC_DIRECTORY_PATH=static_directory_path - Название директории для статичных файлов


File renamed without changes
23 changes: 21 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"devDependencies": {
"@types/convict": "6.1.6",
"@types/convict-format-with-validator": "6.0.5",
"@types/cors": "2.8.17",
"@types/express": "4.17.21",
"@types/mime-types": "2.1.4",
"@types/multer": "1.4.11",
Expand Down Expand Up @@ -51,6 +52,7 @@
"class-validator": "0.14.1",
"convict": "6.2.4",
"convict-format-with-validator": "6.2.0",
"cors": "2.8.5",
"dayjs": "^1.11.10",
"dotenv": "16.4.5",
"express": "4.19.2",
Expand Down
1 change: 1 addition & 0 deletions src/rest/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { RestApplication } from './rest.application.js';
export { STATIC_FILES_ROUTE, STATIC_UPLOAD_ROUTE } from './rest.constant.js';
17 changes: 14 additions & 3 deletions src/rest/rest.application.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { inject, injectable } from 'inversify';
import express, { Express } from 'express';
import cors from 'cors';

import { Logger } from '../shared/libs/logger/index.js';
import { Config, IRestSchema } from '../shared/libs/config/index.js';
import { COMPONENT } from '../shared/constants/index.js';
import { DatabaseClient } from '../shared/libs/database-client/index.js';
import { getMongoURI } from '../shared/helpers/index.js';
import { getFullServerPath, getMongoURI } from '../shared/helpers/index.js';
import { Controller, ExceptionFilter } from '../shared/libs/rest/index.js';
import { ParseTokenMiddleware } from '../shared/libs/rest/middleware/parse-token.middleware.js';
import { STATIC_FILES_ROUTE, STATIC_UPLOAD_ROUTE } from './rest.constant.js';

@injectable()
export class RestApplication {
Expand All @@ -23,6 +25,8 @@ export class RestApplication {
@inject(COMPONENT.COMMENT_CONTROLLER) private readonly commentController: Controller,
@inject(COMPONENT.EXCEPTION_FILTER) private readonly appExceptionFilter: ExceptionFilter,
@inject(COMPONENT.AUTH_EXCEPTION_FILTER) private readonly authExceptionFilter: ExceptionFilter,
@inject(COMPONENT.HTTP_EXCEPTION_FILTER) private readonly httpExceptionFilter: ExceptionFilter,
@inject(COMPONENT.VALIDATION_EXCEPTION_FILTER) private readonly validationExceptionFilter: ExceptionFilter,
) {}

private async initDb() {
Expand Down Expand Up @@ -54,14 +58,21 @@ export class RestApplication {

this.server.use(express.json());
this.server.use(
'/upload',
STATIC_UPLOAD_ROUTE,
express.static(this.config.get('UPLOAD_DIRECTORY'))
);
this.server.use(
STATIC_FILES_ROUTE,
express.static(this.config.get('STATIC_DIRECTORY_PATH'))
);
this.server.use(authenticateMiddleware.execute.bind(authenticateMiddleware));
this.server.use(cors());
}

private async initExceptionFilters() {
this.server.use(this.authExceptionFilter.catch.bind(this.authExceptionFilter));
this.server.use(this.validationExceptionFilter.catch.bind(this.validationExceptionFilter));
this.server.use(this.httpExceptionFilter.catch.bind(this.httpExceptionFilter));
this.server.use(this.appExceptionFilter.catch.bind(this.appExceptionFilter));
}

Expand All @@ -88,7 +99,7 @@ export class RestApplication {
await this.initServer();

this.logger.info(
`Server started on http://${this.config.get('DB_HOST')}:${this.config.get('PORT')}`
`Server started on ${getFullServerPath(this.config.get('HOST'), this.config.get('PORT'))}`
);
}
}
2 changes: 2 additions & 0 deletions src/rest/rest.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const STATIC_UPLOAD_ROUTE = '/upload';
export const STATIC_FILES_ROUTE = '/static';

Check failure on line 2 in src/rest/rest.constant.ts

View workflow job for this annotation

GitHub Actions / Check

Newline required at end of file but not found
6 changes: 5 additions & 1 deletion src/rest/rest.container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Logger, PinoLogger } from '../shared/libs/logger/index.js';
import { Config, IRestSchema, RestConfig } from '../shared/libs/config/index.js';
import { DatabaseClient, MongoDatabaseClient } from '../shared/libs/database-client/index.js';
import { COMPONENT } from '../shared/constants/index.js';
import { AppExceptionFilter, ExceptionFilter } from '../shared/libs/rest/index.js';
import { PathTransformer } from '../shared/libs/rest/transform/path-transformer.js';
import { AppExceptionFilter, ExceptionFilter, HttpErrorExceptionFilter, ValidationExceptionFilter } from '../shared/libs/rest/index.js';

export function createRestApplicationContainer() {
const restApplicationContainer = new Container();
Expand All @@ -15,6 +16,9 @@ export function createRestApplicationContainer() {
restApplicationContainer.bind<Config<IRestSchema>>(COMPONENT.CONFIG).to(RestConfig).inSingletonScope();
restApplicationContainer.bind<DatabaseClient>(COMPONENT.DATABASE_CLIENT).to(MongoDatabaseClient).inSingletonScope();
restApplicationContainer.bind<ExceptionFilter>(COMPONENT.EXCEPTION_FILTER).to(AppExceptionFilter).inSingletonScope();
restApplicationContainer.bind<ExceptionFilter>(COMPONENT.HTTP_EXCEPTION_FILTER).to(HttpErrorExceptionFilter).inSingletonScope();
restApplicationContainer.bind<ExceptionFilter>(COMPONENT.VALIDATION_EXCEPTION_FILTER).to(ValidationExceptionFilter).inSingletonScope();
restApplicationContainer.bind<PathTransformer>(COMPONENT.PATH_TRANSFORMER).to(PathTransformer).inSingletonScope();

return restApplicationContainer;
}
3 changes: 3 additions & 0 deletions src/shared/constants/component.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ export const COMPONENT = {
AUTH_SERVICE: Symbol.for('kAuthService'),
AUTH_CONTROLLER: Symbol.for('kAuthController'),
AUTH_EXCEPTION_FILTER: Symbol.for('kAuthExceptionFilter'),
HTTP_EXCEPTION_FILTER: Symbol.for('kHttpExceptionFilter'),
VALIDATION_EXCEPTION_FILTER: Symbol.for('kValidationExceptionFilter'),
PATH_TRANSFORMER: Symbol.for('kPathTransformer'),
} as const;
21 changes: 17 additions & 4 deletions src/shared/helpers/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ClassConstructor, plainToInstance } from 'class-transformer';
import { ValidationError } from 'class-validator';

import { ApplicationError, ValidationErrorField } from '../libs/rest/index.js';

export function generateRandomValue(min:number, max: number, numAfterDigit = 0) {
return +((Math.random() * (max - min)) + min).toFixed(numAfterDigit);
Expand All @@ -22,8 +25,18 @@ export function fillDTO<T, V>(someDto: ClassConstructor<T>, plainObject: V) {
return plainToInstance(someDto, plainObject, { excludeExtraneousValues: true });
}

export function createErrorObject(message: string) {
return {
error: message,
};
export function createErrorObject(errorType: ApplicationError, error: string, details: ValidationErrorField[] = []) {
return { errorType, error, details };
}

export function reduceValidationErrors(errors: ValidationError[]): ValidationErrorField[] {
return errors.map(({ property, value, constraints}) => ({
property,
value,
messages: constraints ? Object.values(constraints) : []
}));
}

export function getFullServerPath(host: string, port: number) {
return `http://${host}:${port}`;
}
4 changes: 3 additions & 1 deletion src/shared/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ export {
getRandomItem,
getErrorMessage,
fillDTO,
createErrorObject
createErrorObject,
reduceValidationErrors,
getFullServerPath,
} from './common.js';

export { getCurrentModuleDirectoryPath } from './file-system.js';
Expand Down
2 changes: 2 additions & 0 deletions src/shared/libs/config/rest.schema.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ export interface IRestSchema {
UPLOAD_DIRECTORY: string;
JWT_SECRET: string;
JWT_EXPIRED: string;
HOST: string;
STATIC_DIRECTORY_PATH: string;
}
12 changes: 12 additions & 0 deletions src/shared/libs/config/rest.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,16 @@ export const configRestSchema = convict<IRestSchema>({
env: 'JWT_EXPIRED',
default: null
},
HOST: {
doc: 'Host where started service',
format: String,
env: 'HOST',
default: null
},
STATIC_DIRECTORY_PATH: {
doc: 'Path to directory with static resources',
format: String,
env: 'STATIC_DIRECTORY_PATH',
default: null
},
});
10 changes: 8 additions & 2 deletions src/shared/libs/rest/controller/base-controller.abstract.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { injectable } from 'inversify';
import { inject, injectable } from 'inversify';
import { StatusCodes } from 'http-status-codes';
import { Response, Router } from 'express';
import asyncHandler from 'express-async-handler';

import { Controller } from './controller.interface.js';
import { Logger } from '../../logger/index.js';
import { Route } from '../types/route.interface.js';
import { PathTransformer } from '../transform/path-transformer.js';
import { COMPONENT } from '../../../constants/component.constant.js';


@injectable()
export abstract class BaseController implements Controller {
private readonly DEFAULT_CONTENT_TYPE = 'application/json';
public readonly router: Router;

@inject(COMPONENT.PATH_TRANSFORMER)
private pathTranformer!: PathTransformer;

constructor(
protected readonly logger: Logger
) {
Expand All @@ -31,10 +36,11 @@ export abstract class BaseController implements Controller {
}

public send<T>(res: Response, statusCode: number, data: T): void {
const modifiedData = this.pathTranformer.execute(data as Record<string, unknown>);
res
.type(this.DEFAULT_CONTENT_TYPE)
.status(statusCode)
.json(data);
.json(modifiedData);
}

public created<T>(res: Response, data: T): void {
Expand Down
1 change: 1 addition & 0 deletions src/shared/libs/rest/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { HttpError } from './http-error.js';
export { ValidationError } from './validation.error.js';
13 changes: 13 additions & 0 deletions src/shared/libs/rest/errors/validation.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { StatusCodes } from 'http-status-codes';

import { HttpError } from './http-error.js';
import { ValidationErrorField } from '../types/validation-error-field.type.js';

export class ValidationError extends HttpError {
public details: ValidationErrorField[] = [];

constructor(message: string, errors: ValidationErrorField[]) {
super(StatusCodes.BAD_REQUEST, message);
this.details = errors;
}
}
41 changes: 0 additions & 41 deletions src/shared/libs/rest/exception-filter/app-exception-filter.ts

This file was deleted.

25 changes: 25 additions & 0 deletions src/shared/libs/rest/exception-filter/app.exception-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { inject, injectable } from 'inversify';
import { StatusCodes } from 'http-status-codes';
import { NextFunction, Request, Response } from 'express';

import { ExceptionFilter } from './exception-filter.interface.js';
import { Logger } from '../../logger/index.js';
import { createErrorObject } from '../../../helpers/index.js';
import { ApplicationError } from '../types/application-error.enum.js';
import { COMPONENT } from '../../../constants/component.constant.js';

@injectable()
export class AppExceptionFilter implements ExceptionFilter {
constructor(
@inject(COMPONENT.LOGGER) private readonly logger: Logger
) {
this.logger.info('Register AppExceptionFilter');
}

public catch(error: Error, _: Request, res: Response, _next: NextFunction): void {
this.logger.error(error.message, error);
res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.json(createErrorObject(ApplicationError.ServiceError, error.message));
}
}
Loading

0 comments on commit 2b0df5d

Please sign in to comment.