From 3928cbcd61d5dbf565cba012e54b9302aa9464ca Mon Sep 17 00:00:00 2001 From: KofK Date: Sat, 15 Feb 2025 13:52:05 +0300 Subject: [PATCH] fix: main & yml --- .gitignore | 19 +++++- Dockerfile | 25 ++++++-- docker-compose.yml | 38 ++++------- futa-clone/Dockerfile | 18 ++++++ futa-clone/Dockerfile.frontend | 4 -- main.py | 113 +++++++++++++++++++++------------ 6 files changed, 141 insertions(+), 76 deletions(-) create mode 100644 futa-clone/Dockerfile delete mode 100644 futa-clone/Dockerfile.frontend diff --git a/.gitignore b/.gitignore index 358c20e..577f9d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,21 @@ /uploads /__pycache__ /myenv -imageboard.db \ No newline at end of file +imageboard.db +**/node_modules +**/venv +**/.git +**/.github +**/logs +**/dist +**/build +*.log +*.md +.DS_Store +.env +**/__pycache__ +*.sqlite3 +*.pyc +*.pyo +*.pyd +.Python \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index dfea433..124b4cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,22 @@ -# Dockerfile +# Используем официальный образ Python FROM python:3.9-slim -ENV PYTHONUNBUFFERED 1 +# Устанавливаем системные зависимости +RUN apt-get update && \ + apt-get install -y --no-install-recommends gcc python3-dev && \ + rm -rf /var/lib/apt/lists/* -RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc \ - python3-dev \ - && rm -rf /var/lib/apt/lists/* \ No newline at end of file +# Рабочая директория +WORKDIR /app + +# Сначала копируем только зависимости +COPY requirements.txt . + +# Устанавливаем зависимости с кэшированием +RUN pip install --no-cache-dir -r requirements.txt + +# Копируем остальные файлы +COPY . . + +# Команда для запуска (замените на вашу) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b30d2ea..477f587 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: "3.8" services: qdrant: @@ -10,38 +10,24 @@ services: volumes: - qdrant_data:/qdrant/storage restart: unless-stopped - healthcheck: - test: ["CMD", "curl", "--fail", "http://qdrant:6333"] - interval: 30s - timeout: 10s - retries: 3 - app: - build: . + backend: + build: + context: . + dockerfile: Dockerfile container_name: backend ports: - "8000:8000" - volumes: - - .:/app - working_dir: /app - depends_on: - qdrant: - condition: service_healthy - command: sh -c "pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0 --port 8000 --reload" + restart: unless-stopped - frontend: + futa-clone: build: - context: . - dockerfile: ./futa-clone/Dockerfile.frontend - container_name: frontend + context: ./futa-clone + dockerfile: Dockerfile + container_name: futa-clone ports: - "3000:3000" - volumes: - - ./futa-clone:/app - working_dir: /app - depends_on: - - qdrant - command: sh -c "npm install && npm run start" + restart: unless-stopped volumes: - qdrant_data: \ No newline at end of file + qdrant_data: diff --git a/futa-clone/Dockerfile b/futa-clone/Dockerfile new file mode 100644 index 0000000..a5bb3c6 --- /dev/null +++ b/futa-clone/Dockerfile @@ -0,0 +1,18 @@ +# Используем официальный Node.js образ (на примере версии 16 на базе Alpine) +FROM node:20-alpine + +# Устанавливаем рабочую директорию внутри контейнера +WORKDIR /app + +# Копируем файлы зависимостей и устанавливаем их +COPY package*.json ./ +RUN npm install + +# Копируем исходный код приложения +COPY . . + +# Если требуется, можно указать порт (например, 3000) +EXPOSE 3000 + +# Запускаем приложение +CMD ["npm", "run", "start"] diff --git a/futa-clone/Dockerfile.frontend b/futa-clone/Dockerfile.frontend deleted file mode 100644 index c2aa7e5..0000000 --- a/futa-clone/Dockerfile.frontend +++ /dev/null @@ -1,4 +0,0 @@ -# Dockerfile.frontend -FROM node:18-alpine - -RUN apk add --no-cache git \ No newline at end of file diff --git a/main.py b/main.py index d5ab379..e2cf5e0 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ import requests from qdrant_client import QdrantClient from qdrant_client.http import models from fastapi.middleware.cors import CORSMiddleware +import time # Добавлен импорт модуля time # Database setup from sqlalchemy import create_engine, Column, String, DateTime, Text @@ -26,78 +27,112 @@ import logging from qdrant_client.http.models import VectorParams, Distance -# Настройка базового логирования (например, вывод в консоль) +# Настройка базового логирования logging.basicConfig( - level=logging.INFO, # Можно изменить уровень, например, на DEBUG + level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s" ) logger = logging.getLogger(__name__) -# Задайте имя коллекции +# Конфигурация COLLECTION_NAME = "posts" -# Определите размер вектора. Этот размер должен соответствовать длине объединённого эмбеддинга текста и изображения. -VECTOR_SIZE = 1280 # Пример: поменяйте на актуальное значение для вашего случая - -# Configuration +VECTOR_SIZE = 1280 DATABASE_URL = "sqlite:///./imageboard.db" QDRANT_URL = "http://localhost:6333" OLLAMA_URL = "http://localhost:11434" -EMBEDDING_MODEL = "nomic-embed-text" # Локальная модель через Ollama -IMAGE_MODEL = "openai/clip-vit-base-patch32" # Локальная CLIP модель +EMBEDDING_MODEL = "nomic-embed-text" +IMAGE_MODEL = "openai/clip-vit-base-patch32" IMAGE_SIZE = (224, 224) UPLOAD_DIR = "uploads" os.makedirs(UPLOAD_DIR, exist_ok=True) -# Initialize components +# Инициализация компонентов Base = declarative_base() engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -#Base.metadata.drop_all(bind=engine) # Удаляет все таблицы -#Base.metadata.create_all(bind=engine) # Создаёт таблицы заново - -# Инициализация CLIP для изображений +# Инициализация CLIP clip_model = CLIPModel.from_pretrained(IMAGE_MODEL) clip_processor = CLIPProcessor.from_pretrained(IMAGE_MODEL) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") clip_model = clip_model.to(device) -# Qdrant клиент -qdrant_client = QdrantClient(QDRANT_URL) +# Функция для создания QdrantClient с повторными попытками +def create_qdrant_client(): + max_attempts = 5 + attempt = 0 + while attempt < max_attempts: + try: + client = QdrantClient(QDRANT_URL) + # Проверка подключения + client.get_collections() + logger.info("Успешное подключение к Qdrant") + return client + except Exception as e: + logger.warning(f"Попытка {attempt+1} подключения к Qdrant не удалась: {str(e)}") + attempt += 1 + time.sleep(2) + raise RuntimeError(f"Не удалось подключиться к Qdrant после {max_attempts} попыток") +# Инициализация клиента Qdrant +try: + qdrant_client = create_qdrant_client() +except Exception as e: + logger.error(f"Ошибка инициализации Qdrant: {str(e)}") + raise + +# Функция проверки и создания коллекции def ensure_collection_exists(): - try: - # Попытка получить коллекцию. Если коллекция не существует, Qdrant выбросит исключение. - qdrant_client.get_collection(collection_name=COLLECTION_NAME) - logger.info("Коллекция '%s' существует.", COLLECTION_NAME) - except Exception as e: - logger.info("Коллекция '%s' не найдена. Создаём коллекцию...", COLLECTION_NAME) - qdrant_client.create_collection( - collection_name=COLLECTION_NAME, - vectors_config=VectorParams( - size=VECTOR_SIZE, - distance=Distance.COSINE # Или другой подходящий тип расстояния - ) - ) - logger.info("Коллекция '%s' создана.", COLLECTION_NAME) + max_attempts = 5 + attempt = 0 + while attempt < max_attempts: + try: + # Проверка существования коллекции + qdrant_client.get_collection(collection_name=COLLECTION_NAME) + logger.info(f"Коллекция '{COLLECTION_NAME}' существует") + return + except Exception as e: + if "not found" in str(e).lower(): + logger.info(f"Создание коллекции '{COLLECTION_NAME}'...") + try: + qdrant_client.create_collection( + collection_name=COLLECTION_NAME, + vectors_config=VectorParams( + size=VECTOR_SIZE, + distance=Distance.COSINE + ) + ) + logger.info(f"Коллекция '{COLLECTION_NAME}' создана") + return + except Exception as create_error: + logger.error(f"Ошибка создания: {str(create_error)}") + else: + logger.error(f"Ошибка подключения: {str(e)}") -# Вызываем функцию при инициализации приложения, например, в начале main.py -ensure_collection_exists() + attempt += 1 + time.sleep(2) + raise RuntimeError(f"Не удалось инициализировать коллекцию после {max_attempts} попыток") +# Вызов функции проверки коллекции +try: + ensure_collection_exists() +except Exception as e: + logger.error(f"Ошибка инициализации коллекции: {str(e)}") + raise + +# Инициализация FastAPI app = FastAPI() - -app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name=UPLOAD_DIR) - +app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads") app.add_middleware( CORSMiddleware, - allow_origins=["*"], # Разрешить все источники + allow_origins=["*"], allow_credentials=True, - allow_methods=["*"], # Разрешить все методы - allow_headers=["*"], # Разрешить все заголовки + allow_methods=["*"], + allow_headers=["*"], ) -# Database models +# Модель базы данных class Post(Base): __tablename__ = "posts" id = Column(String, primary_key=True, index=True)