Files
plugins/openedx-tenant-api/README.md
DamarKusumo 2b7027e37d Add openedx-tenant-api plugin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 08:20:57 +07:00

237 lines
6.5 KiB
Markdown

# Open edX Tenant API
REST API for programmatic tenant creation in Open edX with eox-tenant.
## Features
- Create tenants with automatic MFE configuration
- List all provisioned tenants
- Delete tenants
- Health check endpoint
- Audit logging of all tenant operations
## Installation
### 1. Add to Tutor Config
Edit your Tutor `config.yml`:
```yaml
MOUNTS:
- /path/to/openedx-tenant-api
OPENEDX_EXTRA_PIP_REQUIREMENTS:
- -e /mnt/openedx-tenant-api
```
### 2. Restart LMS
```bash
tutor dev restart lms
```
### 3. Install Package and Run Migrations
```bash
# Install in running container
docker exec tutor_dev-lms-1 sh -c "pip install -e /mnt/openedx-tenant-api --no-build-isolation"
# Create migrations
docker exec tutor_dev-lms-1 sh -c "cd /openedx/edx-platform && python manage.py lms makemigrations openedx_tenant_api"
# Run migrations
docker exec tutor_dev-lms-1 sh -c "cd /openedx/edx-platform && python manage.py lms migrate openedx_tenant_api"
```
## API Endpoints
| Endpoint | Method | Auth Required | Description |
|----------|--------|---------------|-------------|
| `/api/tenant/v1/health` | GET | No | Health check |
| `/api/tenant/v1/list` | GET | Yes (Admin) | List all tenants |
| `/api/tenant/v1/create` | POST | Yes (Admin) | Create new tenant |
| `/api/tenant/v1/delete/{tenant_name}` | DELETE | Yes (Admin) | Delete tenant |
## Usage
### Health Check
```bash
curl http://local.openedx.io:8000/api/tenant/v1/health
```
Response:
```json
{
"status": "healthy",
"eox_tenant_installed": true,
"tenant_count": 2
}
```
### Create Tenant
Requires admin user authentication. Supports Basic Auth, JWT Bearer, or Session auth.
```python
import requests
# Using Basic Authentication (simplest)
response = requests.post(
'http://local.openedx.io:8000/api/tenant/v1/create',
auth=('admin', 'your-password'),
json={
'tenant_name': 'talent1',
'platform_name': 'Talent 1 Learning',
'theme_name': 'indigo',
'org_filter': ['org1'] # Optional
}
)
print(response.json())
```
```bash
# Using cURL with Basic Auth
curl -X POST http://local.openedx.io:8000/api/tenant/v1/create \
-u admin:your-password \
-H "Content-Type: application/json" \
-d '{
"tenant_name": "talent1",
"platform_name": "Talent 1 Learning",
"theme_name": "indigo"
}'
```
Response:
```json
{
"status": "success",
"tenant_id": 123,
"tenant_name": "talent1",
"lms_url": "http://talent1.local.openedx.io:8000",
"authn_url": "http://talent1.apps.local.openedx.io:1999/authn",
"learner_dashboard_url": "http://talent1.apps.local.openedx.io:1996/learner-dashboard/",
"message": "Tenant created successfully. Add DNS entries to your hosts file."
}
```
### List Tenants
```python
import requests
response = requests.get(
'http://local.openedx.io:8000/api/tenant/v1/list',
headers={'Authorization': 'Bearer YOUR_TOKEN'}
)
print(response.json())
```
### Delete Tenant
```python
import requests
response = requests.delete(
'http://local.openedx.io:8000/api/tenant/v1/delete/talent1',
headers={'Authorization': 'Bearer YOUR_TOKEN'}
)
print(response.json())
```
## Testing via Django Shell
For testing without authentication:
```bash
docker exec tutor_dev-lms-1 sh -c "cd /openedx/edx-platform && python manage.py lms shell" << 'EOF'
import django
django.setup()
from django.db import transaction
from django.contrib.auth import get_user_model
from eox_tenant.models import TenantConfig, Route
User = get_user_model()
admin = User.objects.filter(is_superuser=True).first()
tenant_name = 'talent1'
platform_name = 'Talent 1 Learning'
theme_name = 'indigo'
external_key = f'{tenant_name}.local.openedx.io'
lms_domain = f'{tenant_name}.local.openedx.io'
# Generate MFE config
lms_configs = {
'LMS_BASE': f'{lms_domain}:8000',
'SITE_NAME': lms_domain,
'PLATFORM_NAME': platform_name,
'THEME_NAME': theme_name,
'BASE_URL': f'http://{tenant_name}.apps.local.openedx.io',
'LMS_BASE_URL': f'http://{lms_domain}:8000',
'LEARNER_HOME_MICROFRONTEND_URL': f'http://{tenant_name}.apps.local.openedx.io:1996/learner-dashboard/',
'AUTHN_MICROFRONTEND_URL': f'http://{tenant_name}.apps.local.openedx.io:1999/authn',
# ... add other MFE URLs as needed
}
with transaction.atomic():
tenant_config = TenantConfig.objects.create(
external_key=external_key,
lms_configs=lms_configs,
theming_configs={'THEME_NAME': theme_name},
meta={'created_by': admin.username}
)
Route.objects.create(domain=lms_domain, config=tenant_config)
print(f'Tenant {tenant_name} created!')
EOF
```
## Hosts File Configuration
After creating a tenant, add these entries to your hosts file:
```
127.0.0.1 talent1.local.openedx.io
127.0.0.1 talent1.apps.local.openedx.io
127.0.0.1 studio.talent1.local.openedx.io
```
## MFE Configuration
The API automatically generates MFE URLs for the tenant:
- Authn: `http://{tenant}.apps.local.openedx.io:1999/authn`
- Learner Dashboard: `http://{tenant}.apps.local.openedx.io:1996/learner-dashboard/`
- Account: `http://{tenant}.apps.local.openedx.io:1997/account/`
- Profile: `http://{tenant}.apps.local.openedx.io:1995/profile/u/`
- Learning: `http://{tenant}.apps.local.openedx.io:2000/learning`
- Discussions: `http://{tenant}.apps.local.openedx.io:2002/discussions`
- Gradebook: `http://{tenant}.apps.local.openedx.io:1994/gradebook`
- Communications: `http://{tenant}.apps.local.openedx.io:1984/communications`
- ORA Grading: `http://{tenant}.apps.local.openedx.io:1993/ora-grading`
- Admin Console: `http://{tenant}.apps.local.openedx.io:2025/admin-console`
- Course Authoring: `http://{tenant}.apps.local.openedx.io:2001/authoring`
## Architecture
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ External App │────▶│ Tenant API │────▶│ eox-tenant │
│ (Your System) │ │ (Django REST) │ │ (TenantConfig) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌──────────────────┐
│ TenantProvisioningLog │
│ (Audit Trail) │
└──────────────────┘
```
## License
MIT