Table of contents:
The WebSocket API provides real-time status updates for batch user creation operations.
When a batch is processed asynchronously (i.e., the number of users to generate or import meets or exceeds :ref:`OPENWISP_RADIUS_BATCH_ASYNC_THRESHOLD <openwisp_radius_batch_async_threshold>`), the Django admin interface automatically connects to the relevant endpoint to receive live status updates without polling.
All endpoints:
- Use JSON messages.
- Require an authenticated staff user (session-based authentication).
- Push real-time updates from the server; no client message is required after the connection is established.
All WebSocket endpoints require an authenticated user.
A connection is accepted only if the user is authorized to access the requested resource. The connection is closed immediately if authorization fails.
Authentication uses the Django session cookie via AuthMiddlewareStack
(from channels.auth). DRF token authentication is not supported for
WebSocket connections.
The Origin header is validated against ALLOWED_HOSTS via
AllowedHostsOriginValidator. Cross-origin connections from untrusted
hosts are rejected.
A user is authorized if:
- The user is a superuser, OR
- The user:
- Is authenticated and marked as staff, AND
- Is an organization manager for the organization that owns the requested batch.
If any check fails, the server closes the connection without sending any message.
This endpoint delivers real-time status updates for a single batch user creation operation.
wss://<host>/ws/radius/batch/<batch-id>/
<host>: the hostname and port of the OpenWISP instance.<batch-id>: the UUID of theRadiusBatchobject to monitor.
Note
Use wss:// for HTTPS deployments and ws:// for plain HTTP
(development only). Never use ws:// in production.
A single batch user creation operation identified by its UUID.
After the connection is established, the client does not need to send any messages. The server pushes exactly one message when batch processing finishes (either successfully or with an error).
Message type: batch_status_update
{
"status": "<status>"
}The status field contains one of the following values:
| Value | Description |
|---|---|
"pending" |
The batch has been created but processing has not yet started. This value is not sent via WebSocket; it is visible only through the REST API or admin interface. |
"processing" |
The batch is currently being processed. This value is not sent via WebSocket; it is the status visible when the admin page is opened and the WebSocket connection is established. |
"completed" |
Batch processing finished successfully. This is a terminal status. |
"failed" |
Batch processing encountered an error. This is a terminal status. |
Note
The server sends exactly one message per connection, always with a
terminal status ("completed" or "failed"). The client should
close the connection after receiving it.
- The client connects to the endpoint with the batch UUID in the URL.
- If the user is authorized, the connection is accepted and the client
is added to the channel group
radius_batch_<batch-id>. - When batch processing finishes, the server sends one
batch_status_updatemessage containing the terminal status. - The client should close the connection upon receiving
"completed"or"failed". - On disconnect, the client is removed from the channel group.
Example based on the admin interface implementation:
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = protocol + "//" + window.location.host
+ "/ws/radius/batch/<batch-id>/";
const socket = new WebSocket(wsUrl);
socket.onmessage = function (event) {
const data = JSON.parse(event.data);
if (data.status === "completed" || data.status === "failed") {
socket.close();
}
};
socket.onclose = function (event) {
console.log("RadiusBatch status socket closed.");
};Replace <batch-id> with the UUID of the batch object.
WebSocket support requires server-side configuration beyond the default Django setup. The following components must be in place.
Django's default WSGI server does not support WebSockets. You must use an ASGI-compatible server such as Daphne.
Install Daphne and add it as the first entry in INSTALLED_APPS
so that Django uses it as the ASGI server:
INSTALLED_APPS = [
"daphne",
# ... other apps
"channels",
# ...
]Point Django to your project's ASGI application, which must include the Channels routing:
ASGI_APPLICATION = "your_project.routing.application"A Redis-backed channel layer is required for production deployments.
Install channels_redis and configure it:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
}
}Import openwisp_radius.routing.websocket_urlpatterns and include it
in your project's URLRouter. Example ASGI routing module:
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
from openwisp_radius.routing import websocket_urlpatterns
application = ProtocolTypeRouter(
{
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
),
"http": get_asgi_application(),
}
)