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
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
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:
```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)
else:
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 typing import Literal, Optional
import django
import websockets
from django.conf import settings
from django.core.signing import TimestampSigner
@ -12,13 +11,6 @@ from pydantic import BaseModel, ValidationError
from websockets import WebSocketClientProtocol
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
# 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)
async def main():
def run(host, port):
if not settings.WEBSOCKET_ENABLED:
msg = (
"WEBSOCKET_ENABLED should be set to True to run the WebSocket Server. "
@ -92,12 +84,9 @@ async def main():
print(msg)
exit(1)
host = settings.WEBSOCKET_BACK_HOST
port = settings.WEBSOCKET_BACK_PORT
async def _serve():
async with serve(handler, host, port):
print(f"Waiting for connections on {host}:{port}")
await asyncio.Future() # run forever
async with serve(handler, host, port):
print(f"Waiting for connections on {host}:{port}")
await asyncio.Future() # run forever
asyncio.run(main())
asyncio.run(_serve())