diff --git a/app/back-end/dashboards/consumers.py b/app/back-end/dashboards/consumers.py index a7402cf7..af3c37e5 100644 --- a/app/back-end/dashboards/consumers.py +++ b/app/back-end/dashboards/consumers.py @@ -8,7 +8,6 @@ from channels.db import database_sync_to_async from authentication.models import User from django.db.models import F - @database_sync_to_async def get_user(user_id): try: @@ -34,73 +33,21 @@ def update_user_offline(user_id): except User.DoesNotExist: pass -async def send_online_notification(self, user_id): - await self.channel_layer.group_send( - "online_users", - { - "type": "send_online_notification", - "user_id": user_id, - } - ) - -async def send_online_notification_to_socket(self, event): - user_id = event["user_id"] - user = await get_user(user_id) - await self.send(text_data=json.dumps({ - "type": "online_notification", - "user": { - "id": user.id, - "username": user.username, - "image_url": user.image_url, - # Add any other relevant user data - } - })) - -async def send_online_notification(self, event): - await self.send_online_notification_to_socket(event) - class UserStatusConsumer(AsyncWebsocketConsumer): async def connect(self): - auth_headers = self.scope.get("headers", []) - auth_header = None - for header in auth_headers: - if header[0].decode() == "access": - auth_header = header[1].decode() - break - if auth_header: - try: - access_token = AccessToken(auth_header) - user_id = access_token.payload["user_id"] - print(user_id, file = sys.stderr) - self.user = get_user(user_id) - self.user_id = user_id - await self.accept() - await update_user_online(self.user_id) - await self.send_online_notification(self.user_id) # Send online notification - await self.channel_layer.group_add( - "online_users", - self.channel_name - ) - except Exception as e: - print(f"Authentication error: {e}", file=sys.stderr) - await self.close() - else: - print("Anonymous user", file=sys.stderr) - await self.accept() + await update_user_online(self.scope['user'].id) + await self.accept() async def disconnect(self, close_code): - await self.channel_layer.group_discard( - "online_users", - self.channel_name - ) - await update_user_offline(self.user_id) + await update_user_offline(self.scope['user'].id) async def receive(self, text_data): - if self.user: + user = self.scope['user'] + if user: try: text_data_json = json.loads(text_data) if text_data_json.get("action") == "get_friends": - data = await get_friends(self.user_id) + data = await get_friends(user.id) await self.send(text_data=json.dumps({ "friends": data })) diff --git a/app/back-end/dashboards/models.py b/app/back-end/dashboards/models.py index aaee9948..e8307b98 100644 --- a/app/back-end/dashboards/models.py +++ b/app/back-end/dashboards/models.py @@ -7,5 +7,7 @@ class Friendship(models.Model): user_to = models.ForeignKey('authentication.User', models.DO_NOTHING, db_column='user_to', related_name = "user_to_set") is_accepted = models.BooleanField(default=False) + u_one_is_blocked_u_two = models.BooleanField(default=False) + u_two_is_blocked_u_one = models.BooleanField(default=False) class Meta: db_table = 'Friendship' \ No newline at end of file diff --git a/app/back-end/ft_transcendence/asgi.py b/app/back-end/ft_transcendence/asgi.py index 343eabe2..9e6ae5ad 100644 --- a/app/back-end/ft_transcendence/asgi.py +++ b/app/back-end/ft_transcendence/asgi.py @@ -13,17 +13,19 @@ from django.core.asgi import get_asgi_application from dashboards import routing as dashboard_routing from game import routing as game_routing +from django_channels_jwt_auth_middleware.auth import JWTAuthMiddlewareStack os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ft_transcendence.settings') django.setup() application = ProtocolTypeRouter({ 'http': get_asgi_application(), - 'websocket': + 'websocket': JWTAuthMiddlewareStack( URLRouter ( dashboard_routing.websocket_urlpatterns + game_routing.websocket_urlpatterns ) + ) }) diff --git a/app/back-end/ft_transcendence/settings.py b/app/back-end/ft_transcendence/settings.py index 2c553b76..998c6362 100644 --- a/app/back-end/ft_transcendence/settings.py +++ b/app/back-end/ft_transcendence/settings.py @@ -110,7 +110,7 @@ } SIMPLE_JWT = { - "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60), + "ACCESS_TOKEN_LIFETIME": timedelta(days=30), "REFRESH_TOKEN_LIFETIME": timedelta(days=1), "ROTATE_REFRESH_TOKENS": False, "BLACKLIST_AFTER_ROTATION": False, diff --git a/app/back-end/requirements.txt b/app/back-end/requirements.txt index d80ed918..ef4ec662 100644 --- a/app/back-end/requirements.txt +++ b/app/back-end/requirements.txt @@ -22,3 +22,4 @@ channels faker asgi_redis channels_redis +django-channels-jwt-auth-middleware diff --git a/app/front-end/src/components/authChecker.tsx b/app/front-end/src/components/authChecker.tsx index 4ac86fe7..a870de86 100644 --- a/app/front-end/src/components/authChecker.tsx +++ b/app/front-end/src/components/authChecker.tsx @@ -1,52 +1,86 @@ "use client" - import React, { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import Cookies from 'js-cookie'; import Spinner from 'react-bootstrap/Spinner' import styles from './styles/authChecker.module.css' - const AuthChecker = ({ children }: { children: React.ReactNode }) => { - const router = useRouter(); - const [isAuthenticated, setIsAuthenticated] = useState(null); + const router = useRouter(); + const [isAuthenticated, setIsAuthenticated] = useState(null); + const [socket, setSocket] = useState(null); + const [onlineUsers, setOnlineUsers] = useState<{ id: number; username: string }[]>([]); - useEffect(() => { - const authentication = async () => { - const access = Cookies.get('access'); - console.log(access) - if (access) { - const response = await fetch('http://localhost:8000/api/verify', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ token: access }) - }); - if (response.ok) { - setIsAuthenticated(true); - } else { - setIsAuthenticated(false); - router.push('/sign-in'); - } + useEffect(() => { + const authentication = async () => { + const access = Cookies.get('access'); + console.log(access) + if (access) { + const response = await fetch('http://localhost:8000/api/verify', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ token: access }) + }); + if (response.ok) { + setIsAuthenticated(true); + const newSocket = new WebSocket(`ws://localhost:8000/ws/user-status?token=${access}`); + newSocket.addEventListener('open', () => { + newSocket.send(JSON.stringify({ access: access })); + newSocket.send(JSON.stringify({ action: 'get_friends' })); + }); + newSocket.addEventListener('message', (event) => { + const data = JSON.parse(event.data); + if (data.type === 'online_users') { + setOnlineUsers(data.users); + } else if (data.type === 'online_notification') { + setOnlineUsers((prevUsers) => [...prevUsers, data.user]); } - else - { - setIsAuthenticated(false); - router.push('/sign-in'); - } - }; - authentication(); - }, []); + }); + setSocket(newSocket); + } else { + setIsAuthenticated(false); + router.push('/sign-in'); + } + } else { + setIsAuthenticated(false); + router.push('/sign-in'); + } + }; + authentication(); + }, []); + + useEffect(() => { + return () => { + if (socket) { + socket.close(); + } + }; + }, [socket]); - if (isAuthenticated === null) { - return ( -
- -

LOADING ...

-
- ); - } + if (isAuthenticated === null) { + return ( +
+ +

LOADING ...

+
+ ); + } - return isAuthenticated ? <>{children} : null; + return isAuthenticated ? ( + <> + {children} +
+

Online Users

+
    + {onlineUsers.map((user) => ( +
  • {user.username}
  • + ))} +
+
+ + ) : null; }; -export default AuthChecker; +export default AuthChecker; \ No newline at end of file