update
Browse files- backend/.env.production +43 -0
- backend/.gitignore +75 -0
- backend/Dockerfile +38 -9
- backend/README.md +28 -184
- backend/apps/users/management/commands/create_superuser.py +80 -0
- backend/educonnect/settings.py +25 -4
- frontend/.env.production +9 -0
- frontend/.gitignore +29 -17
- frontend/Dockerfile +25 -7
- frontend/README.md +28 -13
- frontend/nginx.conf +22 -0
- frontend/services/api.ts +1 -1
- frontend/vite.config.ts +2 -1
backend/.env.production
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ==============================================
|
| 2 |
+
# CONFIGURATION PRODUCTION - HUGGING FACE SPACES
|
| 3 |
+
# ==============================================
|
| 4 |
+
|
| 5 |
+
# Django Settings
|
| 6 |
+
DEBUG=False
|
| 7 |
+
SECRET_KEY=django-insecure-xdNTqmUzzdJbmHZ8fLr6B6v1NfShmDZM
|
| 8 |
+
ALLOWED_HOSTS=localhost,127.0.0.1,.hf.space
|
| 9 |
+
|
| 10 |
+
# CORS - Autoriser le frontend Hugging Face
|
| 11 |
+
CORS_ALLOWED_ORIGINS=https://rinogeek-edulabfrontend.hf.space
|
| 12 |
+
|
| 13 |
+
# CSRF - Autoriser les origines de confiance
|
| 14 |
+
CSRF_TRUSTED_ORIGINS=https://rinogeek-edulabfrontend.hf.space,https://rinogeek-edulabbackend.hf.space
|
| 15 |
+
|
| 16 |
+
# Database - SQLite par défaut (pas de DB_HOST = utilise SQLite)
|
| 17 |
+
# Pour PostgreSQL, décommentez et configurez :
|
| 18 |
+
# DB_NAME=educonnect_db
|
| 19 |
+
# DB_USER=postgres
|
| 20 |
+
# DB_PASSWORD=your_db_password
|
| 21 |
+
# DB_HOST=your_db_host
|
| 22 |
+
# DB_PORT=5432
|
| 23 |
+
|
| 24 |
+
# Redis (pour Celery et Channels) - Désactivé sur HF Spaces gratuit
|
| 25 |
+
# REDIS_HOST=localhost
|
| 26 |
+
# CELERY_BROKER_URL=redis://localhost:6379/0
|
| 27 |
+
# CELERY_RESULT_BACKEND=redis://localhost:6379/0
|
| 28 |
+
|
| 29 |
+
# Email Configuration
|
| 30 |
+
EMAIL_HOST=smtp.gmail.com
|
| 31 |
+
EMAIL_PORT=587
|
| 32 |
+
EMAIL_HOST_USER=
|
| 33 |
+
EMAIL_HOST_PASSWORD=
|
| 34 |
+
DEFAULT_FROM_EMAIL=noreply@edulab.africa
|
| 35 |
+
|
| 36 |
+
# AI APIs
|
| 37 |
+
GEMINI_API_KEY=AIzaSyD-hnYdbQudiK8nfFtML05Mmlo26nLzxWQ
|
| 38 |
+
|
| 39 |
+
# Logging
|
| 40 |
+
DJANGO_LOG_LEVEL=INFO
|
| 41 |
+
|
| 42 |
+
# Security - HF gère SSL au niveau proxy
|
| 43 |
+
SECURE_SSL_REDIRECT=False
|
backend/.gitignore
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
*.egg-info/
|
| 24 |
+
.installed.cfg
|
| 25 |
+
*.egg
|
| 26 |
+
|
| 27 |
+
# Django stuff:
|
| 28 |
+
*.log
|
| 29 |
+
*.pot
|
| 30 |
+
*.pyc
|
| 31 |
+
local_settings.py
|
| 32 |
+
db.sqlite3
|
| 33 |
+
db.sqlite3-journal
|
| 34 |
+
|
| 35 |
+
# Media files
|
| 36 |
+
media/
|
| 37 |
+
|
| 38 |
+
# Static files (collectstatic output)
|
| 39 |
+
staticfiles/
|
| 40 |
+
|
| 41 |
+
# Environment
|
| 42 |
+
.env
|
| 43 |
+
.env.local
|
| 44 |
+
venv/
|
| 45 |
+
ENV/
|
| 46 |
+
env/
|
| 47 |
+
.venv/
|
| 48 |
+
|
| 49 |
+
# IDE
|
| 50 |
+
.idea/
|
| 51 |
+
.vscode/
|
| 52 |
+
*.swp
|
| 53 |
+
*.swo
|
| 54 |
+
|
| 55 |
+
# Logs
|
| 56 |
+
logs/
|
| 57 |
+
*.log
|
| 58 |
+
|
| 59 |
+
# OS
|
| 60 |
+
.DS_Store
|
| 61 |
+
Thumbs.db
|
| 62 |
+
|
| 63 |
+
# Celery
|
| 64 |
+
celerybeat-schedule
|
| 65 |
+
celerybeat.pid
|
| 66 |
+
|
| 67 |
+
# Coverage
|
| 68 |
+
.coverage
|
| 69 |
+
htmlcov/
|
| 70 |
+
|
| 71 |
+
# pytest
|
| 72 |
+
.pytest_cache/
|
| 73 |
+
|
| 74 |
+
# mypy
|
| 75 |
+
.mypy_cache/
|
backend/Dockerfile
CHANGED
|
@@ -12,20 +12,49 @@ RUN apt-get update && apt-get install -y \
|
|
| 12 |
libpq-dev \
|
| 13 |
&& rm -rf /var/lib/apt/lists/*
|
| 14 |
|
| 15 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
COPY requirements.txt /app/
|
| 17 |
-
RUN pip install --upgrade pip
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
# Copier le
|
| 21 |
-
|
| 22 |
|
| 23 |
-
#
|
| 24 |
-
RUN
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
# Collecter les fichiers statiques
|
| 27 |
RUN python manage.py collectstatic --noinput
|
| 28 |
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
| 12 |
libpq-dev \
|
| 13 |
&& rm -rf /var/lib/apt/lists/*
|
| 14 |
|
| 15 |
+
# Créer un utilisateur non-root pour Hugging Face Spaces (UID 1000)
|
| 16 |
+
RUN useradd -m -u 1000 user
|
| 17 |
+
|
| 18 |
+
# Créer les dossiers nécessaires avec les bonnes permissions AVANT de changer d'utilisateur
|
| 19 |
+
RUN mkdir -p /app/logs /app/media /app/staticfiles && \
|
| 20 |
+
chown -R user:user /app
|
| 21 |
+
|
| 22 |
+
# Copier requirements et installer les dépendances (en tant que root pour pip)
|
| 23 |
COPY requirements.txt /app/
|
| 24 |
+
RUN pip install --no-cache-dir --upgrade pip && \
|
| 25 |
+
pip install --no-cache-dir -r requirements.txt
|
| 26 |
+
|
| 27 |
+
# Maintenant passer à l'utilisateur non-root
|
| 28 |
+
USER user
|
| 29 |
+
ENV PATH="/home/user/.local/bin:${PATH}"
|
| 30 |
+
|
| 31 |
+
# Copier le reste du projet avec les bonnes permissions
|
| 32 |
+
COPY --chown=user:user . /app/
|
| 33 |
|
| 34 |
+
# Copier le fichier .env.production comme .env pour la production
|
| 35 |
+
RUN cp /app/.env.production /app/.env || true
|
| 36 |
|
| 37 |
+
# Appliquer les migrations de base de données
|
| 38 |
+
RUN python manage.py migrate --noinput
|
| 39 |
+
|
| 40 |
+
# Initialiser les données par défaut (social links, stats, tools, testimonials, badges, opportunities)
|
| 41 |
+
RUN python manage.py init_data
|
| 42 |
+
|
| 43 |
+
RUN python manage.py create_superuser --email admin@edulab.africa --password rinogeek --name "Rino Admin"
|
| 44 |
|
| 45 |
# Collecter les fichiers statiques
|
| 46 |
RUN python manage.py collectstatic --noinput
|
| 47 |
|
| 48 |
+
RUN echo '#!/bin/bash\n\
|
| 49 |
+
echo "=== Application Startup ===" \n\
|
| 50 |
+
python manage.py migrate --noinput\n\
|
| 51 |
+
python manage.py init_data\n\
|
| 52 |
+
python manage.py create_superuser --email admin@edulab.africa --password rinogeek --name "Rino Admin"\n\
|
| 53 |
+
exec gunicorn --bind 0.0.0.0:7860 --workers 2 --timeout 120 educonnect.wsgi:application\n\
|
| 54 |
+
' > /app/start.sh && chmod +x /app/start.sh
|
| 55 |
+
|
| 56 |
+
# Exposer le port par défaut de Hugging Face Spaces
|
| 57 |
+
EXPOSE 7860
|
| 58 |
|
| 59 |
+
# Démarrer avec le script
|
| 60 |
+
CMD ["/app/start.sh"]
|
backend/README.md
CHANGED
|
@@ -1,194 +1,38 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
- **Django 4.2** + **Django REST Framework 3.14**
|
| 8 |
-
- **PostgreSQL 15** (Base de données)
|
| 9 |
-
- **Redis** (Cache & Celery)
|
| 10 |
-
- **Channels** (WebSockets pour le chat)
|
| 11 |
-
- **JWT** (Authentification)
|
| 12 |
-
- **Celery** (Tâches asynchrones)
|
| 13 |
-
- **Docker** (Containerisation)
|
| 14 |
-
|
| 15 |
-
## 📋 Prérequis
|
| 16 |
-
|
| 17 |
-
- Python 3.11+
|
| 18 |
-
- PostgreSQL 15+
|
| 19 |
-
- Redis 7+
|
| 20 |
-
- Docker & Docker Compose (optionnel)
|
| 21 |
-
|
| 22 |
-
## ⚙️ Installation
|
| 23 |
-
|
| 24 |
-
### Méthode 1: Installation locale
|
| 25 |
-
|
| 26 |
-
1. **Cloner le projet**
|
| 27 |
-
```bash
|
| 28 |
-
git clone <repo-url>
|
| 29 |
-
cd educonnect-api
|
| 30 |
-
```
|
| 31 |
-
|
| 32 |
-
2. **Créer un environnement virtuel**
|
| 33 |
-
```bash
|
| 34 |
-
python -m venv venv
|
| 35 |
-
source venv/bin/activate # Linux/Mac
|
| 36 |
-
# ou
|
| 37 |
-
venv\\Scripts\\activate # Windows
|
| 38 |
-
```
|
| 39 |
-
|
| 40 |
-
3. **Installer les dépendances**
|
| 41 |
-
```bash
|
| 42 |
-
pip install -r requirements.txt
|
| 43 |
-
```
|
| 44 |
-
|
| 45 |
-
4. **Configuration**
|
| 46 |
-
```bash
|
| 47 |
-
cp .env.example .env
|
| 48 |
-
# Éditer .env avec vos paramètres
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
5. **Créer la base de données**
|
| 52 |
-
```bash
|
| 53 |
-
createdb educonnect_db
|
| 54 |
-
```
|
| 55 |
-
|
| 56 |
-
6. **Migrations**
|
| 57 |
-
```bash
|
| 58 |
-
python manage.py makemigrations
|
| 59 |
-
python manage.py migrate
|
| 60 |
-
```
|
| 61 |
-
|
| 62 |
-
7. **Initialiser les données**
|
| 63 |
-
```bash
|
| 64 |
-
python manage.py init_db
|
| 65 |
-
python manage.py create_test_data --users 20
|
| 66 |
-
```
|
| 67 |
-
|
| 68 |
-
8. **Créer un superuser**
|
| 69 |
-
```bash
|
| 70 |
-
python manage.py createsuperuser
|
| 71 |
-
```
|
| 72 |
-
|
| 73 |
-
9. **Lancer le serveur**
|
| 74 |
-
```bash
|
| 75 |
-
python manage.py runserver
|
| 76 |
-
```
|
| 77 |
-
|
| 78 |
-
### Méthode 2: Avec Docker
|
| 79 |
-
|
| 80 |
-
```bash
|
| 81 |
-
docker-compose up --build
|
| 82 |
-
docker-compose exec web python manage.py migrate
|
| 83 |
-
docker-compose exec web python manage.py init_db
|
| 84 |
-
docker-compose exec web python manage.py createsuperuser
|
| 85 |
-
```
|
| 86 |
-
|
| 87 |
-
## 📚 Documentation API
|
| 88 |
|
| 89 |
-
|
| 90 |
-
- **Swagger UI**: http://localhost:8000/api/docs/
|
| 91 |
-
- **ReDoc**: http://localhost:8000/api/redoc/
|
| 92 |
-
- **Admin Django**: http://localhost:8000/admin/
|
| 93 |
-
|
| 94 |
-
## 🔑 Endpoints Principaux
|
| 95 |
-
|
| 96 |
-
### Authentification
|
| 97 |
-
- `POST /api/auth/register/` - Inscription
|
| 98 |
-
- `POST /api/auth/login/` - Connexion
|
| 99 |
-
- `GET /api/auth/me/` - Profil actuel
|
| 100 |
-
- `PATCH /api/auth/update_profile/` - Mise à jour profil
|
| 101 |
-
|
| 102 |
-
### Mentors
|
| 103 |
-
- `GET /api/mentors/` - Liste des mentors
|
| 104 |
-
- `GET /api/mentors/{id}/` - Détail mentor
|
| 105 |
-
- `PATCH /api/mentors/my_profile/` - Mise à jour profil mentor
|
| 106 |
-
|
| 107 |
-
### Forum
|
| 108 |
-
- `GET /api/forum/questions/` - Liste questions
|
| 109 |
-
- `POST /api/forum/questions/` - Créer question
|
| 110 |
-
- `POST /api/forum/questions/{id}/vote/` - Voter
|
| 111 |
-
- `POST /api/forum/questions/{id}/answers/` - Répondre
|
| 112 |
-
|
| 113 |
-
### Réservations
|
| 114 |
-
- `POST /api/bookings/` - Créer réservation
|
| 115 |
-
- `GET /api/bookings/mentor_requests/` - Demandes reçues
|
| 116 |
-
- `PATCH /api/bookings/{id}/update_status/` - Accepter/Refuser
|
| 117 |
-
|
| 118 |
-
### Gamification
|
| 119 |
-
- `GET /api/gamification/leaderboard/` - Classement
|
| 120 |
-
- `GET /api/gamification/my_badges/` - Mes badges
|
| 121 |
-
- `GET /api/gamification/stats/` - Statistiques
|
| 122 |
-
|
| 123 |
-
### Messagerie
|
| 124 |
-
- `GET /api/messages/conversations/` - Conversations
|
| 125 |
-
- `POST /api/messages/conversations/{id}/send_message/` - Envoyer message
|
| 126 |
-
- WebSocket: `ws://localhost:8001/ws/chat/{conversation_id}/`
|
| 127 |
-
|
| 128 |
-
### Notifications
|
| 129 |
-
- `GET /api/notifications/` - Mes notifications
|
| 130 |
-
- `POST /api/notifications/mark_all_read/` - Tout marquer lu
|
| 131 |
-
|
| 132 |
-
### Opportunités
|
| 133 |
-
- `GET /api/opportunities/` - Liste opportunités
|
| 134 |
-
|
| 135 |
-
### IA
|
| 136 |
-
- `POST /api/ai/tutor/` - Tuteur IA
|
| 137 |
-
|
| 138 |
-
## 🧪 Tests
|
| 139 |
-
|
| 140 |
-
```bash
|
| 141 |
-
pytest
|
| 142 |
-
pytest --cov=apps
|
| 143 |
-
```
|
| 144 |
-
|
| 145 |
-
## 🏗️ Structure du Projet
|
| 146 |
-
|
| 147 |
-
```
|
| 148 |
-
educonnect-api/
|
| 149 |
-
├── apps/
|
| 150 |
-
│ ├── users/ # Authentification & Utilisateurs
|
| 151 |
-
│ ├── mentors/ # Gestion mentors
|
| 152 |
-
│ ├── bookings/ # Système de réservation
|
| 153 |
-
│ ├── forum/ # Questions & Réponses
|
| 154 |
-
│ ├── gamification/ # Points & Badges
|
| 155 |
-
│ ├── messaging/ # Chat en temps réel
|
| 156 |
-
│ ├── notifications/ # Notifications
|
| 157 |
-
│ ├── opportunities/ # Opportunités
|
| 158 |
-
│ ├── ai_tools/ # Tuteur IA
|
| 159 |
-
│ ├── analytics/ # Analytics
|
| 160 |
-
│ └── core/ # Mixins, utilities
|
| 161 |
-
├── educonnect_api/ # Configuration Django
|
| 162 |
-
├── requirements.txt
|
| 163 |
-
├── Dockerfile
|
| 164 |
-
└── docker-compose.yml
|
| 165 |
-
```
|
| 166 |
-
|
| 167 |
-
## 🔒 Sécurité
|
| 168 |
-
|
| 169 |
-
- Tokens JWT avec refresh
|
| 170 |
-
- Rate limiting
|
| 171 |
-
- CORS configuré
|
| 172 |
-
- Validation des entrées
|
| 173 |
-
- Soft delete pour traçabilité
|
| 174 |
-
- HTTPS obligatoire en production
|
| 175 |
-
|
| 176 |
-
## 📊 Monitoring
|
| 177 |
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
##
|
| 181 |
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
|
|
|
|
|
|
| 187 |
|
| 188 |
-
##
|
| 189 |
|
| 190 |
-
|
| 191 |
|
| 192 |
-
##
|
| 193 |
|
| 194 |
-
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: EduLab Backend
|
| 3 |
+
emoji: 🎓
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
---
|
| 9 |
|
| 10 |
+
# EduLab Africa - Backend API
|
| 11 |
|
| 12 |
+
Backend Django REST API pour la plateforme éducative EduLab Africa.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
## 🚀 Technologies
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
- **Django 4.2** - Framework web Python
|
| 17 |
+
- **Django REST Framework** - API REST
|
| 18 |
+
- **SQLite** - Base de données (par défaut)
|
| 19 |
+
- **Gunicorn** - Serveur WSGI
|
| 20 |
+
- **JWT** - Authentification
|
| 21 |
|
| 22 |
+
## 📡 API Endpoints
|
| 23 |
|
| 24 |
+
- `/api/users/` - Gestion des utilisateurs
|
| 25 |
+
- `/api/mentors/` - Profils mentors
|
| 26 |
+
- `/api/bookings/` - Réservations de sessions
|
| 27 |
+
- `/api/forum/` - Forum communautaire
|
| 28 |
+
- `/api/opportunities/` - Opportunités éducatives
|
| 29 |
+
- `/api/ai-tools/` - Outils IA
|
| 30 |
+
- `/api/gamification/` - Système de badges et points
|
| 31 |
|
| 32 |
+
## 🔗 Frontend
|
| 33 |
|
| 34 |
+
Ce backend est connecté au frontend : [EdulabFrontend](https://huggingface.co/spaces/rinogeek/EdulabFrontend)
|
| 35 |
|
| 36 |
+
## 👨💻 Développeur
|
| 37 |
|
| 38 |
+
Développé par **Marino ATOHOUN** pour **Hypee**
|
backend/apps/users/management/commands/create_superuser.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Commande pour créer un superutilisateur par défaut
|
| 3 |
+
"""
|
| 4 |
+
from django.core.management.base import BaseCommand
|
| 5 |
+
from django.contrib.auth import get_user_model
|
| 6 |
+
from apps.users.models import UserProfile
|
| 7 |
+
|
| 8 |
+
User = get_user_model()
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class Command(BaseCommand):
|
| 12 |
+
help = 'Créer un superutilisateur par défaut si aucun n\'existe'
|
| 13 |
+
|
| 14 |
+
def add_arguments(self, parser):
|
| 15 |
+
parser.add_argument(
|
| 16 |
+
'--email',
|
| 17 |
+
default='admin@edulab.africa',
|
| 18 |
+
help='Email du superuser'
|
| 19 |
+
)
|
| 20 |
+
parser.add_argument(
|
| 21 |
+
'--password',
|
| 22 |
+
default='rinogeek',
|
| 23 |
+
help='Mot de passe du superuser'
|
| 24 |
+
)
|
| 25 |
+
parser.add_argument(
|
| 26 |
+
'--name',
|
| 27 |
+
default='Rino Admin',
|
| 28 |
+
help='Nom complet du superuser'
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
def handle(self, *args, **options):
|
| 32 |
+
email = options['email']
|
| 33 |
+
password = options['password']
|
| 34 |
+
name = options['name']
|
| 35 |
+
|
| 36 |
+
# Vérifier si un superuser existe déjà
|
| 37 |
+
if User.objects.filter(is_superuser=True).exists():
|
| 38 |
+
self.stdout.write(
|
| 39 |
+
self.style.WARNING(f'Un superutilisateur existe déjà. Ignoré.')
|
| 40 |
+
)
|
| 41 |
+
return
|
| 42 |
+
|
| 43 |
+
# Vérifier si l'utilisateur existe déjà
|
| 44 |
+
if User.objects.filter(email=email).exists():
|
| 45 |
+
user = User.objects.get(email=email)
|
| 46 |
+
if not user.is_superuser:
|
| 47 |
+
user.is_superuser = True
|
| 48 |
+
user.is_staff = True
|
| 49 |
+
user.role = 'ADMIN'
|
| 50 |
+
user.save()
|
| 51 |
+
self.stdout.write(
|
| 52 |
+
self.style.SUCCESS(f'Utilisateur "{email}" promu superutilisateur.')
|
| 53 |
+
)
|
| 54 |
+
else:
|
| 55 |
+
self.stdout.write(
|
| 56 |
+
self.style.WARNING(f'Utilisateur "{email}" existe déjà.')
|
| 57 |
+
)
|
| 58 |
+
return
|
| 59 |
+
|
| 60 |
+
# Créer le superutilisateur
|
| 61 |
+
user = User.objects.create_superuser(
|
| 62 |
+
email=email,
|
| 63 |
+
password=password
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Créer le profil utilisateur
|
| 67 |
+
UserProfile.objects.create(
|
| 68 |
+
user=user,
|
| 69 |
+
name=name,
|
| 70 |
+
is_current=True
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
self.stdout.write(
|
| 74 |
+
self.style.SUCCESS(
|
| 75 |
+
f'✓ Superutilisateur créé avec succès!\n'
|
| 76 |
+
f' Email: {email}\n'
|
| 77 |
+
f' Name: {name}\n'
|
| 78 |
+
f' Password: {password}'
|
| 79 |
+
)
|
| 80 |
+
)
|
backend/educonnect/settings.py
CHANGED
|
@@ -11,7 +11,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|
| 11 |
# SECURITY
|
| 12 |
SECRET_KEY = config('SECRET_KEY', default='django-insecure-6tmj-j5auz5kd)bda)acc0!*j+8-)knr7wsp(lqs=46$alpd5j')
|
| 13 |
DEBUG = config('DEBUG', default=False, cast=bool)
|
| 14 |
-
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1').split(',')
|
| 15 |
|
| 16 |
# Application definition
|
| 17 |
INSTALLED_APPS = [
|
|
@@ -182,9 +182,17 @@ SIMPLE_JWT = {
|
|
| 182 |
# CORS Configuration
|
| 183 |
CORS_ALLOWED_ORIGINS = config(
|
| 184 |
'CORS_ALLOWED_ORIGINS',
|
| 185 |
-
default='http://localhost:3000,http://localhost:5173,http://localhost:3001'
|
| 186 |
).split(',')
|
| 187 |
CORS_ALLOW_CREDENTIALS = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
CORS_ALLOW_HEADERS = [
|
| 189 |
'accept',
|
| 190 |
'accept-encoding',
|
|
@@ -196,6 +204,14 @@ CORS_ALLOW_HEADERS = [
|
|
| 196 |
'x-csrftoken',
|
| 197 |
'x-requested-with',
|
| 198 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
# Channels Configuration (WebSockets)
|
| 201 |
CHANNEL_LAYERS = {
|
|
@@ -270,15 +286,20 @@ LOGGING = {
|
|
| 270 |
|
| 271 |
# Security Settings for Production
|
| 272 |
if not DEBUG:
|
| 273 |
-
|
|
|
|
| 274 |
SESSION_COOKIE_SECURE = True
|
| 275 |
CSRF_COOKIE_SECURE = True
|
| 276 |
SECURE_BROWSER_XSS_FILTER = True
|
| 277 |
SECURE_CONTENT_TYPE_NOSNIFF = True
|
| 278 |
-
X_FRAME_OPTIONS = '
|
| 279 |
SECURE_HSTS_SECONDS = 31536000
|
| 280 |
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
| 281 |
SECURE_HSTS_PRELOAD = True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
# Gamification Settings
|
| 284 |
GAMIFICATION_POINTS = {
|
|
|
|
| 11 |
# SECURITY
|
| 12 |
SECRET_KEY = config('SECRET_KEY', default='django-insecure-6tmj-j5auz5kd)bda)acc0!*j+8-)knr7wsp(lqs=46$alpd5j')
|
| 13 |
DEBUG = config('DEBUG', default=False, cast=bool)
|
| 14 |
+
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1,.hf.space').split(',')
|
| 15 |
|
| 16 |
# Application definition
|
| 17 |
INSTALLED_APPS = [
|
|
|
|
| 182 |
# CORS Configuration
|
| 183 |
CORS_ALLOWED_ORIGINS = config(
|
| 184 |
'CORS_ALLOWED_ORIGINS',
|
| 185 |
+
default='http://localhost:3000,http://localhost:5173,http://localhost:3001,https://rinogeek-edulabfrontend.hf.space'
|
| 186 |
).split(',')
|
| 187 |
CORS_ALLOW_CREDENTIALS = True
|
| 188 |
+
CORS_ALLOW_METHODS = [
|
| 189 |
+
'DELETE',
|
| 190 |
+
'GET',
|
| 191 |
+
'OPTIONS',
|
| 192 |
+
'PATCH',
|
| 193 |
+
'POST',
|
| 194 |
+
'PUT',
|
| 195 |
+
]
|
| 196 |
CORS_ALLOW_HEADERS = [
|
| 197 |
'accept',
|
| 198 |
'accept-encoding',
|
|
|
|
| 204 |
'x-csrftoken',
|
| 205 |
'x-requested-with',
|
| 206 |
]
|
| 207 |
+
# Allow preflight requests to be cached
|
| 208 |
+
CORS_PREFLIGHT_MAX_AGE = 86400
|
| 209 |
+
|
| 210 |
+
# CSRF Configuration - Trust frontend origins
|
| 211 |
+
CSRF_TRUSTED_ORIGINS = config(
|
| 212 |
+
'CSRF_TRUSTED_ORIGINS',
|
| 213 |
+
default='http://localhost:3000,http://localhost:5173,https://rinogeek-edulabfrontend.hf.space,https://rinogeek-edulabbackend.hf.space'
|
| 214 |
+
).split(',')
|
| 215 |
|
| 216 |
# Channels Configuration (WebSockets)
|
| 217 |
CHANNEL_LAYERS = {
|
|
|
|
| 286 |
|
| 287 |
# Security Settings for Production
|
| 288 |
if not DEBUG:
|
| 289 |
+
# Hugging Face handles SSL at the proxy level, so we don't necessarily need redirect
|
| 290 |
+
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool)
|
| 291 |
SESSION_COOKIE_SECURE = True
|
| 292 |
CSRF_COOKIE_SECURE = True
|
| 293 |
SECURE_BROWSER_XSS_FILTER = True
|
| 294 |
SECURE_CONTENT_TYPE_NOSNIFF = True
|
| 295 |
+
X_FRAME_OPTIONS = 'ALLOWALL' # Allow HF Space to embed
|
| 296 |
SECURE_HSTS_SECONDS = 31536000
|
| 297 |
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
| 298 |
SECURE_HSTS_PRELOAD = True
|
| 299 |
+
|
| 300 |
+
# HF Spaces specific headers
|
| 301 |
+
USE_X_FORWARDED_HOST = True
|
| 302 |
+
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
| 303 |
|
| 304 |
# Gamification Settings
|
| 305 |
GAMIFICATION_POINTS = {
|
frontend/.env.production
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ==============================================
|
| 2 |
+
# CONFIGURATION PRODUCTION - HUGGING FACE SPACES
|
| 3 |
+
# ==============================================
|
| 4 |
+
|
| 5 |
+
# API Backend URL (Hugging Face Space)
|
| 6 |
+
VITE_API_URL=https://rinogeek-edulabbackend.hf.space/api/
|
| 7 |
+
|
| 8 |
+
# Gemini API Key (si utilisé côté client)
|
| 9 |
+
GEMINI_API_KEY=AIzaSyD-hnYdbQudiK8nfFtML05Mmlo26nLzxWQ
|
frontend/.gitignore
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Logs
|
| 2 |
-
logs
|
| 3 |
*.log
|
| 4 |
npm-debug.log*
|
| 5 |
yarn-debug.log*
|
| 6 |
yarn-error.log*
|
| 7 |
-
pnpm-debug.log*
|
| 8 |
-
lerna-debug.log*
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
dist-ssr
|
| 13 |
-
*.local
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
.
|
| 17 |
-
|
| 18 |
-
.idea
|
| 19 |
-
.DS_Store
|
| 20 |
-
*.suo
|
| 21 |
-
*.ntvs*
|
| 22 |
-
*.njsproj
|
| 23 |
-
*.sln
|
| 24 |
-
*.sw?
|
|
|
|
| 1 |
+
# Dependencies
|
| 2 |
+
node_modules/
|
| 3 |
+
.pnp
|
| 4 |
+
.pnp.js
|
| 5 |
+
|
| 6 |
+
# Build output
|
| 7 |
+
dist/
|
| 8 |
+
build/
|
| 9 |
+
|
| 10 |
+
# Environment files (local only)
|
| 11 |
+
.env
|
| 12 |
+
.env.local
|
| 13 |
+
.env.*.local
|
| 14 |
+
|
| 15 |
+
# IDE
|
| 16 |
+
.idea/
|
| 17 |
+
.vscode/
|
| 18 |
+
*.swp
|
| 19 |
+
*.swo
|
| 20 |
+
|
| 21 |
+
# OS
|
| 22 |
+
.DS_Store
|
| 23 |
+
Thumbs.db
|
| 24 |
+
|
| 25 |
# Logs
|
|
|
|
| 26 |
*.log
|
| 27 |
npm-debug.log*
|
| 28 |
yarn-debug.log*
|
| 29 |
yarn-error.log*
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
# Testing
|
| 32 |
+
coverage/
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
# Cache
|
| 35 |
+
.cache/
|
| 36 |
+
.parcel-cache/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/Dockerfile
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
-
#
|
| 2 |
-
FROM node:20-slim
|
| 3 |
|
| 4 |
-
# Définir le répertoire de travail
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
# Copier les fichiers de package
|
|
@@ -13,8 +12,27 @@ RUN npm install
|
|
| 13 |
# Copier le reste des fichiers du projet
|
| 14 |
COPY . .
|
| 15 |
|
| 16 |
-
#
|
| 17 |
-
|
|
|
|
| 18 |
|
| 19 |
-
#
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Build stage
|
| 2 |
+
FROM node:20-slim AS build
|
| 3 |
|
|
|
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
# Copier les fichiers de package
|
|
|
|
| 12 |
# Copier le reste des fichiers du projet
|
| 13 |
COPY . .
|
| 14 |
|
| 15 |
+
# Argument pour l'URL de l'API (passé lors du build ou défini ici par défaut)
|
| 16 |
+
ARG VITE_API_URL=https://rinogeek-edulabbackend.hf.space/api/
|
| 17 |
+
ENV VITE_API_URL=$VITE_API_URL
|
| 18 |
|
| 19 |
+
# Copier le fichier .env.production comme .env pour la production
|
| 20 |
+
RUN cp .env.production .env || true
|
| 21 |
+
|
| 22 |
+
# Builder l'application
|
| 23 |
+
RUN npm run build
|
| 24 |
+
|
| 25 |
+
# Serve stage
|
| 26 |
+
FROM nginx:alpine
|
| 27 |
+
|
| 28 |
+
# Copier les fichiers buildés vers nginx
|
| 29 |
+
COPY --from=build /app/dist /usr/share/nginx/html
|
| 30 |
+
|
| 31 |
+
# Copier une configuration nginx personnalisée pour gérer le routing SPA
|
| 32 |
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
| 33 |
+
|
| 34 |
+
# Exposer le port 7860 (Hugging Face Spaces)
|
| 35 |
+
EXPOSE 7860
|
| 36 |
+
|
| 37 |
+
# Démarrer Nginx
|
| 38 |
+
CMD ["nginx", "-g", "daemon off;"]
|
frontend/README.md
CHANGED
|
@@ -1,20 +1,35 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
#
|
| 6 |
|
| 7 |
-
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: EduLab Frontend
|
| 3 |
+
emoji: 📚
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
---
|
| 9 |
|
| 10 |
+
# EduLab Africa - Frontend
|
| 11 |
|
| 12 |
+
Interface utilisateur React pour la plateforme éducative EduLab Africa.
|
| 13 |
|
| 14 |
+
## 🚀 Technologies
|
| 15 |
|
| 16 |
+
- **React 19** - Bibliothèque UI
|
| 17 |
+
- **Vite** - Build tool
|
| 18 |
+
- **TypeScript** - Typage statique
|
| 19 |
+
- **Nginx** - Serveur web (production)
|
| 20 |
|
| 21 |
+
## ✨ Fonctionnalités
|
| 22 |
|
| 23 |
+
- 🎓 **Tuteur IA** - Assistant pédagogique intelligent
|
| 24 |
+
- 👨🏫 **Mentorat** - Connexion avec des mentors
|
| 25 |
+
- 💬 **Forum** - Questions & réponses communautaires
|
| 26 |
+
- 🏆 **Gamification** - Badges et points de progression
|
| 27 |
+
- 🎯 **Opportunités** - Bourses et stages
|
| 28 |
|
| 29 |
+
## 🔗 Backend
|
| 30 |
+
|
| 31 |
+
Ce frontend est connecté au backend : [EdulabBackend](https://huggingface.co/spaces/rinogeek/EdulabBackend)
|
| 32 |
+
|
| 33 |
+
## 👨💻 Développeur
|
| 34 |
+
|
| 35 |
+
Développé par **Marino ATOHOUN** pour **Hypee**
|
frontend/nginx.conf
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
server {
|
| 2 |
+
listen 7860;
|
| 3 |
+
server_name localhost;
|
| 4 |
+
|
| 5 |
+
location / {
|
| 6 |
+
root /usr/share/nginx/html;
|
| 7 |
+
index index.html index.htm;
|
| 8 |
+
try_files $uri $uri/ /index.html;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
# Configuration pour le cache des fichiers statiques
|
| 12 |
+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
| 13 |
+
root /usr/share/nginx/html;
|
| 14 |
+
expires 30d;
|
| 15 |
+
add_header Cache-Control "public, no-transform";
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
error_page 500 502 503 504 /50x.html;
|
| 19 |
+
location = /50x.html {
|
| 20 |
+
root /usr/share/nginx/html;
|
| 21 |
+
}
|
| 22 |
+
}
|
frontend/services/api.ts
CHANGED
|
@@ -2,7 +2,7 @@ import axios from 'axios';
|
|
| 2 |
|
| 3 |
// Configuration de l'API
|
| 4 |
// Développé par Marino ATOHOUN pour Hypee
|
| 5 |
-
const API_URL = 'http://127.0.0.1:8000/api/';
|
| 6 |
|
| 7 |
const api = axios.create({
|
| 8 |
baseURL: API_URL,
|
|
|
|
| 2 |
|
| 3 |
// Configuration de l'API
|
| 4 |
// Développé par Marino ATOHOUN pour Hypee
|
| 5 |
+
const API_URL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000/api/';
|
| 6 |
|
| 7 |
const api = axios.create({
|
| 8 |
baseURL: API_URL,
|
frontend/vite.config.ts
CHANGED
|
@@ -12,7 +12,8 @@ export default defineConfig(({ mode }) => {
|
|
| 12 |
plugins: [react()],
|
| 13 |
define: {
|
| 14 |
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
| 15 |
-
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
|
|
|
| 16 |
},
|
| 17 |
resolve: {
|
| 18 |
alias: {
|
|
|
|
| 12 |
plugins: [react()],
|
| 13 |
define: {
|
| 14 |
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
| 15 |
+
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
| 16 |
+
'import.meta.env.VITE_API_URL': JSON.stringify(env.VITE_API_URL || 'https://rinogeek-edulabbackend.hf.space/api/')
|
| 17 |
},
|
| 18 |
resolve: {
|
| 19 |
alias: {
|