diff --git a/mochalov_danila_lab_3/.gitignore b/mochalov_danila_lab_3/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/mochalov_danila_lab_3/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/mochalov_danila_lab_3/README.md b/mochalov_danila_lab_3/README.md new file mode 100644 index 0000000..a156c8d --- /dev/null +++ b/mochalov_danila_lab_3/README.md @@ -0,0 +1,35 @@ +# Лабораторная работа №3 +#### ПИбд-42. Мочалов Данила. + +#### При выполнении использовал: +- Python 3.12 +- Flask +- requests +- Docker +- Docker Compose + +#### Задание: +Для выполнения лабораторной работы были созданы следующие сущности: +##### 1. Категория. Имеет поля: + - id + - name + - description +##### 2. Услуга. Имеет поля: + - id + - name + - category_id + +##### Каждой категории может принадлежать множество услуг. +##### Были развернуты два сервиса - category_service и service_service, синхронно обменивающиеся сообщениями. +##### Сущности хранятся в оперативной памяти (без БД) + +#### Инструкция +Для запуска лабораторной работы, перейдите в папку *mochalov_danila_lab_3* и выполните команду: +``` +docker-compose up --build -d --remove-orphans +``` + +#### Демонстрация работы +Доступна по [ссылке](https://drive.google.com/file/d/1-DoS7b4ArfetVDsqjGHrfVxWhvIBt_fH/view?usp=sharing) + + diff --git a/mochalov_danila_lab_3/category_service/Dockerfile b/mochalov_danila_lab_3/category_service/Dockerfile new file mode 100644 index 0000000..692acd7 --- /dev/null +++ b/mochalov_danila_lab_3/category_service/Dockerfile @@ -0,0 +1,11 @@ +FROM python:latest + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY category_service/category_service.py . + +CMD ["python", "category_service.py"] \ No newline at end of file diff --git a/mochalov_danila_lab_3/category_service/category_service.py b/mochalov_danila_lab_3/category_service/category_service.py new file mode 100644 index 0000000..64cf28c --- /dev/null +++ b/mochalov_danila_lab_3/category_service/category_service.py @@ -0,0 +1,102 @@ +from flask import Flask, jsonify, request +import requests + +app = Flask(__name__) + +services_url = 'http://serviceService:20002/' + +class Category: + def __init__(self, id: int, name: str, description: str): + self.id = id + self.name = name + self.description = description + + def to_dict(self): + return { + "id": self.id, + "name": self.name, + "description": self.description + } + + def to_dict_with_services(self, services: list): + return { + "id": self.id, + "name": self.name, + "description": self.description, + "services": services + } + + +categories = [ + Category(1, 'Shooters', 'Shooter games'), + Category(2, 'Strategy', 'Strategy games'), +] + + +@app.route('/', methods=['GET']) +def get_all_categories(): + return jsonify([category.to_dict() for category in categories]), 200 + + +@app.route('/', methods=['GET']) +def get_category(category_id): + for category in categories: + if category.id == category_id: + return category.to_dict(), 200 + return f'Category with id {category_id} not found', 404 + + +@app.route('/info/', methods=['GET']) +def get_category_extended(category_id): + for category in categories: + if category.id == category_id: + response = requests.get(services_url + f'by-category/{category_id}') + return category.to_dict_with_services(response.json()), 200 + return f'Category with id {category_id} not found', 404 + + +@app.route('/check/', methods=['GET']) +def check_category_exists(category_id): + for category in categories: + if category.id == category_id: + return '', 200 + return '', 404 + + +@app.route('/', methods=['POST']) +def create_category(): + data = request.json + name = data.get('name') + description = data.get('description') + if name is None or description is None: + return 'Insufficient data to create a category', 400 + + next_id = max([c.id for c in categories]) + 1 if categories else 1 + new_category = Category(next_id, name, description) + categories.append(new_category) + return jsonify(new_category.to_dict()), 201 + + + +@app.route('/', methods=['PUT']) +def update_category(category_id): + data = request.json + for category in categories: + if category.id == category_id: + category.name = data.get('name', category.name) + category.description = data.get('description', category.description) + return jsonify(category.to_dict()), 200 + return f'Category with id {category_id} not found', 404 + + +@app.route('/', methods=['DELETE']) +def delete_category(category_id): + for category in categories: + if category.id == category_id: + categories.remove(category) + return 'Category deleted', 200 + return f'Category with id {category_id} not found', 404 + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=20001, debug=True) \ No newline at end of file diff --git a/mochalov_danila_lab_3/docker-compose.yaml b/mochalov_danila_lab_3/docker-compose.yaml new file mode 100644 index 0000000..aeab821 --- /dev/null +++ b/mochalov_danila_lab_3/docker-compose.yaml @@ -0,0 +1,26 @@ +services: + category_service: + container_name: categoryService + build: + context: . + dockerfile: ./category_service/Dockerfile + expose: + - 20001 + + service_service: + container_name: serviceService + build: + context: . + dockerfile: ./service_service/Dockerfile + expose: + - 20002 + + nginx: + image: nginx:latest + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: + - category_service + - service_service \ No newline at end of file diff --git a/mochalov_danila_lab_3/nginx.conf b/mochalov_danila_lab_3/nginx.conf new file mode 100644 index 0000000..20e00f6 --- /dev/null +++ b/mochalov_danila_lab_3/nginx.conf @@ -0,0 +1,25 @@ +events { worker_connections 1024; } + +http { + server { + listen 80; + listen [::]:80; + server_name localhost; + + location /categoryService/ { + proxy_pass http://categoryService:20001/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /serviceService/ { + proxy_pass http://serviceService:20002/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file diff --git a/mochalov_danila_lab_3/requirements.txt b/mochalov_danila_lab_3/requirements.txt new file mode 100644 index 0000000..494909e --- /dev/null +++ b/mochalov_danila_lab_3/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.3 +requests==2.32.3 \ No newline at end of file diff --git a/mochalov_danila_lab_3/service_service/Dockerfile b/mochalov_danila_lab_3/service_service/Dockerfile new file mode 100644 index 0000000..119d191 --- /dev/null +++ b/mochalov_danila_lab_3/service_service/Dockerfile @@ -0,0 +1,11 @@ +FROM python:latest + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY service_service/service_service.py . + +CMD ["python", "service_service.py"] \ No newline at end of file diff --git a/mochalov_danila_lab_3/service_service/service_service.py b/mochalov_danila_lab_3/service_service/service_service.py new file mode 100644 index 0000000..ced90bf --- /dev/null +++ b/mochalov_danila_lab_3/service_service/service_service.py @@ -0,0 +1,111 @@ +from flask import Flask, jsonify, request +import requests + +app = Flask(__name__) + +categories_url = 'http://categoryService:20001/' + +class Service: + def __init__(self, id: int, name: str, category_id: int): + self.id = id + self.name = name + self.category_id = category_id + + def to_dict(self): + return { + "id": self.id, + "name": self.name, + "category_id": self.category_id + } + + def to_dict_with_category(self, category): + return { + "id": self.id, + "name": self.name, + "category": category + } + + +services = [ + Service(1, 'CS 2', 1), + Service(2, 'StarCraft', 2) +] + + +@app.route('/', methods=['GET']) +def get_all_services(): + return jsonify([service.to_dict() for service in services]), 200 + + +@app.route('/by-category/', methods=['GET']) +def get_services_by_category(category_id): + services_for_category = [service.to_dict() for service in services if service.category_id == category_id] + return jsonify(services_for_category), 200 + + +@app.route('/', methods=['GET']) +def get_service(service_id): + for service in services: + if service.id == service_id: + return jsonify(service.to_dict()), 200 + return f'Service with id {service_id} not found', 404 + + +@app.route('/info/', methods=['GET']) +def get_service_with_category(service_id): + for service in services: + if service.id == service_id: + try: + response = requests.get(categories_url + str(service.category_id)) + response.raise_for_status() + category = response.json() + return jsonify(service.to_dict_with_category(category)), 200 + except requests.exceptions.RequestException as e: + return f"Error fetching category: {e}", 500 + + return f'Service with id {service_id} not found', 404 + + +@app.route('/', methods=['POST']) +def create_service(): + data = request.json + name = data.get('name') + category_id = data.get('category_id') + + try: + response = requests.get(categories_url + f'check/{category_id}') + response.raise_for_status() + if response.status_code == 200: + next_id = max([s.id for s in services]) + 1 if services else 1 + new_service = Service(next_id, name, category_id) + services.append(new_service) + return jsonify(new_service.to_dict()), 201 + else: + return f"Category with ID {category_id} not found", 404 + except requests.exceptions.RequestException as e: + return f"Error checking category: {e}", 500 + + +@app.route('/', methods=['PUT']) +def update_service(service_id): + data = request.json + for service in services: + if service.id == service_id: + service.name = data.get('name', service.name) + service.category_id = data.get('category_id', service.category_id) + return jsonify(service.to_dict()), 200 + return f'Service with id {service_id} not found', 404 + + + +@app.route('/', methods=['DELETE']) +def delete_service(service_id): + for service in services: + if service.id == service_id: + services.remove(service) + return 'Service deleted', 200 + return f'Service with id {service_id} not found', 404 + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=20002, debug=True) \ No newline at end of file