From 9ff5b267bc7fcaf27ffbcaeeefb3048442e04d21 Mon Sep 17 00:00:00 2001 From: maxnes3 Date: Mon, 25 Nov 2024 02:10:37 +0400 Subject: [PATCH] bondarenko_max_lab_3 is done --- bondarenko_max_lab_3/README.md | 2 + bondarenko_max_lab_3/authors/.env | 1 + bondarenko_max_lab_3/authors/Dockerfile | 20 ++ bondarenko_max_lab_3/authors/package.json | 14 ++ .../src/controllers/authorController.js | 50 +++++ bondarenko_max_lab_3/authors/src/index.js | 15 ++ .../authors/src/models/authorModel.js | 16 ++ .../authors/src/routes/authorRoutes.js | 166 +++++++++++++++++ bondarenko_max_lab_3/authors/src/swagger.js | 22 +++ bondarenko_max_lab_3/books/.env | 1 + bondarenko_max_lab_3/books/Dockerfile | 20 ++ bondarenko_max_lab_3/books/package.json | 16 ++ .../books/src/controllers/bookController.js | 72 +++++++ bondarenko_max_lab_3/books/src/index.js | 15 ++ .../books/src/models/bookModel.js | 18 ++ .../books/src/routes/bookRoutes.js | 175 ++++++++++++++++++ bondarenko_max_lab_3/books/src/swagger.js | 22 +++ bondarenko_max_lab_3/docker-compose.yml | 21 +++ bondarenko_max_lab_3/nginx.conf | 25 +++ 19 files changed, 691 insertions(+) create mode 100644 bondarenko_max_lab_3/README.md create mode 100644 bondarenko_max_lab_3/authors/.env create mode 100644 bondarenko_max_lab_3/authors/Dockerfile create mode 100644 bondarenko_max_lab_3/authors/package.json create mode 100644 bondarenko_max_lab_3/authors/src/controllers/authorController.js create mode 100644 bondarenko_max_lab_3/authors/src/index.js create mode 100644 bondarenko_max_lab_3/authors/src/models/authorModel.js create mode 100644 bondarenko_max_lab_3/authors/src/routes/authorRoutes.js create mode 100644 bondarenko_max_lab_3/authors/src/swagger.js create mode 100644 bondarenko_max_lab_3/books/.env create mode 100644 bondarenko_max_lab_3/books/Dockerfile create mode 100644 bondarenko_max_lab_3/books/package.json create mode 100644 bondarenko_max_lab_3/books/src/controllers/bookController.js create mode 100644 bondarenko_max_lab_3/books/src/index.js create mode 100644 bondarenko_max_lab_3/books/src/models/bookModel.js create mode 100644 bondarenko_max_lab_3/books/src/routes/bookRoutes.js create mode 100644 bondarenko_max_lab_3/books/src/swagger.js create mode 100644 bondarenko_max_lab_3/docker-compose.yml create mode 100644 bondarenko_max_lab_3/nginx.conf diff --git a/bondarenko_max_lab_3/README.md b/bondarenko_max_lab_3/README.md new file mode 100644 index 0000000..88e8d6e --- /dev/null +++ b/bondarenko_max_lab_3/README.md @@ -0,0 +1,2 @@ +# Лабораторная работа 3 - REST API, Gateway и синхронный обмен между микросервисами +### ПИбд-42 || Бондаренко Максим \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/.env b/bondarenko_max_lab_3/authors/.env new file mode 100644 index 0000000..c0c68b1 --- /dev/null +++ b/bondarenko_max_lab_3/authors/.env @@ -0,0 +1 @@ +PORT=3000 \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/Dockerfile b/bondarenko_max_lab_3/authors/Dockerfile new file mode 100644 index 0000000..9e35a64 --- /dev/null +++ b/bondarenko_max_lab_3/authors/Dockerfile @@ -0,0 +1,20 @@ +# Используем базовый образ Node.js +FROM node:14 + +# Создаем рабочую директорию в контейнере +WORKDIR /app + +# Копируем package.json и package-lock.json +COPY package*.json ./ + +# Устанавливаем зависимости +RUN npm install + +# Копируем исходный код +COPY . . + +# Указываем порт, который будет слушать приложение +EXPOSE 3000 + +# Команда для запуска приложения +CMD ["npm", "start"] \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/package.json b/bondarenko_max_lab_3/authors/package.json new file mode 100644 index 0000000..2b84f1b --- /dev/null +++ b/bondarenko_max_lab_3/authors/package.json @@ -0,0 +1,14 @@ +{ + "name": "authors", + "version": "1.0.0", + "main": "src/index.js", + "scripts": { + "start": "node src/index.js" + }, + "dependencies": { + "express": "^4.17.1", + "swagger-jsdoc": "^6.1.0", + "swagger-ui-express": "^4.3.0", + "uuid": "^8.3.2" + } +} \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/src/controllers/authorController.js b/bondarenko_max_lab_3/authors/src/controllers/authorController.js new file mode 100644 index 0000000..498215f --- /dev/null +++ b/bondarenko_max_lab_3/authors/src/controllers/authorController.js @@ -0,0 +1,50 @@ +const { Author, authors } = require('../models/authorModel'); + +const getAuthors = (req, res) => { + res.json(authors); +}; + +const getAuthorById = (req, res) => { + const author = authors.find(a => a.uuid === req.params.uuid); + if (author) { + res.json(author); + } else { + res.status(404).send('Author not found'); + } +}; + +const createAuthor = (req, res) => { + const { name, birthdate } = req.body; + const newAuthor = new Author(name, birthdate); + authors.push(newAuthor); + res.status(201).json(newAuthor); +}; + +const updateAuthor = (req, res) => { + const author = authors.find(a => a.uuid === req.params.uuid); + if (author) { + author.name = req.body.name || author.name; + author.birthdate = req.body.birthdate || author.birthdate; + res.json(author); + } else { + res.status(404).send('Author not found'); + } +}; + +const deleteAuthor = (req, res) => { + const authorIndex = authors.findIndex(a => a.uuid === req.params.uuid); + if (authorIndex !== -1) { + authors.splice(authorIndex, 1); + res.status(204).send(); + } else { + res.status(404).send('Author not found'); + } +}; + +module.exports = { + getAuthors, + getAuthorById, + createAuthor, + updateAuthor, + deleteAuthor +}; \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/src/index.js b/bondarenko_max_lab_3/authors/src/index.js new file mode 100644 index 0000000..27cab60 --- /dev/null +++ b/bondarenko_max_lab_3/authors/src/index.js @@ -0,0 +1,15 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const authorRoutes = require('./routes/authorRoutes'); +const setupSwagger = require('./swagger'); + +const app = express(); +const port = process.env.PORT || 3000; + +app.use(bodyParser.json()); +app.use('/authors', authorRoutes); +setupSwagger(app); + +app.listen(port, () => { + console.log(`Authors service is running on port ${port}`); +}); \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/src/models/authorModel.js b/bondarenko_max_lab_3/authors/src/models/authorModel.js new file mode 100644 index 0000000..6366300 --- /dev/null +++ b/bondarenko_max_lab_3/authors/src/models/authorModel.js @@ -0,0 +1,16 @@ +const { v4: uuidv4 } = require('uuid'); + +class Author { + constructor(name, birthdate) { + this.uuid = uuidv4(); + this.name = name; + this.birthdate = birthdate; + } +} + +const authors = []; + +module.exports = { + Author, + authors +}; \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/src/routes/authorRoutes.js b/bondarenko_max_lab_3/authors/src/routes/authorRoutes.js new file mode 100644 index 0000000..de60cf3 --- /dev/null +++ b/bondarenko_max_lab_3/authors/src/routes/authorRoutes.js @@ -0,0 +1,166 @@ +const express = require('express'); +const { + getAuthors, + getAuthorById, + createAuthor, + updateAuthor, + deleteAuthor +} = require('../controllers/authorController'); + +const router = express.Router(); + +/** + * @swagger + * components: + * schemas: + * Author: + * type: object + * required: + * - name + * - birthdate + * properties: + * uuid: + * type: string + * description: The auto-generated UUID of the author + * name: + * type: string + * description: The name of the author + * birthdate: + * type: string + * format: date + * description: The birthdate of the author + * example: + * uuid: d5fE_asz + * name: J.K. Rowling + * birthdate: 1965-07-31 + */ + +/** + * @swagger + * tags: + * name: Authors + * description: The authors managing API + */ + +/** + * @swagger + * /authors: + * get: + * summary: Returns the list of all the authors + * tags: [Authors] + * responses: + * 200: + * description: The list of the authors + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Author' + */ +router.get('/', getAuthors); + +/** + * @swagger + * /authors/{uuid}: + * get: + * summary: Get the author by id + * tags: [Authors] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The author id + * responses: + * 200: + * description: The author description by id + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Author' + * 404: + * description: The author was not found + */ +router.get('/:uuid', getAuthorById); + +/** + * @swagger + * /authors: + * post: + * summary: Create a new author + * tags: [Authors] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Author' + * responses: + * 201: + * description: The author was successfully created + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Author' + * 500: + * description: Some server error + */ +router.post('/', createAuthor); + +/** + * @swagger + * /authors/{uuid}: + * put: + * summary: Update the author by the id + * tags: [Authors] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The author id + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Author' + * responses: + * 200: + * description: The author was updated + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Author' + * 404: + * description: The author was not found + * 500: + * description: Some error happened + */ +router.put('/:uuid', updateAuthor); + +/** + * @swagger + * /authors/{uuid}: + * delete: + * summary: Remove the author by id + * tags: [Authors] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The author id + * responses: + * 204: + * description: The author was deleted + * 404: + * description: The author was not found + */ +router.delete('/:uuid', deleteAuthor); + +module.exports = router; \ No newline at end of file diff --git a/bondarenko_max_lab_3/authors/src/swagger.js b/bondarenko_max_lab_3/authors/src/swagger.js new file mode 100644 index 0000000..8cdf7f1 --- /dev/null +++ b/bondarenko_max_lab_3/authors/src/swagger.js @@ -0,0 +1,22 @@ +const swaggerJSDoc = require('swagger-jsdoc'); +const swaggerUi = require('swagger-ui-express'); + +const options = { + definition: { + openapi: '3.0.0', + info: { + title: 'Authors API', + version: '1.0.0', + description: 'API for managing authors', + }, + }, + apis: ['./src/routes/*.js'], +}; + +const swaggerSpec = swaggerJSDoc(options); + +const setupSwagger = (app) => { + app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); +}; + +module.exports = setupSwagger; \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/.env b/bondarenko_max_lab_3/books/.env new file mode 100644 index 0000000..a43d6bb --- /dev/null +++ b/bondarenko_max_lab_3/books/.env @@ -0,0 +1 @@ +PORT=3001 \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/Dockerfile b/bondarenko_max_lab_3/books/Dockerfile new file mode 100644 index 0000000..9d11798 --- /dev/null +++ b/bondarenko_max_lab_3/books/Dockerfile @@ -0,0 +1,20 @@ +# Используем базовый образ Node.js +FROM node:14 + +# Создаем рабочую директорию в контейнере +WORKDIR /app + +# Копируем package.json и package-lock.json +COPY package*.json ./ + +# Устанавливаем зависимости +RUN npm install + +# Копируем исходный код +COPY . . + +# Указываем порт, который будет слушать приложение +EXPOSE 3001 + +# Команда для запуска приложения +CMD ["npm", "start"] \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/package.json b/bondarenko_max_lab_3/books/package.json new file mode 100644 index 0000000..abab68a --- /dev/null +++ b/bondarenko_max_lab_3/books/package.json @@ -0,0 +1,16 @@ +{ + "name": "books", + "version": "1.0.0", + "main": "src/index.js", + "scripts": { + "start": "node src/index.js" + }, + "dependencies": { + "express": "^4.17.1", + "swagger-jsdoc": "^6.1.0", + "swagger-ui-express": "^4.3.0", + "axios": "^0.21.1", + "uuid": "^8.3.2" + } +} + \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/src/controllers/bookController.js b/bondarenko_max_lab_3/books/src/controllers/bookController.js new file mode 100644 index 0000000..12d4e6f --- /dev/null +++ b/bondarenko_max_lab_3/books/src/controllers/bookController.js @@ -0,0 +1,72 @@ +const axios = require('axios'); +const { Book, books } = require('../models/bookModel'); + +const getBooks = (req, res) => { + res.json(books); +}; + +const getBookById = async (req, res) => { + const book = books.find(b => b.uuid === req.params.uuid); + if (book) { + try { + const authorResponse = await axios.get(`http://authors:3000/authors/${book.authorUuid}`); + book.authorInfo = authorResponse.data; + res.json(book); + } catch (error) { + res.status(500).send('Error fetching author information'); + } + } else { + res.status(404).send('Book not found'); + } +}; + +const createBook = async (req, res) => { + const { author, title, year, authorUuid } = req.body; + + try { + const authorResponse = await axios.get(`http://authors:3000/authors/${authorUuid}`); + if (!authorResponse.data) { + return res.status(404).send('Author not found'); + } + const newBook = new Book(author, title, year, authorUuid); + books.push(newBook); + res.status(201).json(newBook); + } catch (error) { + if (error.response && error.response.status === 404) { + res.status(404).send('Author not found'); + } else { + res.status(500).send('Error creating book'); + } + } +}; + +const updateBook = (req, res) => { + const book = books.find(b => b.uuid === req.params.uuid); + if (book) { + book.author = req.body.author || book.author; + book.title = req.body.title || book.title; + book.year = req.body.year || book.year; + book.authorUuid = req.body.authorUuid || book.authorUuid; + res.json(book); + } else { + res.status(404).send('Book not found'); + } +}; + +const deleteBook = (req, res) => { + const bookIndex = books.findIndex(b => b.uuid === req.params.uuid); + if (bookIndex !== -1) { + books.splice(bookIndex, 1); + res.status(204).send(); + } else { + res.status(404).send('Book not found'); + } +}; + +module.exports = { + getBooks, + getBookById, + createBook, + updateBook, + deleteBook +}; \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/src/index.js b/bondarenko_max_lab_3/books/src/index.js new file mode 100644 index 0000000..5312361 --- /dev/null +++ b/bondarenko_max_lab_3/books/src/index.js @@ -0,0 +1,15 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const bookRoutes = require('./routes/bookRoutes'); +const setupSwagger = require('./swagger'); + +const app = express(); +const port = process.env.PORT || 3001; + +app.use(bodyParser.json()); +app.use('/books', bookRoutes); +setupSwagger(app); + +app.listen(port, () => { + console.log(`Books service is running on port ${port}`); +}); \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/src/models/bookModel.js b/bondarenko_max_lab_3/books/src/models/bookModel.js new file mode 100644 index 0000000..819a033 --- /dev/null +++ b/bondarenko_max_lab_3/books/src/models/bookModel.js @@ -0,0 +1,18 @@ +const { v4: uuidv4 } = require('uuid'); + +class Book { + constructor(author, title, year, authorUuid) { + this.uuid = uuidv4(); + this.author = author; + this.title = title; + this.year = year; + this.authorUuid = authorUuid; + } +} + +const books = []; + +module.exports = { + Book, + books +}; \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/src/routes/bookRoutes.js b/bondarenko_max_lab_3/books/src/routes/bookRoutes.js new file mode 100644 index 0000000..03c9b02 --- /dev/null +++ b/bondarenko_max_lab_3/books/src/routes/bookRoutes.js @@ -0,0 +1,175 @@ +const express = require('express'); +const { + getBooks, + getBookById, + createBook, + updateBook, + deleteBook +} = require('../controllers/bookController'); + +const router = express.Router(); + +/** + * @swagger + * components: + * schemas: + * Book: + * type: object + * required: + * - author + * - title + * - year + * - authorUuid + * properties: + * uuid: + * type: string + * description: The auto-generated UUID of the book + * author: + * type: string + * description: The author of the book + * title: + * type: string + * description: The title of the book + * year: + * type: integer + * description: The year the book was published + * authorUuid: + * type: string + * description: The UUID of the author + * example: + * uuid: d5fE_asz + * author: J.K. Rowling + * title: Harry Potter + * year: 1997 + * authorUuid: d5fE_asz + */ + +/** + * @swagger + * tags: + * name: Books + * description: The books managing API + */ + +/** + * @swagger + * /books: + * get: + * summary: Returns the list of all the books + * tags: [Books] + * responses: + * 200: + * description: The list of the books + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Book' + */ +router.get('/', getBooks); + +/** + * @swagger + * /books/{uuid}: + * get: + * summary: Get the book by id + * tags: [Books] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The book id + * responses: + * 200: + * description: The book description by id + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Book' + * 404: + * description: The book was not found + */ +router.get('/:uuid', getBookById); + +/** + * @swagger + * /books: + * post: + * summary: Create a new book + * tags: [Books] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Book' + * responses: + * 201: + * description: The book was successfully created + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Book' + * 500: + * description: Some server error + */ +router.post('/', createBook); + +/** + * @swagger + * /books/{uuid}: + * put: + * summary: Update the book by the id + * tags: [Books] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The book id + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Book' + * responses: + * 200: + * description: The book was updated + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Book' + * 404: + * description: The book was not found + * 500: + * description: Some error happened + */ +router.put('/:uuid', updateBook); + +/** + * @swagger + * /books/{uuid}: + * delete: + * summary: Remove the book by id + * tags: [Books] + * parameters: + * - in: path + * name: uuid + * schema: + * type: string + * required: true + * description: The book id + * responses: + * 204: + * description: The book was deleted + * 404: + * description: The book was not found + */ +router.delete('/:uuid', deleteBook); + +module.exports = router; \ No newline at end of file diff --git a/bondarenko_max_lab_3/books/src/swagger.js b/bondarenko_max_lab_3/books/src/swagger.js new file mode 100644 index 0000000..3fbcdd0 --- /dev/null +++ b/bondarenko_max_lab_3/books/src/swagger.js @@ -0,0 +1,22 @@ +const swaggerJSDoc = require('swagger-jsdoc'); +const swaggerUi = require('swagger-ui-express'); + +const options = { + definition: { + openapi: '3.0.0', + info: { + title: 'Books API', + version: '1.0.0', + description: 'API for managing books', + }, + }, + apis: ['./src/routes/*.js'], +}; + +const swaggerSpec = swaggerJSDoc(options); + +const setupSwagger = (app) => { + app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); +}; + +module.exports = setupSwagger; \ No newline at end of file diff --git a/bondarenko_max_lab_3/docker-compose.yml b/bondarenko_max_lab_3/docker-compose.yml new file mode 100644 index 0000000..346a925 --- /dev/null +++ b/bondarenko_max_lab_3/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.7' + +services: + authors: + build: + context: ./authors + ports: + - "3000:3000" + + books: + build: + context: ./books + ports: + - "3001:3001" + + nginx: + image: nginx:latest + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf \ No newline at end of file diff --git a/bondarenko_max_lab_3/nginx.conf b/bondarenko_max_lab_3/nginx.conf new file mode 100644 index 0000000..83547e4 --- /dev/null +++ b/bondarenko_max_lab_3/nginx.conf @@ -0,0 +1,25 @@ +events { + worker_connections 1024; +} + +http { + upstream authors_service { + server authors:3000; + } + + upstream books_service { + server books:3001; + } + + server { + listen 80; + + location /authors { + proxy_pass http://authors_service; + } + + location /books { + proxy_pass http://books_service; + } + } +} \ No newline at end of file