This commit is contained in:
itqop 2025-12-30 16:51:56 +03:00
parent 7e5e390eb0
commit f54ead4334
4 changed files with 157 additions and 21 deletions

31
.env.example Normal file
View File

@ -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}

95
DEPLOYMENT.md Normal file
View File

@ -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
```
Готово! 🚀

View File

@ -1,6 +1,7 @@
"""Security utilities for authentication and authorization.""" """Security utilities for authentication and authorization."""
from datetime import datetime, timedelta import hashlib
from datetime import datetime, timedelta, timezone
from typing import Optional from typing import Optional
from jose import JWTError, jwt from jose import JWTError, jwt
@ -16,15 +17,22 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str: def hash_password(password: str) -> str:
""" """
Hash a password using bcrypt. Hash a password using bcrypt with SHA256 pre-hashing.
Args: Args:
password: Plain text password password: Plain text password (any length supported)
Returns: Returns:
Hashed password 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: 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: Returns:
True if password matches, False otherwise 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: 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() to_encode = data.copy()
if expires_delta: if expires_delta:
expire = datetime.utcnow() + expires_delta expire = datetime.now(timezone.utc) + expires_delta
else: 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"}) to_encode.update({"exp": expire, "type": "access"})
encoded_jwt = jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm) 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 Encoded JWT token
""" """
to_encode = data.copy() 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"}) to_encode.update({"exp": expire, "type": "refresh"})
encoded_jwt = jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm) encoded_jwt = jwt.encode(to_encode, settings.jwt_secret, algorithm=settings.jwt_algorithm)
return encoded_jwt return encoded_jwt

View File

@ -4,17 +4,17 @@ services:
backend: backend:
build: ./backend build: ./backend
ports: ports:
- "8094:8000" - "${BACKEND_PORT:-8094}:8000"
environment: environment:
- APP_ENV=dev - APP_ENV=dev
- DATABASE_URL=sqlite+aiosqlite:////app/data/app.db - DATABASE_URL=sqlite+aiosqlite:////app/data/app.db
- S3_ENDPOINT_URL=http://minio:9000 - S3_ENDPOINT_URL=http://minio:9000
- S3_REGION=us-east-1 - S3_REGION=us-east-1
- S3_ACCESS_KEY_ID=minioadmin - S3_ACCESS_KEY_ID=${MINIO_ROOT_USER:-minioadmin}
- S3_SECRET_ACCESS_KEY=minioadmin - S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD:-minioadmin}
- MEDIA_BUCKET=itcloud-media - MEDIA_BUCKET=itcloud-media
- JWT_SECRET=dev-secret-key-change-in-production - JWT_SECRET=${JWT_SECRET:-dev-secret-key-change-in-production}
- CORS_ORIGINS=http://192.168.1.8:8095,http://localhost:8095,http://localhost:3000 - CORS_ORIGINS=${CORS_ORIGINS}
- REDIS_URL=redis://redis:6379/0 - REDIS_URL=redis://redis:6379/0
volumes: volumes:
- ./backend/src:/app/src - ./backend/src:/app/src
@ -27,13 +27,13 @@ services:
minio: minio:
image: minio/minio:latest image: minio/minio:latest
ports: ports:
- "9090:9000" - "${MINIO_API_PORT:-9090}:9000"
- "9091:9001" - "${MINIO_CONSOLE_PORT:-9091}:9001"
environment: environment:
- MINIO_ROOT_USER=minioadmin - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minioadmin}
- MINIO_ROOT_PASSWORD=minioadmin - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioadmin}
volumes: volumes:
- /mnt/data/s3:/data - ${S3_DATA_PATH:-/mnt/data/s3}:/data
command: server /data --console-address ":9001" command: server /data --console-address ":9001"
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
@ -57,7 +57,7 @@ services:
redis: redis:
image: redis:7-alpine image: redis:7-alpine
ports: ports:
- "6388:6379" - "${REDIS_PORT:-6388}:6379"
volumes: volumes:
- redis-data:/data - redis-data:/data
healthcheck: healthcheck:
@ -69,9 +69,9 @@ services:
frontend: frontend:
build: ./frontend build: ./frontend
ports: ports:
- "8095:5173" - "${FRONTEND_PORT:-8095}:5173"
environment: environment:
- VITE_API_URL=http://192.168.1.8:8094 - VITE_API_URL=${VITE_API_URL}
volumes: volumes:
- ./frontend/src:/app/src - ./frontend/src:/app/src
- ./frontend/public:/app/public - ./frontend/public:/app/public