working version
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
libmagic1 \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libsm6 \
|
||||
libxext6 \
|
||||
libxrender1 \
|
||||
libgomp1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements first for better caching
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create uploads directory
|
||||
RUN mkdir -p /app/uploads
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
61
backend/Dockerfile.dev
Executable file
61
backend/Dockerfile.dev
Executable file
@@ -0,0 +1,61 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
LABEL maintainer="LeDiscord Team"
|
||||
LABEL version="1.0"
|
||||
LABEL description="LeDiscord Backend - Environnement Développement"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Env dev
|
||||
ENV ENVIRONMENT=development \
|
||||
PYTHONPATH=/app \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Dépendances système (minifiées)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
libmagic1 \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libsm6 \
|
||||
libxext6 \
|
||||
libxrender1 \
|
||||
libgomp1 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Requirements (cache friendly)
|
||||
COPY requirements.txt .
|
||||
|
||||
# Uvicorn[standard] apporte watchfiles pour un --reload rapide/stable en dev
|
||||
RUN pip install --no-cache-dir --upgrade pip \
|
||||
&& pip install --no-cache-dir -r requirements.txt \
|
||||
&& pip install --no-cache-dir "uvicorn[standard]"
|
||||
|
||||
# Dossiers utiles
|
||||
RUN mkdir -p /app/uploads /app/logs
|
||||
|
||||
# Code source
|
||||
COPY . .
|
||||
|
||||
# Env dev
|
||||
COPY .env.development .env
|
||||
|
||||
# Permissions
|
||||
RUN chmod -R 755 /app
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Hot-reload + respect des en-têtes proxy (utile si tu testes derrière Traefik en dev)
|
||||
# Astuce: on exclut uploads/logs du reload pour éviter les restarts inutiles
|
||||
CMD ["uvicorn", "app:app", \
|
||||
"--reload", \
|
||||
"--reload-exclude", "uploads/*", \
|
||||
"--reload-exclude", "logs/*", \
|
||||
"--host", "0.0.0.0", \
|
||||
"--port", "8000", \
|
||||
"--log-level", "debug", \
|
||||
"--proxy-headers", \
|
||||
"--forwarded-allow-ips=*"]
|
||||
75
backend/Dockerfile.prod
Executable file
75
backend/Dockerfile.prod
Executable file
@@ -0,0 +1,75 @@
|
||||
# Multi-stage build pour la production
|
||||
FROM python:3.11-slim AS builder
|
||||
|
||||
LABEL maintainer="LeDiscord Team"
|
||||
LABEL version="1.0"
|
||||
LABEL description="LeDiscord Backend - Environnement Production"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Env
|
||||
ENV ENVIRONMENT=production \
|
||||
PYTHONPATH=/app \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Dépendances de build
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Requirements
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --upgrade pip \
|
||||
&& pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# ---- Stage runtime ----
|
||||
FROM python:3.11-slim AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Env runtime
|
||||
ENV ENVIRONMENT=production \
|
||||
PYTHONPATH=/app \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Dépendances runtime
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libpq5 \
|
||||
libmagic1 \
|
||||
libgl1 \
|
||||
libglib2.0-0 \
|
||||
libsm6 \
|
||||
libxext6 \
|
||||
libxrender1 \
|
||||
libgomp1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Paquets Python depuis le builder
|
||||
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
||||
|
||||
# Dossiers appli
|
||||
RUN mkdir -p /app/uploads /app/logs
|
||||
|
||||
# Code source
|
||||
COPY . .
|
||||
|
||||
# Env prod (attention à ce que tu y mets)
|
||||
COPY .env.production .env
|
||||
|
||||
# Utilisateur non-root
|
||||
RUN groupadd -r lediscord && useradd -r -g lediscord lediscord \
|
||||
&& chown -R lediscord:lediscord /app
|
||||
USER lediscord
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Healthcheck (optionnel mais pratique)
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||||
CMD python -c "import socket; s=socket.socket(); s.settimeout(2); s.connect(('127.0.0.1',8000)); s.close()" || exit 1
|
||||
|
||||
# Démarrage uvicorn — pas de guillemets imbriqués ici
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--log-level", "info", "--proxy-headers", "--forwarded-allow-ips=*"]
|
||||
0
backend/__init__.py
Normal file → Executable file
0
backend/__init__.py
Normal file → Executable file
0
backend/api/routers/__init__.py
Normal file → Executable file
0
backend/api/routers/__init__.py
Normal file → Executable file
0
backend/api/routers/admin.py
Normal file → Executable file
0
backend/api/routers/admin.py
Normal file → Executable file
0
backend/api/routers/albums.py
Normal file → Executable file
0
backend/api/routers/albums.py
Normal file → Executable file
0
backend/api/routers/auth.py
Normal file → Executable file
0
backend/api/routers/auth.py
Normal file → Executable file
14
backend/api/routers/events.py
Normal file → Executable file
14
backend/api/routers/events.py
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
from config.database import get_db
|
||||
@@ -64,7 +64,7 @@ async def get_events(
|
||||
upcoming: bool = None
|
||||
):
|
||||
"""Get all events, optionally filtered by upcoming status."""
|
||||
query = db.query(Event)
|
||||
query = db.query(Event).options(joinedload(Event.creator))
|
||||
|
||||
if upcoming is True:
|
||||
# Only upcoming events
|
||||
@@ -83,7 +83,7 @@ async def get_upcoming_events(
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
):
|
||||
"""Get only upcoming events."""
|
||||
events = db.query(Event).filter(
|
||||
events = db.query(Event).options(joinedload(Event.creator)).filter(
|
||||
Event.date >= datetime.utcnow()
|
||||
).order_by(Event.date).all()
|
||||
return [format_event_response(event, db) for event in events]
|
||||
@@ -94,7 +94,7 @@ async def get_past_events(
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
):
|
||||
"""Get only past events."""
|
||||
events = db.query(Event).filter(
|
||||
events = db.query(Event).options(joinedload(Event.creator)).filter(
|
||||
Event.date < datetime.utcnow()
|
||||
).order_by(Event.date.desc()).all()
|
||||
return [format_event_response(event, db) for event in events]
|
||||
@@ -204,6 +204,9 @@ def format_event_response(event: Event, db: Session) -> dict:
|
||||
participations = []
|
||||
present_count = absent_count = maybe_count = pending_count = 0
|
||||
|
||||
# Get creator user directly
|
||||
creator = db.query(User).filter(User.id == event.creator_id).first()
|
||||
|
||||
for p in event.participations:
|
||||
user = db.query(User).filter(User.id == p.user_id).first()
|
||||
participations.append({
|
||||
@@ -235,7 +238,8 @@ def format_event_response(event: Event, db: Session) -> dict:
|
||||
"cover_image": event.cover_image,
|
||||
"created_at": event.created_at,
|
||||
"updated_at": event.updated_at,
|
||||
"creator_name": event.creator.full_name,
|
||||
"creator_name": creator.full_name if creator else "Unknown",
|
||||
"creator_avatar": creator.avatar_url if creator else None,
|
||||
"participations": participations,
|
||||
"present_count": present_count,
|
||||
"absent_count": absent_count,
|
||||
|
||||
0
backend/api/routers/information.py
Normal file → Executable file
0
backend/api/routers/information.py
Normal file → Executable file
0
backend/api/routers/notifications.py
Normal file → Executable file
0
backend/api/routers/notifications.py
Normal file → Executable file
0
backend/api/routers/posts.py
Normal file → Executable file
0
backend/api/routers/posts.py
Normal file → Executable file
24
backend/api/routers/settings.py
Normal file → Executable file
24
backend/api/routers/settings.py
Normal file → Executable file
@@ -40,7 +40,7 @@ async def get_upload_limits(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_admin_user)
|
||||
):
|
||||
"""Get current upload limits configuration."""
|
||||
"""Get current upload limits configuration (admin only)."""
|
||||
settings = db.query(SystemSettings).filter(
|
||||
SystemSettings.category == "uploads"
|
||||
).all()
|
||||
@@ -61,6 +61,28 @@ async def get_upload_limits(
|
||||
allowed_video_types=settings_dict.get("allowed_video_types", "video/mp4,video/mpeg,video/quicktime,video/webm").split(",")
|
||||
)
|
||||
|
||||
@router.get("/public/upload-limits", response_model=UploadLimitsResponse)
|
||||
async def get_public_upload_limits(
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get current upload limits configuration (public endpoint - no auth required)."""
|
||||
settings = db.query(SystemSettings).filter(
|
||||
SystemSettings.category == "uploads"
|
||||
).all()
|
||||
|
||||
# Convertir en dictionnaire pour faciliter l'accès
|
||||
settings_dict = {s.key: s.value for s in settings}
|
||||
|
||||
return UploadLimitsResponse(
|
||||
max_album_size_mb=int(settings_dict.get("max_album_size_mb", "100")),
|
||||
max_vlog_size_mb=int(settings_dict.get("max_vlog_size_mb", "500")),
|
||||
max_image_size_mb=int(settings_dict.get("max_image_size_mb", "10")),
|
||||
max_video_size_mb=int(settings_dict.get("max_video_size_mb", "100")),
|
||||
max_media_per_album=int(settings_dict.get("max_media_per_album", "50")),
|
||||
allowed_image_types=settings_dict.get("allowed_image_types", "image/jpeg,image/png,image/gif,image/webp").split(","),
|
||||
allowed_video_types=settings_dict.get("allowed_video_types", "video/mp4,video/mpeg,video/quicktime,video/webm").split(",")
|
||||
)
|
||||
|
||||
@router.post("/", response_model=SystemSettingResponse)
|
||||
async def create_setting(
|
||||
setting_data: SystemSettingCreate,
|
||||
|
||||
0
backend/api/routers/stats.py
Normal file → Executable file
0
backend/api/routers/stats.py
Normal file → Executable file
0
backend/api/routers/tickets.py
Normal file → Executable file
0
backend/api/routers/tickets.py
Normal file → Executable file
0
backend/api/routers/users.py
Normal file → Executable file
0
backend/api/routers/users.py
Normal file → Executable file
0
backend/api/routers/vlogs.py
Normal file → Executable file
0
backend/api/routers/vlogs.py
Normal file → Executable file
2
backend/app.py
Normal file → Executable file
2
backend/app.py
Normal file → Executable file
@@ -194,7 +194,7 @@ app = FastAPI(
|
||||
# Configure CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.CORS_ORIGINS,
|
||||
allow_origins=settings.CORS_ORIGINS_LIST,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
|
||||
0
backend/config/database.py
Normal file → Executable file
0
backend/config/database.py
Normal file → Executable file
127
backend/config/settings.py
Normal file → Executable file
127
backend/config/settings.py
Normal file → Executable file
@@ -1,37 +1,118 @@
|
||||
from typing import List
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
class Settings:
|
||||
# Database
|
||||
DATABASE_URL: str = os.getenv("DATABASE_URL", "postgresql://lediscord_user:lediscord_password@postgres:5432/lediscord")
|
||||
"""Configuration principale avec variables d'environnement obligatoires"""
|
||||
|
||||
# JWT
|
||||
JWT_SECRET_KEY: str = "your-super-secret-jwt-key-change-me"
|
||||
JWT_ALGORITHM: str = "HS256"
|
||||
JWT_EXPIRATION_MINUTES: int = 10080 # 7 days
|
||||
# Environnement - OBLIGATOIRE
|
||||
ENVIRONMENT: str = os.getenv("ENVIRONMENT")
|
||||
if not ENVIRONMENT:
|
||||
raise ValueError("ENVIRONMENT variable is required. Use .env.local, .env.development, or .env.production")
|
||||
|
||||
# Debug et reload
|
||||
DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true"
|
||||
RELOAD: bool = os.getenv("RELOAD", "false").lower() == "true"
|
||||
|
||||
# Database - OBLIGATOIRE
|
||||
DATABASE_URL: str = os.getenv("DATABASE_URL")
|
||||
if not DATABASE_URL:
|
||||
raise ValueError("DATABASE_URL variable is required")
|
||||
|
||||
# JWT - OBLIGATOIRE
|
||||
JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY")
|
||||
if not JWT_SECRET_KEY:
|
||||
raise ValueError("JWT_SECRET_KEY variable is required")
|
||||
|
||||
JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256")
|
||||
JWT_EXPIRATION_MINUTES: int = int(os.getenv("JWT_EXPIRATION_MINUTES", "10080"))
|
||||
|
||||
# Upload
|
||||
UPLOAD_PATH: str = "/app/uploads"
|
||||
MAX_UPLOAD_SIZE: int = 100 * 1024 * 1024 # 100MB
|
||||
ALLOWED_IMAGE_TYPES: List[str] = ["image/jpeg", "image/png", "image/gif", "image/webp"]
|
||||
ALLOWED_VIDEO_TYPES: List[str] = ["video/mp4", "video/mpeg", "video/quicktime", "video/webm"]
|
||||
UPLOAD_PATH: str = os.getenv("UPLOAD_PATH", "./uploads")
|
||||
MAX_UPLOAD_SIZE: int = int(os.getenv("MAX_UPLOAD_SIZE", "104857600"))
|
||||
ALLOWED_IMAGE_TYPES: List[str] = os.getenv("ALLOWED_IMAGE_TYPES", "image/jpeg,image/png,image/gif,image/webp").split(",")
|
||||
ALLOWED_VIDEO_TYPES: List[str] = os.getenv("ALLOWED_VIDEO_TYPES", "video/mp4,video/mpeg,video/quicktime,video/webm").split(",")
|
||||
|
||||
# CORS - Fixed list, no environment parsing
|
||||
CORS_ORIGINS: List[str] = ["http://localhost:5173", "http://localhost:3000"]
|
||||
# CORS - OBLIGATOIRE
|
||||
CORS_ORIGINS: str = os.getenv("CORS_ORIGINS")
|
||||
if not CORS_ORIGINS:
|
||||
raise ValueError("CORS_ORIGINS variable is required")
|
||||
CORS_ORIGINS_LIST: List[str] = CORS_ORIGINS.split(",")
|
||||
|
||||
# Email
|
||||
SMTP_HOST: str = "smtp.gmail.com"
|
||||
SMTP_PORT: int = 587
|
||||
SMTP_USER: str = ""
|
||||
SMTP_PASSWORD: str = ""
|
||||
SMTP_FROM: str = "noreply@lediscord.com"
|
||||
SMTP_HOST: str = os.getenv("SMTP_HOST", "smtp.gmail.com")
|
||||
SMTP_PORT: int = int(os.getenv("SMTP_PORT", "587"))
|
||||
SMTP_USER: str = os.getenv("SMTP_USER", "")
|
||||
SMTP_PASSWORD: str = os.getenv("SMTP_PASSWORD", "")
|
||||
SMTP_FROM: str = os.getenv("SMTP_FROM", "noreply@lediscord.com")
|
||||
|
||||
# Admin
|
||||
ADMIN_EMAIL: str = "admin@lediscord.com"
|
||||
ADMIN_PASSWORD: str = "admin123"
|
||||
# Admin - OBLIGATOIRE
|
||||
ADMIN_EMAIL: str = os.getenv("ADMIN_EMAIL")
|
||||
if not ADMIN_EMAIL:
|
||||
raise ValueError("ADMIN_EMAIL variable is required")
|
||||
|
||||
ADMIN_PASSWORD: str = os.getenv("ADMIN_PASSWORD")
|
||||
if not ADMIN_PASSWORD:
|
||||
raise ValueError("ADMIN_PASSWORD variable is required")
|
||||
|
||||
# App
|
||||
APP_NAME: str = "LeDiscord"
|
||||
APP_URL: str = "http://localhost:5173"
|
||||
APP_NAME: str = os.getenv("APP_NAME", "LeDiscord")
|
||||
APP_URL: str = os.getenv("APP_URL", "http://localhost:5173")
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO" if ENVIRONMENT == "production" else "DEBUG")
|
||||
|
||||
# Performance
|
||||
WORKERS: int = int(os.getenv("WORKERS", "1" if ENVIRONMENT in ["local", "development"] else "4"))
|
||||
|
||||
# Sécurité (production uniquement)
|
||||
SECURE_COOKIES: bool = os.getenv("SECURE_COOKIES", "false").lower() == "true"
|
||||
SECURE_HEADERS: bool = os.getenv("SECURE_HEADERS", "false").lower() == "true"
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Représentation lisible de la configuration"""
|
||||
return f"""
|
||||
🔧 Configuration LeDiscord - Environnement: {self.ENVIRONMENT.upper()}
|
||||
📊 Debug: {self.DEBUG}
|
||||
🔄 Reload: {self.RELOAD}
|
||||
🌐 CORS Origins: {', '.join(self.CORS_ORIGINS_LIST)}
|
||||
🗄️ Database: {self.DATABASE_URL.split('@')[1] if '@' in self.DATABASE_URL else 'Unknown'}
|
||||
📁 Upload Path: {self.UPLOAD_PATH}
|
||||
📝 Log Level: {self.LOG_LEVEL}
|
||||
🚀 Workers: {self.WORKERS}
|
||||
"""
|
||||
|
||||
def validate(self) -> bool:
|
||||
"""Valide la configuration"""
|
||||
required_vars = [
|
||||
"ENVIRONMENT",
|
||||
"DATABASE_URL",
|
||||
"JWT_SECRET_KEY",
|
||||
"CORS_ORIGINS",
|
||||
"ADMIN_EMAIL",
|
||||
"ADMIN_PASSWORD"
|
||||
]
|
||||
|
||||
for var in required_vars:
|
||||
if not getattr(self, var):
|
||||
print(f"❌ Configuration invalide: {var} est manquant")
|
||||
return False
|
||||
|
||||
print("✅ Configuration validée avec succès")
|
||||
return True
|
||||
|
||||
settings = Settings()
|
||||
# Instance globale
|
||||
try:
|
||||
settings = Settings()
|
||||
except ValueError as e:
|
||||
print(f"❌ Erreur de configuration: {e}")
|
||||
print("💡 Assurez-vous d'utiliser un fichier d'environnement spécifique:")
|
||||
print(" - .env.local pour le développement local")
|
||||
print(" - .env.development pour l'environnement de développement")
|
||||
print(" - .env.production pour la production")
|
||||
raise
|
||||
|
||||
# Validation automatique au démarrage
|
||||
if __name__ == "__main__":
|
||||
print(settings)
|
||||
settings.validate()
|
||||
|
||||
0
backend/models/__init__.py
Normal file → Executable file
0
backend/models/__init__.py
Normal file → Executable file
0
backend/models/album.py
Normal file → Executable file
0
backend/models/album.py
Normal file → Executable file
0
backend/models/event.py
Normal file → Executable file
0
backend/models/event.py
Normal file → Executable file
0
backend/models/information.py
Normal file → Executable file
0
backend/models/information.py
Normal file → Executable file
0
backend/models/notification.py
Normal file → Executable file
0
backend/models/notification.py
Normal file → Executable file
0
backend/models/post.py
Normal file → Executable file
0
backend/models/post.py
Normal file → Executable file
0
backend/models/settings.py
Normal file → Executable file
0
backend/models/settings.py
Normal file → Executable file
0
backend/models/ticket.py
Normal file → Executable file
0
backend/models/ticket.py
Normal file → Executable file
0
backend/models/user.py
Normal file → Executable file
0
backend/models/user.py
Normal file → Executable file
0
backend/models/vlog.py
Normal file → Executable file
0
backend/models/vlog.py
Normal file → Executable file
0
backend/requirements.txt
Normal file → Executable file
0
backend/requirements.txt
Normal file → Executable file
0
backend/schemas/__init__.py
Normal file → Executable file
0
backend/schemas/__init__.py
Normal file → Executable file
0
backend/schemas/album.py
Normal file → Executable file
0
backend/schemas/album.py
Normal file → Executable file
1
backend/schemas/event.py
Normal file → Executable file
1
backend/schemas/event.py
Normal file → Executable file
@@ -40,6 +40,7 @@ class EventResponse(EventBase):
|
||||
id: int
|
||||
creator_id: int
|
||||
creator_name: str
|
||||
creator_avatar: Optional[str] = None
|
||||
cover_image: Optional[str]
|
||||
created_at: datetime
|
||||
participations: List[ParticipationResponse] = []
|
||||
|
||||
0
backend/schemas/information.py
Normal file → Executable file
0
backend/schemas/information.py
Normal file → Executable file
0
backend/schemas/notification.py
Normal file → Executable file
0
backend/schemas/notification.py
Normal file → Executable file
0
backend/schemas/post.py
Normal file → Executable file
0
backend/schemas/post.py
Normal file → Executable file
0
backend/schemas/settings.py
Normal file → Executable file
0
backend/schemas/settings.py
Normal file → Executable file
0
backend/schemas/ticket.py
Normal file → Executable file
0
backend/schemas/ticket.py
Normal file → Executable file
0
backend/schemas/user.py
Normal file → Executable file
0
backend/schemas/user.py
Normal file → Executable file
0
backend/schemas/vlog.py
Normal file → Executable file
0
backend/schemas/vlog.py
Normal file → Executable file
84
backend/test_config.py
Executable file
84
backend/test_config.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script de test pour vérifier la configuration des environnements
|
||||
Usage: python test_config.py [local|development|production]
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def test_environment(env_name: str):
|
||||
"""Teste la configuration d'un environnement spécifique"""
|
||||
print(f"🔍 Test de la configuration {env_name.upper()}")
|
||||
print("=" * 50)
|
||||
|
||||
# Définir l'environnement
|
||||
os.environ["ENVIRONMENT"] = env_name
|
||||
|
||||
try:
|
||||
# Importer la configuration
|
||||
from config.settings import settings
|
||||
|
||||
print(f"✅ Configuration chargée avec succès")
|
||||
print(f" Environnement détecté: {settings.ENVIRONMENT}")
|
||||
print(f" Debug: {settings.DEBUG}")
|
||||
print(f" Reload: {settings.RELOAD}")
|
||||
print(f" Log Level: {settings.LOG_LEVEL}")
|
||||
print(f" Workers: {settings.WORKERS}")
|
||||
print(f" CORS Origins: {', '.join(settings.CORS_ORIGINS)}")
|
||||
print(f" Upload Path: {settings.UPLOAD_PATH}")
|
||||
print(f" Database: {settings.DATABASE_URL.split('@')[1] if '@' in settings.DATABASE_URL else 'Unknown'}")
|
||||
|
||||
# Validation
|
||||
if settings.validate():
|
||||
print(f"✅ Configuration {env_name} validée avec succès")
|
||||
else:
|
||||
print(f"❌ Configuration {env_name} invalide")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur lors du chargement de la configuration {env_name}: {e}")
|
||||
return False
|
||||
|
||||
print()
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""Fonction principale"""
|
||||
print("🚀 Test de configuration LeDiscord Backend")
|
||||
print("=" * 60)
|
||||
|
||||
# Vérifier que nous sommes dans le bon répertoire
|
||||
if not Path("config/settings.py").exists():
|
||||
print("❌ Erreur: Ce script doit être exécuté depuis le répertoire backend/")
|
||||
sys.exit(1)
|
||||
|
||||
# Test des environnements
|
||||
environments = ["local", "development", "production"]
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
env_arg = sys.argv[1].lower()
|
||||
if env_arg in environments:
|
||||
environments = [env_arg]
|
||||
else:
|
||||
print(f"❌ Environnement invalide: {env_arg}")
|
||||
print(f" Environnements valides: {', '.join(environments)}")
|
||||
sys.exit(1)
|
||||
|
||||
success_count = 0
|
||||
for env in environments:
|
||||
if test_environment(env):
|
||||
success_count += 1
|
||||
|
||||
print("=" * 60)
|
||||
print(f"📊 Résultats: {success_count}/{len(environments)} configurations valides")
|
||||
|
||||
if success_count == len(environments):
|
||||
print("🎉 Toutes les configurations sont valides !")
|
||||
return 0
|
||||
else:
|
||||
print("⚠️ Certaines configurations ont des problèmes")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
0
backend/utils/email.py
Normal file → Executable file
0
backend/utils/email.py
Normal file → Executable file
0
backend/utils/init_db.py
Normal file → Executable file
0
backend/utils/init_db.py
Normal file → Executable file
0
backend/utils/notification_service.py
Normal file → Executable file
0
backend/utils/notification_service.py
Normal file → Executable file
0
backend/utils/security.py
Normal file → Executable file
0
backend/utils/security.py
Normal file → Executable file
0
backend/utils/settings_service.py
Normal file → Executable file
0
backend/utils/settings_service.py
Normal file → Executable file
0
backend/utils/video_utils.py
Normal file → Executable file
0
backend/utils/video_utils.py
Normal file → Executable file
Reference in New Issue
Block a user