217 lines
5.6 KiB
Markdown
217 lines
5.6 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yml up -d --build
|
|
```
|
|
|
|
### 4. Run migrations and create admin user
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```nginx
|
|
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_KEY`
|
|
- `DJANGO_ALLOWED_HOSTS`
|
|
- `DATABASE_URL`
|
|
- `REDIS_URL`
|
|
- `DJANGO_SETTINGS_MODULE` (use `config.settings.local` for dev)
|
|
|
|
### 3. Build and run with Docker Compose
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
### 4. Run migrations
|
|
|
|
```bash
|
|
docker compose exec web python manage.py migrate
|
|
```
|
|
|
|
### 5. Verify health
|
|
|
|
```bash
|
|
curl http://localhost:8000/healthz/
|
|
```
|
|
|
|
### Local frontend in Docker (optional)
|
|
|
|
`docker-compose.yml` also includes a `frontend` service on `http://localhost:8080`.
|
|
|
|
Set `FRONTEND_API_BASE_URL` in `.env` to point the frontend to your backend API, for example:
|
|
|
|
```bash
|
|
FRONTEND_API_BASE_URL=http://localhost:8000
|
|
```
|
|
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```js
|
|
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
|