From b2699db7279ac85971e084e2df573de9a7099d19 Mon Sep 17 00:00:00 2001 From: "Leonov Artur (Depish)" Date: Wed, 11 Dec 2024 16:16:49 +0300 Subject: [PATCH] init --- .gitignore | 7 ++ Dockerfile | 32 ++++++++++ LICENSE | 9 +++ README.md | 96 ++++++++++++++++++++++++++++ client/BuildIncrementerClient.py | 106 +++++++++++++++++++++++++++++++ database.py | 13 ++++ linux/create_service.sh | 60 +++++++++++++++++ linux/remove_service.sh | 31 +++++++++ main.py | 99 +++++++++++++++++++++++++++++ models.py | 14 ++++ requirments.txt | 4 ++ schemas.py | 10 +++ 12 files changed, 481 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 client/BuildIncrementerClient.py create mode 100644 database.py create mode 100755 linux/create_service.sh create mode 100755 linux/remove_service.sh create mode 100644 main.py create mode 100644 models.py create mode 100644 requirments.txt create mode 100644 schemas.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9dabb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +*.pyc +*.db +*.log + +venv +.venv \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..36e754f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Устанавливаем переменную окружения для предотвращения создания .pyc файлов +ENV PYTHONDONTWRITEBYTECODE=1 + +# Устанавливаем переменную окружения для буферизации вывода +ENV PYTHONUNBUFFERED=1 + +# Копируем файл зависимостей в контейнер +COPY requirements.txt . + +# Обновляем pip и устанавливаем зависимости +RUN pip install --upgrade pip \ + && pip install --no-cache-dir -r requirements.txt + +# Копируем весь код проекта в контейнер +COPY . . + +# Создаем директорию для базы данных и устанавливаем права доступа +RUN mkdir -p /app/data \ + && chmod -R 755 /app/data + +# Устанавливаем переменную окружения для указания пути к базе данных +ENV DATABASE_URL=sqlite:///./data/counters.db + +# Экспонируем порт, на котором будет работать приложение +EXPOSE 8000 + +# Команда для запуска приложения +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..11c58a9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 depish + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9c56e8 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Сервис Счетчиков + +**Сервис Счетчиков** на базе FastAPI позволяет инкрементировать, получать и сбрасывать счетчики с поддержкой `namespace`, `application` и `version`. Данные сохраняются в базе данных SQLite. + +## Возможности + +- Инкрементирование счетчика +- Получение текущего значения счетчика +- Сброс счетчика до нуля +- Поддержка пространств имен, приложений и версий +- Безопасность при одновременных запросах +- Разворачивание через Docker + +## API Документация + +После запуска сервиса, интерактивная документация доступна по адресу: [http://localhost:8000/docs](http://localhost:8000/docs) + +### Эндпоинты + +#### Инкрементировать счетчик + +- **URL**: `POST /{namespace}/{application}/{version}` +- **Описание**: Увеличивает счетчик на 1. Если счетчик не существует, создаёт его с начальным значением 1. +- **Параметры**: + - `namespace` (строка) + - `application` (строка) + - `version` (строка) +- **Ответ**: + + ```json + { + "namespace": "my_namespace", + "application": "my_app", + "version": "1.0", + "value": 1 + } + ``` + +#### Получить значение счетчика + +- **URL**: `GET /{namespace}/{application}/{version}` +- **Описание**: Возвращает текущее значение счетчика. Если счетчик не найден, возвращает 0. +- **Параметры**: + - `namespace` (строка) + - `application` (строка) + - `version` (строка) +- **Ответ**: + + ```json + { + "namespace": "my_namespace", + "application": "my_app", + "version": "1.0", + "value": 1 + } + ``` + +#### Сбросить счетчик + +- **URL**: `DELETE /{namespace}/{application}/{version}` +- **Описание**: Сбрасывает счетчик до 0. Если счетчик не существует, создаёт его с значением 0. +- **Параметры**: + - `namespace` (строка) + - `application` (строка) + - `version` (строка) +- **Ответ**: + + ```json + { + "namespace": "my_namespace", + "application": "my_app", + "version": "1.0", + "value": 0 + } + ``` + +## Примеры использования + +### Инкрементировать счетчик + +```bash +curl -X POST "http://localhost:8000/my_namespace/my_app/1.0" -H "Content-Type: application/json" +``` + +Получить значение счетчика + +```bash +curl -X GET "http://localhost:8000/my_namespace/my_app/1.0" -H "Content-Type: application/json" +``` + +Сбросить счетчик + +```bash +curl -X DELETE "http://localhost:8000/my_namespace/my_app/1.0" -H "Content-Type: application/json" +``` + diff --git a/client/BuildIncrementerClient.py b/client/BuildIncrementerClient.py new file mode 100644 index 0000000..5b12cf1 --- /dev/null +++ b/client/BuildIncrementerClient.py @@ -0,0 +1,106 @@ +import requests +from typing import Optional + +class BuildIncrementerClient: + """ + Клиент для взаимодействия с сервисом счетчиков на базе FastAPI. + + Позволяет инкрементировать, получать и сбрасывать счетчики с поддержкой namespace, + application и version. + + Пример использования: + client = BuildIncrementerClient(base_url="http://localhost:8000") + new_value = client.increment("my_namespace", "my_app", "1.0") + current_value = client.get_value("my_namespace", "my_app", "1.0") + reset_value = client.reset("my_namespace", "my_app", "1.0") + """ + + def __init__(self, base_url: str): + """ + Инициализирует клиент с базовым URL сервиса. + + Args: + base_url (str): Базовый URL сервиса, например, "http://localhost:8000" + """ + self.base_url = base_url.rstrip('/') + + def increment(self, namespace: str, application: str, version: str) -> int: + """ + Инкрементирует счетчик и возвращает новое значение. + + Args: + namespace (str): Пространство имен. + application (str): Имя приложения. + version (str): Версия приложения. + + Returns: + int: Новое значение счетчика. + + Raises: + requests.HTTPError: Если запрос завершился с ошибкой. + """ + url = f"{self.base_url}/{namespace}/{application}/{version}" + response = requests.post(url) + self._handle_response(response) + return response.json()['value'] + + def get_value(self, namespace: str, application: str, version: str) -> int: + """ + Получает текущее значение счетчика. + + Args: + namespace (str): Пространство имен. + application (str): Имя приложения. + version (str): Версия приложения. + + Returns: + int: Текущее значение счетчика. + + Raises: + requests.HTTPError: Если запрос завершился с ошибкой. + """ + url = f"{self.base_url}/{namespace}/{application}/{version}" + response = requests.get(url) + self._handle_response(response) + return response.json()['value'] + + def reset(self, namespace: str, application: str, version: str) -> int: + """ + Сбрасывает счетчик до 0 и возвращает новое значение. + + Args: + namespace (str): Пространство имен. + application (str): Имя приложения. + version (str): Версия приложения. + + Returns: + int: Новое значение счетчика после сброса. + + Raises: + requests.HTTPError: Если запрос завершился с ошибкой. + """ + url = f"{self.base_url}/{namespace}/{application}/{version}" + response = requests.delete(url) + self._handle_response(response) + return response.json()['value'] + + def _handle_response(self, response: requests.Response): + """ + Обрабатывает ответ от сервера, выбрасывая исключение при ошибке. + + Args: + response (requests.Response): Ответ от сервера. + + Raises: + requests.HTTPError: Если ответ содержит ошибку. + """ + try: + response.raise_for_status() + except requests.HTTPError as e: + try: + error_detail = response.json().get('detail', '') + raise requests.HTTPError(f"{e}, Detail: {error_detail}") from None + except ValueError: + # Если ответ не JSON, просто выбросить исходную ошибку + raise + diff --git a/database.py b/database.py new file mode 100644 index 0000000..f80ebb6 --- /dev/null +++ b/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///./counters.db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} # Только для SQLite +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() diff --git a/linux/create_service.sh b/linux/create_service.sh new file mode 100755 index 0000000..e64da6d --- /dev/null +++ b/linux/create_service.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Имя сервиса +SERVICE_NAME="BuildIncrementer" +APP_DIR="/opt/BuildIncrementer" +VENV_PATH="$APP_DIR/.venv" +PORT=7898 + +# Полный путь к исполняемому файлу uvicorn +EXEC_START="$VENV_PATH/bin/uvicorn main:app --host 0.0.0.0 --port $PORT" + +# Файл службы systemd +SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service" + +# Проверка, что директория приложения существует +if [ ! -d "$APP_DIR" ]; then + echo "Директория приложения не найдена: $APP_DIR" + exit 1 +fi + +# Проверка, что виртуальное окружение существует +if [ ! -d "$VENV_PATH" ]; then + echo "Виртуальное окружение не найдено: $VENV_PATH" + exit 1 +fi + +# Создание файла службы systemd +echo "Создаю файл службы systemd: $SERVICE_FILE" + +sudo bash -c "cat > $SERVICE_FILE" <