Before creating a UserRoleBinding for a specific org_id, check whether the org's OrganizationProfile.license_user_limit has been reached. New users attempting to join a full org receive HTTP 403 with a descriptive message. Re-assigning a user already in the org (role change) is unaffected, as is assignment to an org with no OrganizationProfile. 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
Backend deployment
1. Prerequisites
- Python 3.12
- PostgreSQL 16+
- Redis 7+
- Docker (recommended for containerized deploy)
2. Environment variables
Create environment variables based on .env.example.
Required minimum values:
DJANGO_SECRET_KEYDJANGO_ALLOWED_HOSTSDATABASE_URLREDIS_URLDJANGO_SETTINGS_MODULE(useconfig.settings.prodin production)
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://<api-domain>/healthz/
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
Description
Languages
Python
96.6%
Shell
1.3%
HTML
0.9%
TypeScript
0.5%
JavaScript
0.3%
Other
0.4%