Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e3584be
Initial rewrite
stopnoanime May 24, 2025
3dd609c
Make notifications actually work
stopnoanime May 24, 2025
9d71345
Store the user_id in websocket and fix queue unsubscribe
stopnoanime May 26, 2025
237fcc1
Add notifications server auth cache
stopnoanime May 26, 2025
e9768d6
Make logging use django configuration
stopnoanime May 28, 2025
b2b18d4
Add necessary dependencies to docker image
stopnoanime Jun 1, 2025
dc308c6
Add correct types
stopnoanime Jun 1, 2025
8312d72
Add tests
stopnoanime Jun 2, 2025
4eb057e
Make notifications client recconect on error
stopnoanime Jun 5, 2025
7f9d643
Stop multiple authentications on single websocket
stopnoanime Jun 5, 2025
6d2333a
Update notifications related docs
stopnoanime Jun 5, 2025
92aae1b
Close server with descriptive error on invalid URL config
stopnoanime Jun 17, 2025
b37cdb7
Don't clear notifications error status when opening notifications dro…
stopnoanime Jun 17, 2025
3930049
Rewrite server to python websockets
stopnoanime Jun 24, 2025
07c5529
Implement graceful shutdown for notifications server
stopnoanime Jun 25, 2025
e84cb45
Add some comments
stopnoanime Jun 25, 2025
1a7b0a1
Add proper getLogger initialization
stopnoanime Jun 25, 2025
5223081
Change notifications authenticate endpoint to use Http status codes t…
stopnoanime Jun 25, 2025
a045127
Remove unused packages that were required by socketify.py
stopnoanime Jun 25, 2025
57d3ed7
Update setup.py
stopnoanime Jul 1, 2025
57d21ca
Merge branch 'master' into notifications-python
stopnoanime Sep 24, 2025
28b784c
Fix ruff errors for notifications server
stopnoanime Sep 24, 2025
225bce4
Remove unused imports
stopnoanime Sep 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion UPGRADING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ List of changes since the *CONFIG_VERSION* numbering was introduced:
* Added *NOTIFICATIONS_* options to *deployment/settings.py*::

# Notifications configuration (client)
# This one is for JavaScript socket.io client.
# This one is for JavaScript WebSocket client.
# It should contain actual URL available from remote machines.
NOTIFICATIONS_SERVER_URL = 'http://localhost:7887/'

Expand Down
2 changes: 1 addition & 1 deletion oioioi/default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@
RANKING_MAX_COOLDOWN = 100 # seconds

# Notifications configuration (client)
# This one is for JavaScript socket.io client.
# This one is for JavaScript WebSocket client.
# It should contain actual URL available from remote machines.
NOTIFICATIONS_SERVER_URL = 'http://localhost:7887/'

Expand Down
2 changes: 1 addition & 1 deletion oioioi/deployment/settings.py.template
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ ZEUS_INSTANCES = {
# RANKING_MAX_COOLDOWN = 100 # seconds

# Notifications configuration (client)
# This one is for JavaScript socket.io client.
# This one is for JavaScript WebSocket client.
# It should contain actual URL available from remote machines.
# NOTIFICATIONS_SERVER_URL = 'http://localhost:7887/'

Expand Down
8 changes: 3 additions & 5 deletions oioioi/notifications/README.rst
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
An optional module for transmitting instant notifications to users.
It consists of a Django app and a Node.js server passing notifications.
It consists of a Django app and a Python server passing notifications.
The server is tested with normal Django unit tests.

How to use:
- expose port 7887 for web container in docker-compose-dev.yml
- open container bash (easy_toolbox.py bash)
- install Node.js (sudo apt install nodejs npm)
- uncomment notifications from INSTALLED_APPS in settings.py
- uncomment notifications from context_processors in settings.py
- set following settings in settings.py:
NOTIFICATIONS_SERVER_ENABLED = True
NOTIFICATIONS_RABBITMQ_URL = 'amqp://oioioi:oioioi@broker'
NOTIFICATIONS_SERVER_URL = 'http://localhost:7887/'
- stop and start again the containers (easy_toolbox.py stop; easy_toolbox.py start)
- stop and start again the containers (easy_toolbox.py stop; easy_toolbox.py up)



Expand All @@ -23,8 +23,6 @@ If required, modify appropriate settings in settings.py file - their names begin
with "NOTIFICATIONS_" prefix.


How to run tests:
- in ./server directory, invoke: npm test

How to notify users manually:
- invoke: ./manage.py notify [options]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
import os
import asyncio

from django.conf import settings
from django.core.management.base import BaseCommand

from oioioi.notifications.server.server import Server


class Command(BaseCommand):
help = "Runs the OIOIOI notifications server"
requires_model_validation = False

def add_arguments(self, parser):
parser.add_argument(
"-i",
"--install",
action="store_true",
help="install dependencies required by the server",
)

def handle(self, *args, **options):
path = os.path.join(os.path.dirname(__file__), "..", "..", "server")
os.chdir(path)
if options["install"]:
os.execlp("env", "env", "npm", "install")
else:
os.execlp(
"env",
"env",
"node",
"ns-main.js",
"--port",
settings.NOTIFICATIONS_SERVER_PORT.__str__(),
"--url",
settings.NOTIFICATIONS_OIOIOI_URL,
"--amqp",
settings.NOTIFICATIONS_RABBITMQ_URL,
)
server = Server(settings.NOTIFICATIONS_SERVER_PORT, settings.NOTIFICATIONS_RABBITMQ_URL, settings.NOTIFICATIONS_OIOIOI_URL)
try:
asyncio.run(server.run())
except KeyboardInterrupt:
pass # Allow graceful shutdown on Ctrl+C
1 change: 0 additions & 1 deletion oioioi/notifications/server/.gitignore

This file was deleted.

Empty file.
139 changes: 0 additions & 139 deletions oioioi/notifications/server/auth.js

This file was deleted.

50 changes: 50 additions & 0 deletions oioioi/notifications/server/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import logging

import aiohttp
from cachetools import TTLCache


class Auth:
AUTH_CACHE_EXPIRATION_SECONDS = 300
AUTH_CACHE_MAX_SIZE = 10000
URL_AUTHENTICATE_SUFFIX = "notifications/authenticate/"

def __init__(self, url: str):
self.auth_url = url + self.URL_AUTHENTICATE_SUFFIX
self.auth_cache: TTLCache[str, str] = TTLCache(maxsize=self.AUTH_CACHE_MAX_SIZE, ttl=self.AUTH_CACHE_EXPIRATION_SECONDS)
self.logger = logging.getLogger(__name__)
self.http_client: aiohttp.ClientSession | None = None

async def connect(self):
self.http_client = aiohttp.ClientSession()

async def close(self):
if self.http_client is not None:
await self.http_client.close()
self.http_client = None
self.logger.info("HTTP client closed")

async def authenticate(self, session_id: str) -> str:
"""
Authenticate a user with session ID.

Returns the user ID if authentication is successful.
Raises RuntimeError if authentication fails.
"""
if self.http_client is None:
raise RuntimeError("Connection not established. Call connect() first.")

if session_id in self.auth_cache:
user_id = self.auth_cache[session_id]
self.logger.debug(f"Cache hit for session ID: {session_id} with user ID: {user_id}")
return user_id

async with self.http_client.post(self.auth_url, data={"nsid": session_id}, headers={"Content-Type": "application/x-www-form-urlencoded"}) as response:
response.raise_for_status()
result = await response.json()

user_id = result["user"]
self.auth_cache[session_id] = user_id

self.logger.debug(f"Authenticated session ID: {session_id} with user ID: {user_id}")
return user_id
Loading