Skip to content

Commit

Permalink
Merge pull request #11 from rcole1919/module8-task1
Browse files Browse the repository at this point in the history
  • Loading branch information
keksobot authored Nov 3, 2024
2 parents 2e541b0 + bc4600e commit 044847b
Show file tree
Hide file tree
Showing 46 changed files with 525 additions and 113 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ DB_PORT=
DB_UI_PORT=
DB_NAME=
UPLOAD_DIRECTORY=
JWT_SECRET=
14 changes: 14 additions & 0 deletions Workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ npm start

В процессе запуска проекта будет выполнен процесс «Сборки проекта» и запуска результирующего кода.

#### Запустить проект в дев режиме

```bash
npm run start:dev
```

В процессе запуска проекта будет выполнен процесс «Сборки проекта» и запуска результирующего кода на этапе раработки с отслеживанием внесения правок.

#### Запустить сервер с моковыми данными

```bash
npm run mock:server
```

## Структура проекта

### Директория `src`
Expand Down
7 changes: 7 additions & 0 deletions custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TTokenPayload } from './src/shared/modules/auth/types/index.js';

declare module 'express-serve-static-core' {
export interface Request {
tokenPayload: TTokenPayload;
}
}
36 changes: 34 additions & 2 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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 @@ -49,13 +50,15 @@
"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",
"express-async-handler": "1.2.0",
"got": "14.4.2",
"http-status-codes": "2.3.0",
"inversify": "6.0.2",
"jose": "^5.9.6",
"mime-types": "2.1.35",
"mongoose": "8.3.4",
"multer": "1.4.5-lts.1",
Expand Down
40 changes: 32 additions & 8 deletions specification/specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ paths:
schema:
$ref: '#/components/schemas/user'

"400":
description: Несуществующий логин или пароль.
"404":
description: Несуществующий логин.

"401":
description: Неправильный пароль.

get:
tags:
Expand All @@ -85,8 +88,8 @@ paths:
schema:
$ref: '#/components/schemas/user'

"404":
description: Пользователь не найден.
"401":
description: Невалидный токен.

/users/favorites:
get:
Expand Down Expand Up @@ -117,6 +120,9 @@ paths:
"200":
description: Предложение добавлено в избранное.

"400":
description: Невалидный id предложения

"404":
description: Предложение с таким id не найдено.

Expand All @@ -136,6 +142,9 @@ paths:
"204":
description: Предложение удалено из избранного.

"400":
description: Невалидный id предложения

"404":
description: Предложение с таким id не найдено.

Expand Down Expand Up @@ -178,7 +187,7 @@ paths:
"400":
description: Введена некорректная информация

"403":
"401":
description: Только авторизованные пользователи могут создавать предложение

get:
Expand Down Expand Up @@ -226,9 +235,12 @@ paths:
"400":
description: Данные введены некорректно

"403":
"401":
description: Только авторизованные пользователи могут редактировать предложение

"403":
description: Доступ к предложению запрещен

delete:
tags:
- offers
Expand All @@ -242,9 +254,15 @@ paths:
"404":
description: Предложение с таким id не найдено.

"403":
"400":
description: Невалидный id предложения

"401":
description: Только авторизованные пользователи могут удалять предложение

"403":
description: Доступ к предложению запрещен

get:
tags:
- offers
Expand All @@ -259,6 +277,9 @@ paths:
schema:
$ref: '#/components/schemas/offer'

"400":
description: Невалидный id предложения

"404":
description: Предложение с таким id не найдено.

Expand Down Expand Up @@ -303,6 +324,9 @@ paths:
schema:
$ref: '#/components/schemas/comments'

"400":
description: Невалидный id предложения

"404":
description: Предложение с таким id не найдено.

Expand Down Expand Up @@ -330,7 +354,7 @@ paths:
"404":
description: Предложение с таким id не найдено.

"403":
"401":
description: Только авторизованные пользователи могут оставлять комментарии

components:
Expand Down
2 changes: 2 additions & 0 deletions src/main.rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { COMPONENT } from './shared/constants/index.js';
import { createUserContainer } from './shared/modules/user/index.js';
import { createOfferContainer } from './shared/modules/offer/index.js';
import { createCommentContainer } from './shared/modules/comment/index.js';
import { createAuthContainer } from './shared/modules/auth/index.js';

