fix: main & yml
This commit is contained in:
parent
2eafc574b6
commit
3928cbcd61
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,4 +1,21 @@
|
|||||||
/uploads
|
/uploads
|
||||||
/__pycache__
|
/__pycache__
|
||||||
/myenv
|
/myenv
|
||||||
imageboard.db
|
imageboard.db
|
||||||
|
**/node_modules
|
||||||
|
**/venv
|
||||||
|
**/.git
|
||||||
|
**/.github
|
||||||
|
**/logs
|
||||||
|
**/dist
|
||||||
|
**/build
|
||||||
|
*.log
|
||||||
|
*.md
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
**/__pycache__
|
||||||
|
*.sqlite3
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
25
Dockerfile
25
Dockerfile
@ -1,9 +1,22 @@
|
|||||||
# Dockerfile
|
# Используем официальный образ Python
|
||||||
FROM python:3.9-slim
|
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 \
|
WORKDIR /app
|
||||||
python3-dev \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
# Сначала копируем только зависимости
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Устанавливаем зависимости с кэшированием
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копируем остальные файлы
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Команда для запуска (замените на вашу)
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
@ -1,4 +1,4 @@
|
|||||||
version: '3.8'
|
version: "3.8"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
qdrant:
|
qdrant:
|
||||||
@ -10,38 +10,24 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- qdrant_data:/qdrant/storage
|
- qdrant_data:/qdrant/storage
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "--fail", "http://qdrant:6333"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
|
|
||||||
app:
|
backend:
|
||||||
build: .
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
container_name: backend
|
container_name: backend
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
restart: unless-stopped
|
||||||
- .:/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"
|
|
||||||
|
|
||||||
frontend:
|
futa-clone:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ./futa-clone
|
||||||
dockerfile: ./futa-clone/Dockerfile.frontend
|
dockerfile: Dockerfile
|
||||||
container_name: frontend
|
container_name: futa-clone
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
restart: unless-stopped
|
||||||
- ./futa-clone:/app
|
|
||||||
working_dir: /app
|
|
||||||
depends_on:
|
|
||||||
- qdrant
|
|
||||||
command: sh -c "npm install && npm run start"
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
qdrant_data:
|
qdrant_data:
|
||||||
|
18
futa-clone/Dockerfile
Normal file
18
futa-clone/Dockerfile
Normal file
@ -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"]
|
@ -1,4 +0,0 @@
|
|||||||
# Dockerfile.frontend
|
|
||||||
FROM node:18-alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache git
|
|
113
main.py
113
main.py
@ -10,6 +10,7 @@ import requests
|
|||||||
from qdrant_client import QdrantClient
|
from qdrant_client import QdrantClient
|
||||||
from qdrant_client.http import models
|
from qdrant_client.http import models
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
import time # Добавлен импорт модуля time
|
||||||
|
|
||||||
# Database setup
|
# Database setup
|
||||||
from sqlalchemy import create_engine, Column, String, DateTime, Text
|
from sqlalchemy import create_engine, Column, String, DateTime, Text
|
||||||
@ -26,78 +27,112 @@ import logging
|
|||||||
|
|
||||||
from qdrant_client.http.models import VectorParams, Distance
|
from qdrant_client.http.models import VectorParams, Distance
|
||||||
|
|
||||||
# Настройка базового логирования (например, вывод в консоль)
|
# Настройка базового логирования
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO, # Можно изменить уровень, например, на DEBUG
|
level=logging.INFO,
|
||||||
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
|
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
|
||||||
)
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Задайте имя коллекции
|
# Конфигурация
|
||||||
COLLECTION_NAME = "posts"
|
COLLECTION_NAME = "posts"
|
||||||
# Определите размер вектора. Этот размер должен соответствовать длине объединённого эмбеддинга текста и изображения.
|
VECTOR_SIZE = 1280
|
||||||
VECTOR_SIZE = 1280 # Пример: поменяйте на актуальное значение для вашего случая
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
DATABASE_URL = "sqlite:///./imageboard.db"
|
DATABASE_URL = "sqlite:///./imageboard.db"
|
||||||
QDRANT_URL = "http://localhost:6333"
|
QDRANT_URL = "http://localhost:6333"
|
||||||
OLLAMA_URL = "http://localhost:11434"
|
OLLAMA_URL = "http://localhost:11434"
|
||||||
EMBEDDING_MODEL = "nomic-embed-text" # Локальная модель через Ollama
|
EMBEDDING_MODEL = "nomic-embed-text"
|
||||||
IMAGE_MODEL = "openai/clip-vit-base-patch32" # Локальная CLIP модель
|
IMAGE_MODEL = "openai/clip-vit-base-patch32"
|
||||||
IMAGE_SIZE = (224, 224)
|
IMAGE_SIZE = (224, 224)
|
||||||
UPLOAD_DIR = "uploads"
|
UPLOAD_DIR = "uploads"
|
||||||
|
|
||||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
# Initialize components
|
# Инициализация компонентов
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
engine = create_engine(DATABASE_URL)
|
engine = create_engine(DATABASE_URL)
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
#Base.metadata.drop_all(bind=engine) # Удаляет все таблицы
|
# Инициализация CLIP
|
||||||
#Base.metadata.create_all(bind=engine) # Создаёт таблицы заново
|
|
||||||
|
|
||||||
# Инициализация CLIP для изображений
|
|
||||||
clip_model = CLIPModel.from_pretrained(IMAGE_MODEL)
|
clip_model = CLIPModel.from_pretrained(IMAGE_MODEL)
|
||||||
clip_processor = CLIPProcessor.from_pretrained(IMAGE_MODEL)
|
clip_processor = CLIPProcessor.from_pretrained(IMAGE_MODEL)
|
||||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
clip_model = clip_model.to(device)
|
clip_model = clip_model.to(device)
|
||||||
|
|
||||||
# Qdrant клиент
|
# Функция для создания QdrantClient с повторными попытками
|
||||||
qdrant_client = QdrantClient(QDRANT_URL)
|
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():
|
def ensure_collection_exists():
|
||||||
try:
|
max_attempts = 5
|
||||||
# Попытка получить коллекцию. Если коллекция не существует, Qdrant выбросит исключение.
|
attempt = 0
|
||||||
qdrant_client.get_collection(collection_name=COLLECTION_NAME)
|
while attempt < max_attempts:
|
||||||
logger.info("Коллекция '%s' существует.", COLLECTION_NAME)
|
try:
|
||||||
except Exception as e:
|
# Проверка существования коллекции
|
||||||
logger.info("Коллекция '%s' не найдена. Создаём коллекцию...", COLLECTION_NAME)
|
qdrant_client.get_collection(collection_name=COLLECTION_NAME)
|
||||||
qdrant_client.create_collection(
|
logger.info(f"Коллекция '{COLLECTION_NAME}' существует")
|
||||||
collection_name=COLLECTION_NAME,
|
return
|
||||||
vectors_config=VectorParams(
|
except Exception as e:
|
||||||
size=VECTOR_SIZE,
|
if "not found" in str(e).lower():
|
||||||
distance=Distance.COSINE # Или другой подходящий тип расстояния
|
logger.info(f"Создание коллекции '{COLLECTION_NAME}'...")
|
||||||
)
|
try:
|
||||||
)
|
qdrant_client.create_collection(
|
||||||
logger.info("Коллекция '%s' создана.", COLLECTION_NAME)
|
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
|
attempt += 1
|
||||||
ensure_collection_exists()
|
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 = FastAPI()
|
||||||
|
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name="uploads")
|
||||||
app.mount("/uploads", StaticFiles(directory=UPLOAD_DIR), name=UPLOAD_DIR)
|
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"], # Разрешить все источники
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"], # Разрешить все методы
|
allow_methods=["*"],
|
||||||
allow_headers=["*"], # Разрешить все заголовки
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Database models
|
# Модель базы данных
|
||||||
class Post(Base):
|
class Post(Base):
|
||||||
__tablename__ = "posts"
|
__tablename__ = "posts"
|
||||||
id = Column(String, primary_key=True, index=True)
|
id = Column(String, primary_key=True, index=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user