Educore - E-Learning Kursverwaltungsplattform
Overview
Educore is a production-ready e-learning platform that enables educational institutions to manage courses and handle online enrollments efficiently. The platform provides a clean REST API, a responsive Bootstrap frontend, and fully automated deployment on Coolify with Traefik reverse proxy and automatic SSL certificates. Key capabilities include: - Course management with capacity tracking - Self-service participant enrollment - Token-based authentication - Automatic duplicate enrollment prevention - Real-time course availability updates - PostgreSQL database with row-level locking - Docker containerization with multi-service orchestration - Traefik reverse proxy with Let's Encrypt SSL - Complete API documentation via Swagger/OpenAPI
The Challenge
Educational institutions struggle with manual course registration processes that are error-prone, time-consuming, and lack real-time visibility into course capacities. Administrators waste hours managing spreadsheets and handling duplicate registrations. Participants face frustration when courses are overbooked or when they can't easily see available options. The main challenges include: - Manual enrollment processes leading to overbooking - No real-time visibility of course availability - Duplicate registrations wasting administrative time - Difficult for participants to discover and enroll in courses - Lack of integration between course catalogs and enrollment systems - No automated capacity management
The Solution
Educore solves these challenges by providing a complete digital platform for course management and enrollment. Administrators can easily create and manage courses through Django's admin interface. Participants can browse courses, check real-time availability, and self-enroll through an intuitive Bootstrap frontend. Technical Approach: 1. Django REST Framework for robust API development 2. PostgreSQL with row-level locking for concurrent enrollment handling 3. Token-based authentication for secure API access 4. Nginx serving static files with API proxying 5. Traefik reverse proxy with automatic SSL certificate generation 6. Docker multi-container orchestration on Coolify 7. Bootstrap 5 responsive frontend with vanilla JavaScript 8. Business logic validation at both view and model layers
Technical Implementation
Enrollment Business Logic with Double Validation
Implementation of course enrollment logic with capacity checking and duplicate prevention at both view and model layers for maximum data integrity.
class Anmeldung(models.Model):
def clean(self):
if self.status == 'angemeldet':
self._prevent_duplicate_enrollment()
self._check_course_capacity()
def _check_course_capacity(self):
if self.kurs.ist_voll:
raise ValidationError(
f"Der Kurs '{self.kurs.titel}' ist bereits ausgebucht "
f"(max. {self.kurs.max_teilnehmer} Teilnehmer)."
)
class AnmeldungCreateView(APIView):
def post(self, request):
teilnehmer, created = Teilnehmer.objects.get_or_create(
email=data['email'],
defaults={'vorname': data['vorname'], 'nachname': data['nachname']}
)
if Anmeldung.objects.filter(kurs=kurs, teilnehmer=teilnehmer, status='angemeldet').exists():
return Response({'error': 'Bereits angemeldet'}, status=status.HTTP_409_CONFLICT)
anmeldung = Anmeldung.objects.create(kurs=kurs, teilnehmer=teilnehmer)
return Response(AnmeldungSerializer(anmeldung).data, status=status.HTTP_201_CREATED)
Course Capacity Property with Active Enrollment Counting
Dynamic course capacity calculation that only counts active (non-cancelled) enrollments for accurate availability tracking.
class Kurs(models.Model):
@property
def verfuegbare_plaetze(self):
aktive_anmeldungen = self.anmeldungen.filter(status='angemeldet').count()
return self.max_teilnehmer - aktive_anmeldungen
@property
def ist_voll(self):
return self.verfuegbare_plaetze <= 0
@property
def anzahl_anmeldungen(self):
return self.anmeldungen.filter(status='angemeldet').count()
Docker Compose Multi-Service Orchestration
Complete Docker Compose configuration for PostgreSQL, Django backend, and Nginx frontend with healthchecks and network isolation.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ${DB_NAME:-educore}
POSTGRES_USER: ${DB_USER:-educore_user}
POSTGRES_PASSWORD: ${DB_PASSWORD:-educore_password}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-educore_user}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build: ./backend
environment:
- SECRET_KEY=${SECRET_KEY}
- DEBUG=False
- ALLOWED_HOSTS=${ALLOWED_HOSTS}
- DB_HOST=postgres
depends_on:
postgres:
condition: service_healthy
expose:
- "8100"
frontend:
build: ./frontend
depends_on:
- backend
ports:
- "80:80"
Frontend API Integration with Token Authentication
Vanilla JavaScript implementation of token-based authentication and API calls with localStorage persistence.
// api.js
async function apiRequest(endpoint, method = 'GET', token = null, data = null) {
const headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Token ${token}`;
const response = await fetch(endpoint, {
method, headers,
body: data ? JSON.stringify(data) : null
});
if (!response.ok) throw await response.json();
return response.json();
}
// auth.js
async function login(username, password) {
const response = await apiRequest('/api-token-auth/', 'POST', null, { username, password });
localStorage.setItem('auth_token', response.token);
return response;
}
function getToken() {
return localStorage.getItem('auth_token');
}
Nginx Reverse Proxy Configuration
Nginx configuration for serving static files and proxying API requests to the Django backend.
server {
listen 80;
server_name educore.smarta.website;
root /usr/share/nginx/html;
index index.html;
location /api/ {
proxy_pass http://backend:8100/api/;
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;
}
location /admin/ {
proxy_pass http://backend:8000/admin/;
}
location /api-token-auth/ {
proxy_pass http://backend:8000/api-token-auth/;
}
location / {
try_files $uri $uri/ /index.html;
}
}