feat(websockets): run the WS server as a django management command.

This allows to handle the loading of the settings in a consistant way,
and aditionnaly to provide a way to override the `WEBSOCKET_BACK_HOST`
and `WEBSOCKET_BACK_PORT` settings with arg commands `--host` and
`--port`.

Without this change, because of how we are currently loading our
settings, we would require the settings the be exposed by the
`umap.settings.__init__` file.

Previous implementations were exposing these settings, with the
following code:

```python
settings_as_dict = {k: v for k, v in globals().items() if k.isupper()}
  ```
This commit is contained in:
Alexis Métaireau 2024-05-31 12:32:02 +02:00
parent 940ae77602
commit d91c86e7fa
4 changed files with 34 additions and 22 deletions

View file

@ -279,12 +279,15 @@ Setting `WEBSOCKET_ENABLED` to `True` will **not** enable real-time
collaboration on all the maps served by the server. Instead, a switch will be collaboration on all the maps served by the server. Instead, a switch will be
available in the "advanced properties" of the map. available in the "advanced properties" of the map.
The websocket server can be run with the following command: The websocket server can be started with the following command:
```bash ```bash
python -m umap.ws umap run_websocket_server
``` ```
And can take optional settings `--host` and `--port` (default values are defined in
the settings).
Configuration example: Configuration example:
```python ```python

View file

@ -0,0 +1,23 @@
from django.conf import settings
from django.core.management.base import BaseCommand
from umap import ws
class Command(BaseCommand):
help = "Run the websocket server"
def add_arguments(self, parser):
parser.add_argument(
"--host",
help="The server host to bind to.",
default=settings.WEBSOCKET_BACK_HOST,
)
parser.add_argument(
"--port",
help="The server port to bind to.",
default=settings.WEBSOCKET_BACK_PORT,
)
def handle(self, *args, **options):
ws.run(options["host"], options["port"])

View file

@ -43,6 +43,3 @@ if path:
globals()["STATICFILES_DIRS"].insert(0, value) globals()["STATICFILES_DIRS"].insert(0, value)
else: else:
globals()[key] = value globals()[key] = value
# Expose these settings for consumption by e.g. django.settings.configure.
settings_as_dict = {k: v for k, v in globals().items() if k.isupper()}

View file

@ -4,7 +4,6 @@ import asyncio
from collections import defaultdict from collections import defaultdict
from typing import Literal, Optional from typing import Literal, Optional
import django
import websockets import websockets
from django.conf import settings from django.conf import settings
from django.core.signing import TimestampSigner from django.core.signing import TimestampSigner
@ -12,13 +11,6 @@ from pydantic import BaseModel, ValidationError
from websockets import WebSocketClientProtocol from websockets import WebSocketClientProtocol
from websockets.server import serve from websockets.server import serve
# This needs to run before the django-specific imports
# See https://docs.djangoproject.com/en/5.0/topics/settings/#calling-django-setup-is-required-for-standalone-django-usage
from umap.settings import settings_as_dict
settings.configure(**settings_as_dict)
django.setup()
from umap.models import Map, User # NOQA from umap.models import Map, User # NOQA
# Contains the list of websocket connections handled by this process. # Contains the list of websocket connections handled by this process.
@ -81,7 +73,7 @@ async def handler(websocket):
await join_and_listen(map_id, permissions, user, websocket) await join_and_listen(map_id, permissions, user, websocket)
async def main(): def run(host, port):
if not settings.WEBSOCKET_ENABLED: if not settings.WEBSOCKET_ENABLED:
msg = ( msg = (
"WEBSOCKET_ENABLED should be set to True to run the WebSocket Server. " "WEBSOCKET_ENABLED should be set to True to run the WebSocket Server. "
@ -92,12 +84,9 @@ async def main():
print(msg) print(msg)
exit(1) exit(1)
host = settings.WEBSOCKET_BACK_HOST async def _serve():
port = settings.WEBSOCKET_BACK_PORT
async with serve(handler, host, port): async with serve(handler, host, port):
print(f"Waiting for connections on {host}:{port}") print(f"Waiting for connections on {host}:{port}")
await asyncio.Future() # run forever await asyncio.Future() # run forever
asyncio.run(_serve())
asyncio.run(main())