Backend (courses app): - courses/serializers.py: Course, Module, Lesson, Page, Enrollment, PageProgress serializers - courses/views.py: CourseListView, CourseDetailView, CourseEnrollView, CourseMyProgressView, MyEnrollmentsView, PageDetailView - courses/urls.py: expose all 6 endpoints under /api/v1/courses/ Frontend: - courses.html + courses.js: course catalog with "Meine Kurse" and "Alle Kurse" sections, enroll button with toast feedback, progress bars - course-player.html + course-player.js: full-screen player with collapsible sidebar nav (module → lesson → page tree), page content renderer (text/video/embed), dwell gate, prev/next navigation, enrollment CTA, completion screen - courses.css: course grid, player layout, sidebar nav, progress bar, dwell gate - Add Kurse nav link to meetings.html, attendance.html - Update index.html hero and workflow grid to include Kurse Co-Authored-By: Paperclip <noreply@paperclip.ing>
Training Software
This repository contains a Django-based training platform backend plus a static customer portal frontend.
Features for customer environment setup
- Per-customer organization profile with configurable license user limits (
20,50,100,1000) - Per-customer branding via company name and logo URL
- Static frontend that can be hosted on Nginx and configured with the backend API domain at deploy time
- CI pipeline for tests, linting, and container build
Production deployment with nginx
docker-compose.prod.yml starts the full stack in one command:
- nginx — public-facing web server on port 80; serves the frontend SPA and Django static files directly; proxies all backend routes to gunicorn
- web — Django/gunicorn (prod settings, 4 workers)
- celery / celery-beat — background task workers
- db — PostgreSQL 16
- redis — Redis 7
1. Prerequisites
- Docker Engine 24+ with Compose v2
2. Environment variables
cp .env.example .env
Edit .env and set at minimum:
| Variable | Notes |
|---|---|
DJANGO_SECRET_KEY |
Long random string |
DJANGO_ALLOWED_HOSTS |
Comma-separated hostnames, e.g. training.example.com |
DB_PASSWORD |
PostgreSQL password |
SECURE_SSL_REDIRECT |
Set false for HTTP-only; set true (or omit) when TLS is terminated at nginx or an upstream proxy |
3. Start the stack
docker compose -f docker-compose.prod.yml up -d --build
4. Run migrations and create admin user
docker compose -f docker-compose.prod.yml exec web python manage.py migrate
docker compose -f docker-compose.prod.yml exec web python manage.py createsuperuser
5. Verify health
curl http://<host>/healthz/
HTTPS
To enable HTTPS, add ssl_certificate and ssl_certificate_key directives to
nginx/nginx.conf and expose port 443 in docker-compose.prod.yml, or place a
TLS-terminating reverse proxy (Traefik, Caddy, etc.) in front and set
SECURE_SSL_REDIRECT=true.
Production deployment with dedicated frontend container
Use docker-compose.proxy.yml if you want your external nginx to be only a
reverse proxy while this stack runs its own frontend container.
1. Start the stack
cp .env.example .env
docker compose -f docker-compose.proxy.yml up -d --build
docker compose -f docker-compose.proxy.yml exec web python manage.py migrate
docker compose -f docker-compose.proxy.yml exec web python manage.py createsuperuser
By default, the frontend container is exposed on http://localhost:8080.
Set FRONTEND_PORT in .env.proxy if needed.
2. External nginx reverse-proxy example
server {
listen 80;
server_name training.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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;
}
}
Local development
1. Prerequisites
- Python 3.12
- PostgreSQL 16+
- Redis 7+
- Docker (recommended for containerised dev)
2. Environment variables
Create environment variables based on .env.example.
Required minimum values:
DJANGO_SECRET_KEYDJANGO_ALLOWED_HOSTSDATABASE_URLREDIS_URLDJANGO_SETTINGS_MODULE(useconfig.settings.localfor dev)
For Docker Compose (docker-compose.yml), use:
DATABASE_URL=postgres://training:training@db:5432/trainingREDIS_URL=redis://redis:6379/0
3. Build and run with Docker Compose
docker compose up -d --build
4. Run migrations
docker compose exec web python manage.py migrate
5. Verify health
curl http://localhost:8000/healthz/
Local frontend in Docker (optional)
docker-compose.yml also includes a frontend service on http://localhost:8080.
Backend-only services (db, redis, celery, certificate-renderer) run on an
internal Docker network and are not exposed on host ports.
The landing page at / is a small health/config page.
Main app UIs are:
http://localhost:8080/meetings.htmlhttp://localhost:8080/attendance.html
Set FRONTEND_API_BASE_URL in .env:
# recommended in Docker: leave empty to use same-origin + /api proxy
FRONTEND_API_BASE_URL=
# only when API is on another domain:
# FRONTEND_API_BASE_URL=https://api.example.com
The same variable is supported by docker-compose.proxy.yml and docker-compose.prod.yml.
Organization profile API (license + branding)
Admin-only endpoint (requires admin role):
GET /api/v1/accounts/organizations/{org_id}/profile/PATCH /api/v1/accounts/organizations/{org_id}/profile/
Fields
| Field | Type | Description |
|---|---|---|
company_name |
string | Customer company name shown in the UI |
license_user_limit |
integer | Maximum number of active users — any positive integer (e.g. 20, 50, 100, 250, 1000) |
brand_logo_url |
string (URL) | URL of the customer's logo, displayed in the frontend header |
Setting the license limit
license_user_limit accepts any positive integer — there are no fixed tiers. Set it to exactly the number of users your customer has licensed:
curl -X PATCH https://api.example.com/api/v1/accounts/organizations/<org_id>/profile/ \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{
"company_name": "Acme Corp",
"license_user_limit": 250,
"brand_logo_url": "https://cdn.example.com/acme-logo.svg"
}'
Common values: 20 · 50 · 100 · 250 · 500 · 1000 — but any number works.
Frontend deployment on Nginx
The static frontend is under frontend/public.
1. Set backend API domain
Edit frontend/public/config.js and set:
window.APP_CONFIG = {
API_BASE_URL: "https://api.example.com"
};
2. Serve via Nginx
Use frontend/nginx.conf as a reference server config and copy the frontend/public files to your Nginx web root.
CI for Gitea
Gitea Actions workflow file:
.gitea/workflows/ci.yml
It runs:
- Ruff lint
- Django test suite
- Docker image build