From f54ead43346ade6d519d2cdf4193475caf7acd6c Mon Sep 17 00:00:00 2001 From: itqop Date: Tue, 30 Dec 2025 16:51:56 +0300 Subject: [PATCH] fixes --- .env.example | 31 ++++++++++ DEPLOYMENT.md | 95 +++++++++++++++++++++++++++++++ backend/src/app/infra/security.py | 26 ++++++--- docker-compose.yml | 26 ++++----- 4 files changed, 157 insertions(+), 21 deletions(-) create mode 100644 .env.example create mode 100644 DEPLOYMENT.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fac39f1 --- /dev/null +++ b/.env.example @@ -0,0 +1,31 @@ +# Сервер +SERVER_IP=192.168.1.8 +# Для домена замените на: +# SERVER_IP=yourdomain.com +# FRONTEND_URL=https://yourdomain.com +# BACKEND_URL=https://api.yourdomain.com + +# Порты +FRONTEND_PORT=8095 +BACKEND_PORT=8094 +MINIO_API_PORT=9090 +MINIO_CONSOLE_PORT=9091 +REDIS_PORT=6388 + +# S3 Storage +S3_DATA_PATH=/mnt/data/s3 + +# Безопасность (ОБЯЗАТЕЛЬНО ПОМЕНЯЙТЕ В PRODUCTION!) +JWT_SECRET=dev-secret-key-change-in-production +MINIO_ROOT_USER=minioadmin +MINIO_ROOT_PASSWORD=minioadmin + +# CORS Origins (добавьте свой домен) +# Для production: +# CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com +CORS_ORIGINS=http://${SERVER_IP}:${FRONTEND_PORT},http://localhost:${FRONTEND_PORT} + +# API URL для frontend +# Для production: +# VITE_API_URL=https://api.yourdomain.com +VITE_API_URL=http://${SERVER_IP}:${BACKEND_PORT} diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..cc220f6 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,95 @@ +# Инструкция по развертыванию + +## Текущая конфигурация (разработка) + +Сейчас проект настроен для локальной сети на IP `192.168.1.8`: +- Frontend: http://192.168.1.8:8095 +- Backend: http://192.168.1.8:8094 +- MinIO Console: http://192.168.1.8:9091 + +## Переход на домен (production) + +Когда будете ставить на домен, просто отредактируйте файл `.env`: + +### Вариант 1: Домен без поддомена + +```bash +# .env +SERVER_IP=yourdomain.com +CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com +VITE_API_URL=https://yourdomain.com:8094 + +# Рекомендуется сменить порты на стандартные через nginx: +# FRONTEND_PORT=80 (или 443 для HTTPS) +# BACKEND_PORT=8080 (скрыть за nginx reverse proxy) +``` + +### Вариант 2: С поддоменом для API (рекомендуется) + +```bash +# .env +SERVER_IP=yourdomain.com +CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com,https://api.yourdomain.com +VITE_API_URL=https://api.yourdomain.com + +# Настроить nginx reverse proxy: +# yourdomain.com -> frontend:8095 +# api.yourdomain.com -> backend:8094 +``` + +### Обязательно измените в production: + +```bash +JWT_SECRET=ваш-очень-длинный-случайный-секретный-ключ +MINIO_ROOT_USER=ваш-логин +MINIO_ROOT_PASSWORD=ваш-сильный-пароль +``` + +## Настройка HTTPS (nginx + Let's Encrypt) + +Создайте `nginx.conf`: + +```nginx +# Frontend +server { + listen 80; + server_name yourdomain.com www.yourdomain.com; + + location / { + proxy_pass http://localhost:8095; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} + +# Backend API +server { + listen 80; + server_name api.yourdomain.com; + + location / { + proxy_pass http://localhost:8094; + 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; + } +} +``` + +Затем установите SSL сертификаты: + +```bash +sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com -d api.yourdomain.com +``` + +## Быстрая смена конфигурации + +1. Отредактируйте `.env` +2. Перезапустите контейнеры: + ```bash + docker-compose down + docker-compose up -d + ``` + +Готово! 🚀 diff --git a/backend/src/app/infra/security.py b/backend/src/app/infra/security.py index d354271..24523ca 100644 --- a/backend/src/app/infra/security.py +++ b/backend/src/app/infra/security.py @@ -1,6 +1,7 @@ """Security utilities for authentication and authorization.""" -from datetime import datetime, timedelta +import hashlib +from datetime import datetime, timedelta, timezone from typing import Optional from jose import JWTError, jwt @@ -16,15 +17,22 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(password: str) -> str: """ - Hash a password using bcrypt. + Hash a password using bcrypt with SHA256 pre-hashing. Args: - password: Plain text password + password: Plain text password (any length supported) Returns: Hashed password + + Note: + Uses SHA256 pre-hashing to support passwords of any length, + avoiding bcrypt's 72-byte limitation. """ - return pwd_context.hash(password) + # Pre-hash with SHA256 to support unlimited password length + # This is a common technique to work around bcrypt's 72-byte limit + password_hash = hashlib.sha256(password.encode('utf-8')).hexdigest() + return pwd_context.hash(password_hash) def verify_password(plain_password: str, hashed_password: str) -> bool: @@ -38,7 +46,9 @@ def verify_password(plain_password: str, hashed_password: str) -> bool: Returns: True if password matches, False otherwise """ - return pwd_context.verify(plain_password, hashed_password) + # Apply same SHA256 pre-hashing as hash_password + password_hash = hashlib.sha256(plain_password.encode('utf-8')).hexdigest() + return pwd_context.verify(password_hash, hashed_password) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: @@ -54,9 +64,9 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) - """ to_encode = data.copy() if expires_delta: - expire = datetime.utcnow() + expires_delta + expire = datetime.now(timezone.utc) + expires_delta else: - expire = datetime.utcnow() + timedelta(seconds=settings.jwt_access_ttl_seconds) + expire = datetime.now(timezone.utc) + timedelta(seconds=settings.jwt_access_ttl_seconds) to_encode.update({"exp": expire, "type": "access"}) encoded_jwt = jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm) @@ -74,7 +84,7 @@ def create_refresh_token(data: dict) -> str: Encoded JWT token """ to_encode = data.copy() - expire = datetime.utcnow() + timedelta(seconds=settings.jwt_refresh_ttl_seconds) + expire = datetime.now(timezone.utc) + timedelta(seconds=settings.jwt_refresh_ttl_seconds) to_encode.update({"exp": expire, "type": "refresh"}) encoded_jwt = jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm) return encoded_jwt diff --git a/docker-compose.yml b/docker-compose.yml index e4fa132..28262c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,17 +4,17 @@ services: backend: build: ./backend ports: - - "8094:8000" + - "${BACKEND_PORT:-8094}:8000" environment: - APP_ENV=dev - DATABASE_URL=sqlite+aiosqlite:////app/data/app.db - S3_ENDPOINT_URL=http://minio:9000 - S3_REGION=us-east-1 - - S3_ACCESS_KEY_ID=minioadmin - - S3_SECRET_ACCESS_KEY=minioadmin + - S3_ACCESS_KEY_ID=${MINIO_ROOT_USER:-minioadmin} + - S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD:-minioadmin} - MEDIA_BUCKET=itcloud-media - - JWT_SECRET=dev-secret-key-change-in-production - - CORS_ORIGINS=http://192.168.1.8:8095,http://localhost:8095,http://localhost:3000 + - JWT_SECRET=${JWT_SECRET:-dev-secret-key-change-in-production} + - CORS_ORIGINS=${CORS_ORIGINS} - REDIS_URL=redis://redis:6379/0 volumes: - ./backend/src:/app/src @@ -27,13 +27,13 @@ services: minio: image: minio/minio:latest ports: - - "9090:9000" - - "9091:9001" + - "${MINIO_API_PORT:-9090}:9000" + - "${MINIO_CONSOLE_PORT:-9091}:9001" environment: - - MINIO_ROOT_USER=minioadmin - - MINIO_ROOT_PASSWORD=minioadmin + - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin} + - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin} volumes: - - /mnt/data/s3:/data + - ${S3_DATA_PATH:-/mnt/data/s3}:/data command: server /data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] @@ -57,7 +57,7 @@ services: redis: image: redis:7-alpine ports: - - "6388:6379" + - "${REDIS_PORT:-6388}:6379" volumes: - redis-data:/data healthcheck: @@ -69,9 +69,9 @@ services: frontend: build: ./frontend ports: - - "8095:5173" + - "${FRONTEND_PORT:-8095}:5173" environment: - - VITE_API_URL=http://192.168.1.8:8094 + - VITE_API_URL=${VITE_API_URL} volumes: - ./frontend/src:/app/src - ./frontend/public:/app/public