async function bootstrap() {
const appContainer = Container.merge(
createRestApplicationContainer(),
createUserContainer(),
createOfferContainer(),
createCommentContainer(),
createAuthContainer(),
);

const application = appContainer.get<RestApplication>(COMPONENT.REST_APPLICATION);
Expand Down
2 changes: 2 additions & 0 deletions src/rest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export { ValidateObjectIdMiddleware } from './middlewares/validate-objectid.midd
export { ValidateDTOMiddleware } from './middlewares/validate-dto.middleware.js';
export { DocumentExistsMiddleware } from './middlewares/document-exists.middleware.js';
export { UploadFileMiddleware } from './middlewares/upload-file.middleware.js';
export { ParseTokenMiddleware } from './middlewares/parse-token.middleware.js';
export { PrivateRouteMiddleware } from './middlewares/private-route.middleware.js';
40 changes: 40 additions & 0 deletions src/rest/middlewares/parse-token.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NextFunction, Request, Response } from 'express';
import { jwtVerify } from 'jose';
import { StatusCodes } from 'http-status-codes';

import { createSecretKey } from 'node:crypto';

import { IMiddleware } from '../types/index.js';
import { HttpError } from '../index.js';
import { isTokenPayload } from '../../shared/helpers/index.js';

export class ParseTokenMiddleware implements IMiddleware {
constructor(private readonly jwtSecret: string) {}

public async execute(req: Request, _res: Response, next: NextFunction): Promise<void> {
const authorizationHeader = req.headers?.authorization?.split(' ');

if (!authorizationHeader) {
return next();
}

const [, token] = authorizationHeader;

try {
const { payload } = await jwtVerify(token, createSecretKey(this.jwtSecret, 'utf-8'));

if (isTokenPayload(payload)) {
req.tokenPayload = { ...payload };
return next();
} else {
throw new Error('Bad token');
}
} catch {
return next(new HttpError(
StatusCodes.UNAUTHORIZED,
'Invalid token',
'ParseTokenMiddleware',
));
}
}
}
19 changes: 19 additions & 0 deletions src/rest/middlewares/private-route.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { StatusCodes } from 'http-status-codes';
import { NextFunction, Request, Response } from 'express';

import { IMiddleware } from '../types/index.js';
import { HttpError } from '../index.js';

export class PrivateRouteMiddleware implements IMiddleware {
public async execute({ tokenPayload }: Request, _res: Response, next: NextFunction): Promise<void> {
if (!tokenPayload) {
throw new HttpError(
StatusCodes.UNAUTHORIZED,
'Unauthorized',
'PrivateRouteMiddleware',
);
}

return next();
}
}
7 changes: 7 additions & 0 deletions src/rest/rest.application.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { inject, injectable } from 'inversify';
import express, { Express } from 'express';
import cors from 'cors';

import { ILogger } from '../shared/libs/logger/types/index.js';
import { IConfig, TRestSchema } from '../shared/libs/config/types/index.js';
import { COMPONENT } from '../shared/constants/index.js';
import { IDatabaseClient } from '../shared/libs/database-client/types/index.js';
import { getMongoURI } from '../shared/helpers/index.js';
import { IController, IExceptionFilter } from './types/index.js';
import { ParseTokenMiddleware } from './index.js';

@injectable()
export class RestApplication {
Expand All @@ -20,6 +22,7 @@ export class RestApplication {
@inject(COMPONENT.USER_CONTROLLER) private readonly userController: IController,
@inject(COMPONENT.COMMENT_CONTROLLER) private readonly commentController: IController,
@inject(COMPONENT.EXCEPTION_FILTER) private readonly appExceptionFilter: IExceptionFilter,
@inject(COMPONENT.AUTH_EXCEPTION_FILTER) private readonly authExceptionFilter: IExceptionFilter,
) {}

private async initDb() {
Expand All @@ -46,14 +49,18 @@ export class RestApplication {
}

private initMiddleware() {
const authenticateMiddleware = new ParseTokenMiddleware(this.config.get('JWT_SECRET'));
this.server.use(express.json());
this.server.use(
'/upload',
express.static(this.config.get('UPLOAD_DIRECTORY')),
);
this.server.use(authenticateMiddleware.execute.bind(authenticateMiddleware));
this.server.use(cors());
}

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

Expand Down
Loading

0 comments on commit 044847b

Please sign in to comment.