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."""
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

View File

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