Compare commits
No commits in common. "Chatte" and "main" have entirely different histories.
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
.env
|
||||
*.pyc
|
||||
db.sqlite3
|
||||
**/migrations/**
|
||||
/profiles/static/avatars/*
|
||||
!/profiles/static/avatars/default.env
|
40
README.md
40
README.md
@ -1,38 +1,4 @@
|
||||
# BACKEND
|
||||
# TRANSCENDENCE
|
||||
The last project of the 42 common core
|
||||
|
||||
## Installation
|
||||
|
||||
- Clone the project:
|
||||
``` bash
|
||||
git clone https://git.chauvet.pro/michel/ft_transcendence
|
||||
cd ft_transcendence
|
||||
git switch server
|
||||
```
|
||||
- Create python virtual environnement.
|
||||
``` bash
|
||||
python3 -m venv .env
|
||||
```
|
||||
- Source the environnement.
|
||||
``` bash
|
||||
source .env/bin/activate
|
||||
```
|
||||
- Install the requirements
|
||||
``` bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
- Setup database
|
||||
```
|
||||
python manage.py makemigrations games
|
||||
python manage.py makemigrations profiles
|
||||
python manage.py makemigrations chat
|
||||
python manage.py migrate
|
||||
```
|
||||
- Start the developpement server
|
||||
```
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
```
|
||||
|
||||
coc nvim
|
||||
```
|
||||
pip install django-stubs
|
||||
```
|
||||
# adrien est une merde
|
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'accounts'
|
@ -1,12 +0,0 @@
|
||||
from rest_framework.serializers import Serializer, CharField
|
||||
from django.contrib.auth import authenticate
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
class LoginSerializer(Serializer):
|
||||
|
||||
username = CharField()
|
||||
password = CharField()
|
||||
|
||||
def get_user(self, data):
|
||||
user = authenticate(username=data['username'], password=data['password'])
|
||||
return user
|
@ -1,12 +0,0 @@
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class RegisterSerialiser(ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'password']
|
||||
|
||||
def create(self, data):
|
||||
user_obj = User.objects.create_user(username=data['username'], password=data['password'])
|
||||
user_obj.save()
|
||||
return user_obj
|
@ -1,5 +0,0 @@
|
||||
from .register import *
|
||||
from .login import *
|
||||
from .logout import *
|
||||
from .edit import *
|
||||
from .delete import *
|
@ -1,37 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from django.test.client import Client
|
||||
from django.http import HttpResponse
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
import uuid
|
||||
|
||||
class DeleteTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
self.url = "/api/accounts/delete"
|
||||
|
||||
self.username: str = str(uuid.uuid4())
|
||||
self.password: str = str(uuid.uuid4())
|
||||
|
||||
user: User = User.objects.create_user(username=self.username, password=self.password)
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
|
||||
|
||||
def test_normal_delete(self):
|
||||
response: HttpResponse = self.client.delete(self.url, {"password": self.password}, content_type='application/json')
|
||||
response_text: str = response.content.decode("utf-8")
|
||||
self.assertEqual(response_text, '"user deleted"')
|
||||
|
||||
def test_wrong_pass(self):
|
||||
response: HttpResponse = self.client.delete(self.url, {"password": "cacaman a frapper"}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {"password": ["Password wrong."]})
|
||||
|
||||
def test_no_logged(self):
|
||||
self.client.logout()
|
||||
response: HttpResponse = self.client.delete(self.url, {"password": self.password}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {"detail":"Authentication credentials were not provided."})
|
@ -1,49 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from django.test.client import Client
|
||||
from django.http import HttpResponse
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
import uuid
|
||||
|
||||
class EditTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
self.url = "/api/accounts/edit"
|
||||
|
||||
self.username: str = str(uuid.uuid4())
|
||||
self.password: str = str(uuid.uuid4())
|
||||
self.new_password: str = str(uuid.uuid4())
|
||||
|
||||
User.objects.create_user(username = self.username, password = self.password)
|
||||
|
||||
def test_normal(self):
|
||||
self.client.login(username = self.username, password = self.password)
|
||||
response: HttpResponse = self.client.patch(self.url, {"current_password": self.password, "new_password": self.new_password, "username": "bozo"}, content_type='application/json')
|
||||
response_text: str = response.content.decode('utf-8')
|
||||
self.assertEqual(response_text, '"data has been alterate"')
|
||||
|
||||
def test_invalid_current_password(self):
|
||||
self.client.login(username = self.username, password = self.password)
|
||||
response: HttpResponse = self.client.patch(self.url, {"current_password": "bozo", "new_password": self.new_password, "username": "bozo"}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {"current_password":["Password is wrong."]})
|
||||
|
||||
def test_invalid_new_username_blank(self):
|
||||
self.client.login(username = self.username, password = self.password)
|
||||
response: HttpResponse = self.client.patch(self.url, {"current_password": self.password, "username": " "}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {'username': ['This field may not be blank.']})
|
||||
|
||||
def test_invalid_new_username_char(self):
|
||||
self.client.login(username = self.username, password = self.password)
|
||||
response: HttpResponse = self.client.patch(self.url, {"current_password": self.password, "username": "*&"}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {'username': ['Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters.']})
|
||||
|
||||
def test_nologged(self):
|
||||
response: HttpResponse = self.client.patch(self.url, {"current_password": self.password, "new_password": self.new_password}, content_type='application/json')
|
||||
errors: dict = eval(response.content)
|
||||
self.assertDictEqual(errors, {'detail': 'Authentication credentials were not provided.'})
|
@ -1,53 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse
|
||||
import uuid
|
||||
|
||||
class LoginTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
self.url = "/api/accounts/login"
|
||||
|
||||
self.username: str = str(uuid.uuid4())
|
||||
self.password: str = str(uuid.uuid4())
|
||||
|
||||
User.objects.create_user(username=self.username, password=self.password)
|
||||
|
||||
def test_normal_login(self):
|
||||
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
|
||||
response_text = response.content.decode('utf-8')
|
||||
#self.assertEqual(response_text, 'user connected')
|
||||
|
||||
def test_invalid_username(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"username": self.password, "password": self.password})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'user': ['Username or password wrong.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_invalid_password(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"username": self.username, "password": self.username})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'user': ['Username or password wrong.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_invalid_no_username(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"password": self.password})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'username': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_invalid_no_password(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"username": self.username})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'password': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_invalid_no_password_no_username(self):
|
||||
response: HttpResponse = self.client.post(self.url, {})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'username': ['This field is required.'], 'password': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
@ -1,17 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth import login
|
||||
|
||||
class LoginTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
self.url = "/api/accounts/logout"
|
||||
|
||||
self.client.login()
|
||||
|
||||
def test_normal_logout(self):
|
||||
response: HttpResponse = self.client.post(self.url)
|
||||
self.assertNotIn('_auth_user_id', self.client.session)
|
@ -1,52 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from rest_framework import status
|
||||
from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse
|
||||
import uuid
|
||||
|
||||
class RegisterTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
self.url: str = "/api/accounts/register"
|
||||
|
||||
self.username: str = str(uuid.uuid4())
|
||||
self.password: str = str(uuid.uuid4())
|
||||
|
||||
def test_normal_register(self):
|
||||
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
|
||||
def test_incomplet_form_no_username_no_password(self):
|
||||
response: HttpResponse = self.client.post(self.url)
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'username': ['This field is required.'], 'password': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_incomplet_form_no_password(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"username": self.username})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'password': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_incomplet_form_no_username(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"password": self.password})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_incomplet_form_no_username(self):
|
||||
response: HttpResponse = self.client.post(self.url, {"password": self.password})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'username': ['This field is required.']}
|
||||
self.assertEqual(errors, errors_expected)
|
||||
|
||||
def test_already_registered(self):
|
||||
User(username=self.username, password=self.password).save()
|
||||
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
|
||||
errors: dict = eval(response.content)
|
||||
errors_expected: dict = {'username': ['A user with that username already exists.']}
|
||||
self.assertEqual(errors, errors_expected)
|
@ -1,13 +0,0 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import register, login, logout, delete, edit, logged
|
||||
|
||||
urlpatterns = [
|
||||
path("register", register.RegisterView.as_view(), name="register"),
|
||||
path("login", login.LoginView.as_view(), name="login"),
|
||||
path("logout", logout.LogoutView.as_view(), name="logout"),
|
||||
path("logged", logged.LoggedView.as_view(), name="logged"),
|
||||
path("delete", delete.DeleteView.as_view(), name="delete"),
|
||||
path("edit", edit.EditView.as_view(), name="change_password")
|
||||
|
||||
]
|
@ -1,21 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework.response import Response
|
||||
from django.contrib.auth import logout
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
class DeleteView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
def delete(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
password: str = data["password"]
|
||||
if (password is None):
|
||||
return Response({"password": ["This field may not be blank."]})
|
||||
if (request.user.check_password(password) == False):
|
||||
return Response({"password": ["Password wrong."]})
|
||||
request.user.delete()
|
||||
logout(request)
|
||||
return Response("user deleted", status=status.HTTP_200_OK)
|
@ -1,45 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from django.contrib.auth.models import User
|
||||
import re
|
||||
|
||||
class EditView(APIView):
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
return Response({"username": request.user.username, "id": request.user.pk})
|
||||
|
||||
def patch(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
current_password: str = data.get("current_password")
|
||||
if (current_password is None):
|
||||
return Response({"current_password": ["This field may not be blank."]})
|
||||
|
||||
user_object = request.user
|
||||
|
||||
if (user_object.check_password(current_password) == False):
|
||||
return Response({"current_password": ["Password is wrong."]})
|
||||
|
||||
new_username = data.get("username", user_object.username)
|
||||
if (new_username != user_object.username):
|
||||
if (User.objects.filter(username=new_username).exists()):
|
||||
return Response({"username": ["A user with that username already exists."]})
|
||||
if (set(new_username) == {' '}):
|
||||
return Response({"username": ["This field may not be blank."]})
|
||||
if (re.search('^([a-z]||\@||\+||\-||\_)+$', new_username) is None):
|
||||
return Response({"username":["Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters."]})
|
||||
|
||||
new_password: str = data.get("password")
|
||||
if (new_password is not None):
|
||||
user_object.set_password(new_password)
|
||||
|
||||
user_object.save()
|
||||
|
||||
return Response("data has been alterate")
|
@ -1,18 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
class LoggedView(APIView):
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
if (request.user.is_authenticated):
|
||||
return Response({'id': request.user.pk}, status=status.HTTP_200_OK)
|
||||
return Response('false', status=status.HTTP_200_OK)
|
@ -1,23 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
class LoginView(APIView):
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
data = request.data
|
||||
serializer = LoginSerializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
user = serializer.get_user(data)
|
||||
if user is None:
|
||||
return Response({'user': ['Username or password wrong.']}, status.HTTP_200_OK)
|
||||
login(request, user)
|
||||
return Response({'id': user.pk}, status=status.HTTP_200_OK)
|
@ -1,13 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from django.contrib.auth import logout
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
def get(self, request: HttpRequest):
|
||||
logout(request)
|
||||
return Response("user unlogged", status=status.HTTP_200_OK)
|
@ -1,18 +0,0 @@
|
||||
from rest_framework import permissions, status
|
||||
from ..serializers.register import RegisterSerialiser
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
|
||||
class RegisterView(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
def post(self, request: HttpRequest):
|
||||
data = request.data
|
||||
serializer = RegisterSerialiser(data=data)
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
user = serializer.create(data)
|
||||
if user:
|
||||
login(request, user)
|
||||
return Response("user created", status=status.HTTP_201_CREATED)
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
@ -1,6 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from .models import ChannelModel, MemberModel, MessageModel
|
||||
|
||||
admin.site.register(ChannelModel)
|
||||
admin.site.register(MemberModel)
|
||||
admin.site.register(MessageModel)
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ChatConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'chat'
|
@ -1,94 +0,0 @@
|
||||
import json
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
from asgiref.sync import async_to_sync
|
||||
from .models import MemberModel, MessageModel
|
||||
from profiles.models import BlockModel
|
||||
import time
|
||||
|
||||
class ChatConsumer(WebsocketConsumer):
|
||||
def connect(self):
|
||||
channel_id : str = self.scope['path'].split('/')[3]
|
||||
|
||||
self.room_group_name = 'chat' + channel_id
|
||||
|
||||
user = self.scope["user"]
|
||||
if (user.is_anonymous or not user.is_authenticated):
|
||||
return
|
||||
|
||||
if MemberModel.objects.filter(member_id=user.pk, channel_id=int(channel_id)).count() != 1:
|
||||
return
|
||||
|
||||
if (self.channel_layer == None):
|
||||
return
|
||||
|
||||
async_to_sync(self.channel_layer.group_add)(
|
||||
self.room_group_name,
|
||||
self.channel_name
|
||||
)
|
||||
|
||||
self.accept()
|
||||
|
||||
|
||||
def receive(self, text_data=None, bytes_data=None):
|
||||
if text_data == None:
|
||||
return
|
||||
|
||||
text_data_json = json.loads(text_data)
|
||||
message = text_data_json['message']
|
||||
receivers_id = text_data_json['receivers_id']
|
||||
|
||||
print(text_data)
|
||||
|
||||
channel_id : int = int(self.scope['path'].split('/')[3])
|
||||
user = self.scope["user"]
|
||||
if (user.is_anonymous or not user.is_authenticated):
|
||||
return
|
||||
|
||||
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
|
||||
return
|
||||
|
||||
if (self.channel_layer == None):
|
||||
return
|
||||
|
||||
message_time : int = int(time.time() * 1000)
|
||||
|
||||
if (len(receivers_id) == 1 and
|
||||
BlockModel.objects.filter(blocker=user.pk, blocked=receivers_id[0]) or
|
||||
BlockModel.objects.filter(blocker=receivers_id[0], blocked=user.pk)
|
||||
):
|
||||
return
|
||||
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.room_group_name,
|
||||
{
|
||||
'type':'chat_message',
|
||||
'author_id':user.pk,
|
||||
'content':message,
|
||||
'time':message_time,
|
||||
}
|
||||
)
|
||||
|
||||
new_message = MessageModel()
|
||||
new_message.channel_id = channel_id
|
||||
new_message.author_id = user.pk
|
||||
new_message.content = message
|
||||
new_message.time = message_time
|
||||
new_message.save()
|
||||
|
||||
|
||||
def chat_message(self, event):
|
||||
|
||||
channel_id : int = int(self.scope['path'].split('/')[3])
|
||||
user = self.scope["user"]
|
||||
if (user.is_anonymous or not user.is_authenticated):
|
||||
return
|
||||
|
||||
if MemberModel.objects.filter(member_id=user.pk, channel_id=channel_id).count() != 1:
|
||||
return
|
||||
|
||||
self.send(text_data=json.dumps({
|
||||
'type':'chat',
|
||||
'author_id':event['author_id'],
|
||||
'content':event['content'],
|
||||
'time': event['time'],
|
||||
}))
|
@ -1,24 +0,0 @@
|
||||
from django.db import models
|
||||
from django.db.models import IntegerField
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib import admin
|
||||
|
||||
# Create your models here.
|
||||
class ChannelModel(models.Model):
|
||||
pass
|
||||
|
||||
class MemberModel(models.Model):
|
||||
member_id = IntegerField(primary_key=False)
|
||||
channel_id = IntegerField(primary_key=False)
|
||||
|
||||
def __str__(self):
|
||||
return "member_id: " + str(self.member_id) + ", channel_id: " + str(self.channel_id)
|
||||
|
||||
class MessageModel(models.Model):
|
||||
channel_id = IntegerField(primary_key=False)
|
||||
author_id = IntegerField(primary_key=False)
|
||||
content = models.CharField(max_length=255)
|
||||
time = IntegerField(primary_key=False)
|
||||
|
||||
def __str__(self):
|
||||
return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content
|
@ -1,6 +0,0 @@
|
||||
from django.urls import re_path
|
||||
from . import consumers
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'ws/chat/(?P<chat_id>\d+)/$', consumers.ChatConsumer.as_asgi())
|
||||
]
|
@ -1,8 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ChannelModel
|
||||
|
||||
class ChannelSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ChannelModel
|
||||
fields = []
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
10
chat/urls.py
10
chat/urls.py
@ -1,10 +0,0 @@
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("<int:pk>", views.ChatView.as_view(), name="chat_page"),
|
||||
path("", views.ChatsView.as_view(), name="chats_page"),
|
||||
]
|
@ -1,79 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import authentication, permissions, status
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from .models import ChannelModel, MemberModel, MessageModel
|
||||
from django.core import serializers
|
||||
|
||||
class ChatView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request, pk):
|
||||
if (ChannelModel.objects.filter(pk=pk)):
|
||||
return Response({'channel_id': pk}, status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response("Channel doesn't exist", status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def delete(self, request, pk):
|
||||
|
||||
ChannelModel.objects.filter(pk=pk).delete()
|
||||
MessageModel.objects.filter(pk=pk).delete()
|
||||
MemberModel.objects.filter(pk=pk).delete()
|
||||
|
||||
return Response({'channel_id': pk}, status=status.HTTP_200_OK)
|
||||
|
||||
class ChatsView(APIView):
|
||||
def post(self, request):
|
||||
|
||||
data: dict = request.data
|
||||
users_id = request.data.get("users_id", [])
|
||||
if len(users_id) < 2:
|
||||
return Response('Not enought members to create the channel', status=status.HTTP_400_BAD_REQUEST)
|
||||
if users_id[0] == users_id[1]:
|
||||
return Response('Same member', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
for user_id1 in users_id:
|
||||
for member1 in MemberModel.objects.filter(member_id=user_id1):
|
||||
for user_id2 in users_id:
|
||||
if user_id1 == user_id2:
|
||||
continue
|
||||
for member2 in MemberModel.objects.filter(member_id=user_id2):
|
||||
if (member1.channel_id == member2.channel_id):
|
||||
messages = MessageModel.objects.filter(channel_id=member1.channel_id).order_by("time")
|
||||
messages = serializers.serialize("json", messages)
|
||||
return Response({'channel_id': member1.channel_id, 'messages':messages}, status=status.HTTP_200_OK)
|
||||
|
||||
new_channel = ChannelModel()
|
||||
new_channel.save()
|
||||
|
||||
for user_id in users_id:
|
||||
|
||||
new_member = MemberModel()
|
||||
new_member.channel_id = new_channel.pk
|
||||
new_member.member_id = user_id
|
||||
new_member.save()
|
||||
|
||||
return Response({'channel_id': new_channel.pk}, status=status.HTTP_201_CREATED)
|
||||
|
||||
def delete(self, request):
|
||||
|
||||
data: dict = request.data
|
||||
users_id = request.data.get("users_id", [])
|
||||
|
||||
#print(list(MemberModel.objects.all()))
|
||||
|
||||
for user_id1 in users_id:
|
||||
for member1 in MemberModel.objects.filter(member_id=user_id1):
|
||||
for user_id2 in users_id:
|
||||
if user_id1 == user_id2:
|
||||
break
|
||||
for member2 in MemberModel.objects.filter(member_id=user_id2):
|
||||
if (member1.channel_id == member2.channel_id):
|
||||
MessageModel.objects.filter(channel_id=member1.channel_id).delete()
|
||||
member1.delete()
|
||||
member2.delete()
|
||||
ChannelModel.objects.get(pk=member1.channel_id).delete()
|
||||
return Response("Channel removed", status=status.HTTP_200_OK)
|
||||
|
||||
return Response("Channel doesn't exist", status=status.HTTP_404_NOT_FOUND)
|
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FrontendConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'frontend'
|
@ -1,3 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
@ -1,12 +0,0 @@
|
||||
#app .form {
|
||||
background-color: red;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
grid-gap: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 90px;
|
||||
border: 15px black solid;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#app .form {
|
||||
background-color: red;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
grid-gap: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 90px;
|
||||
border: 15px black solid;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
|
||||
body {
|
||||
margin: 0.5em;
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #009579;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#app #main .account
|
||||
{
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
#app #main
|
||||
{
|
||||
width: 60%;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#app #main .profile
|
||||
{
|
||||
background-color: green;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#app #avatar
|
||||
{
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
#app #username
|
||||
{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#app #block {
|
||||
cursor: pointer;
|
||||
font-size: 0.7em;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#app {
|
||||
margin-top: 20px;
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
#app img
|
||||
{
|
||||
max-height: 3em;
|
||||
max-width: 3em;
|
||||
}
|
||||
|
||||
#app ul
|
||||
{
|
||||
font-size: 0.75em;
|
||||
margin: 0.25em 0 0 0;
|
||||
padding: 0 0 0 0;
|
||||
list-style-type: none;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#app li
|
||||
{
|
||||
margin: 0.25em 0.25em 0 0;
|
||||
}
|
||||
|
||||
#app #chats {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
text-align: left;
|
||||
}
|
||||
#app #users {
|
||||
margin: 0em 1.0em 0em 0.05em;
|
||||
}
|
||||
|
||||
#app #chat {
|
||||
position: relative;
|
||||
max-height: 100vh;
|
||||
width: 100vh;
|
||||
/*border: 2px solid green;*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#app #members {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#app #add_chat_off {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#app #add_chat_on {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#app #messages {
|
||||
max-height: 60vh;
|
||||
overflow: scroll;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
#app #input_user{
|
||||
color: green;
|
||||
width: 8.5em;
|
||||
height: 1.1em;
|
||||
font-size: 0.65em;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-bottom: 0.15em solid green;
|
||||
}
|
||||
|
||||
#app #input_chat{
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
/*width: calc(100% - 8px);*/
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-bottom: 0.2em solid green;
|
||||
caret-color: green;
|
||||
color: green;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#app #you {
|
||||
text-align: left;
|
||||
position: relative;
|
||||
max-width: 48%;
|
||||
left: 0.5em;
|
||||
margin: 0.5em 0 0 0;
|
||||
color: green;
|
||||
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#app #other {
|
||||
text-align: right;
|
||||
position: relative;
|
||||
max-width: 48%;
|
||||
margin: 0.5em 0 0 auto;
|
||||
right: 0.5em;
|
||||
color: red;
|
||||
|
||||
/* permet le retour à la ligne à la place de dépasser*/
|
||||
word-wrap: break-word;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { Profile } from "./profile.js";
|
||||
|
||||
class MyProfile extends Profile
|
||||
{
|
||||
async change_avatar(form_data)
|
||||
{
|
||||
let response = await this.client._patch_file(`/api/profiles/me`, form_data);
|
||||
let response_data = await response.json()
|
||||
|
||||
return response_data;
|
||||
}
|
||||
|
||||
async init()
|
||||
{
|
||||
super.init("me");
|
||||
}
|
||||
}
|
||||
|
||||
export {MyProfile}
|
@ -1,72 +0,0 @@
|
||||
import { Client } from "./client.js";
|
||||
|
||||
class Account
|
||||
{
|
||||
/**
|
||||
* @param {Client} client
|
||||
*/
|
||||
constructor (client)
|
||||
{
|
||||
/**
|
||||
* @type {Client} client
|
||||
*/
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async create(username, password)
|
||||
{
|
||||
let response = await this.client._post("/api/accounts/register", {username: username, password: password});
|
||||
let response_data = await response.json()
|
||||
|
||||
if (response_data == "user created")
|
||||
{
|
||||
this._logged = true;
|
||||
return null;
|
||||
}
|
||||
return response_data
|
||||
}
|
||||
|
||||
async delete(password)
|
||||
{
|
||||
let response = await this.client._delete("/api/accounts/delete", {password: password});
|
||||
let response_data = await response.json();
|
||||
|
||||
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
|
||||
{
|
||||
this.client._logged = false;
|
||||
return null;
|
||||
}
|
||||
if (response_data == "user deleted")
|
||||
this.client._logged = false;
|
||||
return response_data;
|
||||
}
|
||||
|
||||
async get()
|
||||
{
|
||||
let response = await this.client._get("/api/accounts/edit");
|
||||
let response_data = await response.json();
|
||||
|
||||
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
|
||||
{
|
||||
this.client._logged = false;
|
||||
return null;
|
||||
}
|
||||
return response_data;
|
||||
}
|
||||
|
||||
async update(data, password)
|
||||
{
|
||||
data.current_password = password;
|
||||
let response = await this.client._patch_json("/api/accounts/edit", data);
|
||||
let response_data = await response.json();
|
||||
|
||||
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
|
||||
{
|
||||
this.client._;
|
||||
return null;
|
||||
}
|
||||
return response_data;
|
||||
}
|
||||
}
|
||||
|
||||
export { Account }
|
@ -1,78 +0,0 @@
|
||||
import {Message} from "./message.js"
|
||||
|
||||
class Channel {
|
||||
constructor(client, channel_id, members_id, messages, reload) {
|
||||
this.client = client;
|
||||
this.channel_id = channel_id;
|
||||
this.members_id = members_id;
|
||||
this.messages = [];
|
||||
if (messages != undefined)
|
||||
this.updateMessages(messages);
|
||||
|
||||
this.connect(reload);
|
||||
}
|
||||
|
||||
// reload = function to use when we receive a message
|
||||
async connect(reload) {
|
||||
let url = `ws://${window.location.host}/ws/chat/${this.channel_id}/`;
|
||||
|
||||
this.chatSocket = new WebSocket(url);
|
||||
this.chatSocket.onmessage = (event) =>{
|
||||
let data = JSON.parse(event.data)
|
||||
|
||||
this.messages.push(new Message(
|
||||
this.channel_id,
|
||||
data.author_id,
|
||||
data.content,
|
||||
data.time,
|
||||
));
|
||||
|
||||
reload();
|
||||
};
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
this.chatSocket.close();
|
||||
}
|
||||
|
||||
updateMessages(messages) {
|
||||
messages = JSON.parse(messages);
|
||||
let new_messages = [];
|
||||
|
||||
messages.forEach((message) => {
|
||||
message = message["fields"];
|
||||
new_messages.push(new Message(
|
||||
message["channel_id"],
|
||||
message["author_id"],
|
||||
message["content"],
|
||||
message["time"],
|
||||
));
|
||||
});
|
||||
|
||||
//console.log(new_messages);
|
||||
this.messages = new_messages;
|
||||
return new_messages;
|
||||
}
|
||||
|
||||
async sendMessageChannel(message, receivers_id) {
|
||||
|
||||
if (this.chatSocket == undefined)
|
||||
return;
|
||||
|
||||
this.chatSocket.send(JSON.stringify({
|
||||
'message':message,
|
||||
'receivers_id':receivers_id,
|
||||
}));
|
||||
}
|
||||
|
||||
async deleteChannel() {
|
||||
let response = await this.client._delete("/api/chat/" + this.channel_id, {
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {Channel}
|
@ -1,46 +0,0 @@
|
||||
import {Channel} from "./channel.js"
|
||||
import {Message} from "./message.js"
|
||||
|
||||
class Channels {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async createChannel(users_id, reload) {
|
||||
|
||||
let null_id = false;
|
||||
users_id.forEach(user_id => {
|
||||
if (user_id == null)
|
||||
null_id = true;
|
||||
});
|
||||
if (null_id)
|
||||
return console.log(users_id, "createChannel error, null id;");
|
||||
|
||||
let response = await this.client._post("/api/chat/", {
|
||||
users_id:users_id
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
let exit_code = await response.status;
|
||||
if (exit_code >= 300)
|
||||
return undefined;
|
||||
|
||||
let messages = undefined;
|
||||
if (exit_code == 200)
|
||||
messages = data.messages;
|
||||
return new Channel(this.client, data.channel_id, users_id, messages, reload);
|
||||
}
|
||||
|
||||
async deleteChannel(users_id) {
|
||||
let response = await this.client._delete("/api/chat/", {
|
||||
users_id:users_id
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
console.log(response.status)
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {Channels}
|
@ -1,10 +0,0 @@
|
||||
class Message {
|
||||
constructor(channel_id, author_id, content, time) {
|
||||
this.channel_id = channel_id;
|
||||
this.author_id = author_id;
|
||||
this.content = content;
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
export {Message}
|
@ -1,137 +0,0 @@
|
||||
import { Account } from "./account.js";
|
||||
import { MatchMaking } from "./matchmaking.js";
|
||||
import { Profiles } from "./profiles.js";
|
||||
import { Channels } from './chat/channels.js';
|
||||
import { MyProfile } from "./MyProfile.js";
|
||||
|
||||
function getCookie(name)
|
||||
{
|
||||
let cookie = {};
|
||||
document.cookie.split(';').forEach(function(el) {
|
||||
let split = el.split('=');
|
||||
cookie[split[0].trim()] = split.slice(1).join("=");
|
||||
})
|
||||
return cookie[name];
|
||||
}
|
||||
|
||||
class Client
|
||||
{
|
||||
constructor(url)
|
||||
{
|
||||
this._url = url;
|
||||
this.account = new Account(this);
|
||||
this.profiles = new Profiles(this);
|
||||
this.matchmaking = new MatchMaking(this);
|
||||
this._logged = undefined;
|
||||
|
||||
this.channels = new Channels(this);
|
||||
this.channel = undefined;
|
||||
}
|
||||
|
||||
async isAuthentificate()
|
||||
{
|
||||
if (this._logged == undefined)
|
||||
this.logged = await this._test_logged();
|
||||
return this.logged;
|
||||
}
|
||||
|
||||
async _get(uri, data)
|
||||
{
|
||||
let response = await fetch(this._url + uri, {
|
||||
method: "GET",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async _post(uri, data)
|
||||
{
|
||||
let response = await fetch(this._url + uri, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async _delete(uri, data)
|
||||
{
|
||||
let response = await fetch(this._url + uri, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async _patch_json(uri, data)
|
||||
{
|
||||
let response = await fetch(this._url + uri, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async _patch_file(uri, file)
|
||||
{
|
||||
let response = await fetch(this._url + uri, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
},
|
||||
body: file,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
async _update_logged(state)
|
||||
{
|
||||
if (!this.logged && state)
|
||||
{
|
||||
this.me = new MyProfile(this);
|
||||
await this.me.init();
|
||||
}
|
||||
this.logged = state;
|
||||
}
|
||||
|
||||
async login(username, password)
|
||||
{
|
||||
let response = await this._post("/api/accounts/login", {username: username, password: password})
|
||||
let data = await response.json();
|
||||
if (data.id != undefined)
|
||||
{
|
||||
await this._update_logged(true);
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async logout()
|
||||
{
|
||||
await this._get("/api/accounts/logout");
|
||||
await this._update_logged(false);
|
||||
}
|
||||
|
||||
async _test_logged()
|
||||
{
|
||||
let response = await this._get("/api/accounts/logged");
|
||||
let data = await response.json();
|
||||
|
||||
if (data.id !== undefined)
|
||||
await this._update_logged(true);
|
||||
return data.id !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export {Client}
|
@ -1,37 +0,0 @@
|
||||
import { Client } from "./client.js";
|
||||
|
||||
class MatchMaking
|
||||
{
|
||||
/**
|
||||
* @param {Client} client
|
||||
*/
|
||||
constructor(client)
|
||||
{
|
||||
/**
|
||||
* @type {client}
|
||||
*/
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async start(func)
|
||||
{
|
||||
if (!await this.client.isAuthentificate())
|
||||
return null;
|
||||
|
||||
let url = `wss://${window.location.host}/ws/matchmaking/`;
|
||||
|
||||
this._chatSocket = new WebSocket(url);
|
||||
|
||||
this._chatSocket.onmessage = function (event) {
|
||||
const data = JSON.parse(event.data);
|
||||
func(data.game_id)
|
||||
};
|
||||
}
|
||||
|
||||
async stop()
|
||||
{
|
||||
this._chatSocket.close()
|
||||
}
|
||||
}
|
||||
|
||||
export {MatchMaking}
|
@ -1,45 +0,0 @@
|
||||
import { Client } from "./client.js";
|
||||
|
||||
class Profile
|
||||
{
|
||||
/**
|
||||
* @param {Client} client
|
||||
*/
|
||||
constructor (client, username = undefined, avatar_url = undefined, user_id = undefined)
|
||||
{
|
||||
/**
|
||||
* @type {Client} client
|
||||
*/
|
||||
this.client = client;
|
||||
this.username = username;
|
||||
this.avatar_url = avatar_url;
|
||||
this.user_id = user_id;
|
||||
this.isBlocked = false;
|
||||
}
|
||||
|
||||
async init(user_id)
|
||||
{
|
||||
let response = await this.client._get(`/api/profiles/${user_id}`);
|
||||
let response_data = await response.json();
|
||||
|
||||
this.user_id = response_data.user_id;
|
||||
this.username = response_data.username;
|
||||
this.avatar_url = response_data.avatar_url;
|
||||
|
||||
let block_response = await this.client._get("/api/profiles/block");
|
||||
|
||||
if (block_response.status == 404)
|
||||
return
|
||||
|
||||
let block_data = await block_response.json();
|
||||
let block_list = JSON.parse(block_data);
|
||||
block_list.forEach(block => {
|
||||
let blocker = block.fields.blocker;
|
||||
let blocked = block.fields.blocked;
|
||||
if (blocker == this.client.me.user_id && blocked == user_id)
|
||||
return this.isBlocked = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {Profile}
|
@ -1,64 +0,0 @@
|
||||
import { Profile } from "./profile.js";
|
||||
|
||||
class Profiles
|
||||
{
|
||||
/**
|
||||
* @param {Client} client
|
||||
*/
|
||||
constructor (client)
|
||||
{
|
||||
/**
|
||||
* @type {Client} client
|
||||
*/
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async all()
|
||||
{
|
||||
let response = await this.client._get("/api/profiles/");
|
||||
let response_data = await response.json();
|
||||
|
||||
let profiles = []
|
||||
response_data.forEach((profile) => {
|
||||
profiles.push(new Profile(this.client, profile.username, profile.avatar_url, profile.user_id))
|
||||
});
|
||||
return profiles;
|
||||
}
|
||||
|
||||
async getProfile(user_id)
|
||||
{
|
||||
let profile = new Profile(this.client);
|
||||
await profile.init(user_id);
|
||||
return profile;
|
||||
}
|
||||
|
||||
async block(user_id) {
|
||||
|
||||
// blocker & blocked
|
||||
let response = await this.client._post("/api/profiles/block", {
|
||||
users_id:[this.client.me.user_id, user_id],
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
console.log(response.status);
|
||||
console.log(data);
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
async deblock(user_id) {
|
||||
|
||||
// blocker & blocked
|
||||
let response = await this.client._delete("/api/profiles/block", {
|
||||
users_id:[this.client.me.user_id, user_id],
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
console.log(response.status);
|
||||
console.log(data);
|
||||
return data;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export {Profiles}
|
@ -1,106 +0,0 @@
|
||||
import { Client } from "./api/client.js";
|
||||
|
||||
import LoginView from "./views/accounts/LoginView.js";
|
||||
import Dashboard from "./views/Dashboard.js";
|
||||
import Search from "./views/Search.js";
|
||||
import HomeView from "./views/HomeView.js";
|
||||
import RegisterView from "./views/accounts/RegisterView.js";
|
||||
import LogoutView from "./views/accounts/LogoutView.js";
|
||||
import GameView from "./views/Game.js"
|
||||
|
||||
import PageNotFoundView from './views/PageNotFoundView.js'
|
||||
|
||||
import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js";
|
||||
import MeView from "./views/MeView.js";
|
||||
import ProfilePageView from "./views/ProfilePageView.js";
|
||||
import MatchMakingView from "./views/MatchMakingView.js";
|
||||
|
||||
let client = new Client(location.protocol + "//" + location.host)
|
||||
|
||||
let lastView = undefined
|
||||
|
||||
const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
|
||||
|
||||
const getParams = match => {
|
||||
const values = match.result.slice(1);
|
||||
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(result => result[1]);
|
||||
|
||||
return Object.fromEntries(keys.map((key, i) => {
|
||||
return [key, values[i]];
|
||||
}));
|
||||
};
|
||||
|
||||
const navigateTo = async (uri) => {
|
||||
if (await router(uri) === 0)
|
||||
history.pushState(null, null, uri);
|
||||
};
|
||||
|
||||
const router = async (uri) => {
|
||||
const routes = [
|
||||
{ path: "/", view: Dashboard },
|
||||
{ path: "/profiles/:id", view: ProfilePageView },
|
||||
{ path: "/login", view: LoginView },
|
||||
{ path: "/logout", view: LogoutView },
|
||||
{ path: "/register", view: RegisterView },
|
||||
{ path: "/search", view: Search },
|
||||
{ path: "/home", view: HomeView },
|
||||
{ path: "/me", view: MeView },
|
||||
{ path: "/matchmaking", view: MatchMakingView },
|
||||
{ path: "/game/offline", view: GameView },
|
||||
];
|
||||
|
||||
// Test each route for potential match
|
||||
const potentialMatches = routes.map(route => {
|
||||
return {
|
||||
route: route,
|
||||
result: uri.match(pathToRegex(route.path))
|
||||
};
|
||||
});
|
||||
|
||||
let match = potentialMatches.find(potentialMatch => potentialMatch.result !== null);
|
||||
|
||||
if (!match) {
|
||||
match = {
|
||||
route: {
|
||||
path: uri,
|
||||
view: PageNotFoundView
|
||||
},
|
||||
result: [uri]
|
||||
};
|
||||
}
|
||||
|
||||
if (lastView !== undefined)
|
||||
await lastView.leavePage();
|
||||
|
||||
const view = new match.route.view(getParams(match));
|
||||
|
||||
if (view instanceof AbstractRedirectView && await view.redirect())
|
||||
return 1;
|
||||
|
||||
lastView = view;
|
||||
|
||||
await client.isAuthentificate();
|
||||
let content = await view.getHtml();
|
||||
if (content == null)
|
||||
return 1;
|
||||
|
||||
view.setTitle();
|
||||
document.querySelector("#app").innerHTML = content
|
||||
|
||||
await view.postInit();
|
||||
return 0;
|
||||
};
|
||||
|
||||
window.addEventListener("popstate", function() {router(location.pathname)});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.body.addEventListener("click", e => {
|
||||
if (e.target.matches("[data-link]")) {
|
||||
e.preventDefault();
|
||||
navigateTo(e.target.href.slice(location.origin.length));
|
||||
}
|
||||
});
|
||||
router(location.pathname);
|
||||
});
|
||||
|
||||
export { client, navigateTo }
|
@ -1,18 +0,0 @@
|
||||
function clear(property_name, elements_id)
|
||||
{
|
||||
elements_id.forEach(element_id => {
|
||||
let element = document.getElementById(element_id)
|
||||
element[property_name] = ""
|
||||
});
|
||||
}
|
||||
|
||||
function fill_errors(errors, property_name)
|
||||
{
|
||||
Object.keys(errors).forEach(error_field =>
|
||||
{
|
||||
let element = document.getElementById(error_field);
|
||||
element[property_name] = errors[error_field];
|
||||
});
|
||||
}
|
||||
|
||||
export {fill_errors, clear}
|
@ -1,19 +0,0 @@
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, "Dashboard");
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<h1>Welcome back, Dom</h1>
|
||||
<p>
|
||||
Fugiat voluptate et nisi Lorem cillum anim sit do eiusmod occaecat irure do. Reprehenderit anim fugiat sint exercitation consequat. Sit anim laborum sit amet Lorem adipisicing ullamco duis. Anim in do magna ea pariatur et.
|
||||
</p>
|
||||
<p>
|
||||
<a href="/posts" data-link>View recent posts</a>.
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, 'Game');
|
||||
this.game = null;
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<h1>Game</h1>
|
||||
<button id='startGameButton'>Start Game</button>
|
||||
<button id='stopGameButton'>Stop Game</button>
|
||||
`;
|
||||
}
|
||||
|
||||
async postInit() {
|
||||
document.getElementById('startGameButton').onclick = this.startGame.bind(this);
|
||||
document.getElementById('stopGameButton').onclick = this.stopGame.bind(this);
|
||||
}
|
||||
|
||||
startGame() {
|
||||
if (this.game == null) {
|
||||
document.getElementById('startGameButton').innerHTML = 'Reset Game';
|
||||
this.game = new Game;
|
||||
}
|
||||
else {
|
||||
document.getElementById('app').removeChild(this.game.canvas);
|
||||
this.game.cleanup();
|
||||
this.game = new Game;
|
||||
}
|
||||
}
|
||||
|
||||
stopGame() {
|
||||
if (!this.game)
|
||||
return;
|
||||
document.getElementById('app').removeChild(this.game.canvas);
|
||||
document.getElementById('app').removeChild(this.game.scoresDisplay);
|
||||
this.game.cleanup();
|
||||
this.game = null;
|
||||
document.getElementById('startGameButton').innerHTML = 'Start Game';
|
||||
}
|
||||
}
|
||||
|
||||
class Game {
|
||||
constructor() {
|
||||
//Global variables
|
||||
this.def = {
|
||||
CANVASHEIGHT: 270,
|
||||
CANVASWIDTH: 480,
|
||||
PADDLEHEIGHT: 70,
|
||||
PADDLEWIDTH: 10,
|
||||
PADDLEMARGIN: 5,
|
||||
PADDLESPEED: 3,
|
||||
BALLRADIUS: 5,
|
||||
BALLSPEED: 2,
|
||||
BALLSPEEDINCR: 0.15,
|
||||
MAXBOUNCEANGLE: 10 * (Math.PI / 12),
|
||||
MAXSCORE: 6
|
||||
};
|
||||
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.id = 'gameCanvas';
|
||||
this.canvas.width = this.def.CANVASWIDTH;
|
||||
this.canvas.height = this.def.CANVASHEIGHT;
|
||||
this.canvas.style.border = '1px solid #d3d3d3';
|
||||
this.canvas.style.backgroundColor = '#f1f1f1';
|
||||
this.context = this.canvas.getContext('2d');
|
||||
document.getElementById('app').appendChild(this.canvas);
|
||||
this.scoresDisplay = document.createElement('p');
|
||||
this.scoresDisplay.innerHTML = 'Scores: 0 - 0';
|
||||
document.getElementById('app').appendChild(this.scoresDisplay);
|
||||
|
||||
this.players = [
|
||||
{
|
||||
paddle: new Paddle(this.context,
|
||||
this.def.PADDLEMARGIN,
|
||||
this.def),
|
||||
score: 0
|
||||
},
|
||||
{
|
||||
paddle: new Paddle(this.context,
|
||||
this.def.CANVASWIDTH - this.def.PADDLEMARGIN - this.def.PADDLEWIDTH,
|
||||
this.def),
|
||||
score: 0
|
||||
}
|
||||
];
|
||||
this.ballStartSide = 0;
|
||||
this.ballRespawned = false;
|
||||
this.ball = new Ball(this.context, this.def, this.ballStartSide);
|
||||
|
||||
this.interval = setInterval(this.updateGame.bind(this), 10);
|
||||
|
||||
this.keys = [];
|
||||
this.keyUpHandler = this.keyUpHandler.bind(this);
|
||||
this.keyDownHandler = this.keyDownHandler.bind(this);
|
||||
document.addEventListener('keydown', this.keyDownHandler);
|
||||
document.addEventListener('keyup', this.keyUpHandler);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
clearInterval(this.interval);
|
||||
document.removeEventListener('keydown', this.keyDownHandler);
|
||||
document.removeEventListener('keyup', this.keyUpHandler);
|
||||
this.canvas.style.display = 'none';
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
updateGame() {
|
||||
//Paddle movement
|
||||
if (this.keys.includes('s') &&
|
||||
this.players[0].paddle.y + this.def.PADDLEHEIGHT < this.def.CANVASHEIGHT - this.def.PADDLEMARGIN)
|
||||
this.players[0].paddle.y += this.def.PADDLESPEED;
|
||||
if (this.keys.includes('w') &&
|
||||
this.players[0].paddle.y > 0 + this.def.PADDLEMARGIN)
|
||||
this.players[0].paddle.y -= this.def.PADDLESPEED;
|
||||
|
||||
if (this.keys.includes('ArrowDown') &&
|
||||
this.players[1].paddle.y + this.def.PADDLEHEIGHT < this.def.CANVASHEIGHT - this.def.PADDLEMARGIN)
|
||||
this.players[1].paddle.y += this.def.PADDLESPEED;
|
||||
if (this.keys.includes('ArrowUp') &&
|
||||
this.players[1].paddle.y > 0 + this.def.PADDLEMARGIN)
|
||||
this.players[1].paddle.y -= this.def.PADDLESPEED;
|
||||
|
||||
//GOOAAAAL
|
||||
if (this.ball.x <= 0)
|
||||
this.updateScore(this.players[0].score, ++this.players[1].score);
|
||||
else if (this.ball.x >= this.def.CANVASWIDTH)
|
||||
this.updateScore(++this.players[0].score, this.players[1].score);
|
||||
|
||||
//Ball collisions
|
||||
if (this.detectCollision(this.players[0].paddle, this.ball))
|
||||
this.calculateBallVelocity(this.players[0].paddle.getCenter().y, this.ball);
|
||||
else if (this.detectCollision(this.players[1].paddle, this.ball))
|
||||
this.calculateBallVelocity(this.players[1].paddle.getCenter().y, this.ball, -1);
|
||||
|
||||
if (this.ball.y - this.ball.radius <= 0)
|
||||
this.ball.vy *= -1;
|
||||
else if (this.ball.y + this.ball.radius >= this.canvas.height)
|
||||
this.ball.vy *= -1;
|
||||
|
||||
if (!this.ballRespawned) {
|
||||
this.ball.x += this.ball.vx;
|
||||
this.ball.y += this.ball.vy;
|
||||
}
|
||||
|
||||
this.clear();
|
||||
this.players[0].paddle.update();
|
||||
this.players[1].paddle.update();
|
||||
this.ball.update();
|
||||
}
|
||||
|
||||
updateScore(p1Score, p2Score) {
|
||||
if (p1Score > this.def.MAXSCORE) {
|
||||
this.scoresDisplay.innerHTML = 'Player 1 wins!! GGS';
|
||||
this.cleanup();
|
||||
}
|
||||
else if (p2Score > this.def.MAXSCORE) {
|
||||
this.scoresDisplay.innerHTML = 'Player 2 wins!! GGS';
|
||||
this.cleanup();
|
||||
} else {
|
||||
this.scoresDisplay.innerHTML = `Scores: ${p1Score} - ${p2Score}`;
|
||||
this.ballStartSide = 1 - this.ballStartSide;
|
||||
this.ball = new Ball(this.context, this.def, this.ballStartSide);
|
||||
this.ballRespawned = true;
|
||||
new Promise(r => setTimeout(r, 300))
|
||||
.then(_ => this.ballRespawned = false);
|
||||
}
|
||||
}
|
||||
|
||||
detectCollision(paddle, ball) {
|
||||
let paddleCenter = paddle.getCenter();
|
||||
let dx = Math.abs(ball.x - paddleCenter.x);
|
||||
let dy = Math.abs(ball.y - paddleCenter.y);
|
||||
if (dx <= ball.radius + paddle.width / 2 &&
|
||||
dy <= ball.radius + paddle.height / 2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
calculateBallVelocity(paddleCenterY, ball, side = 1) {
|
||||
let relativeIntersectY = paddleCenterY - ball.y;
|
||||
let normRelIntersectY = relativeIntersectY / this.def.PADDLEHEIGHT / 2;
|
||||
let bounceAngle = normRelIntersectY * this.def.MAXBOUNCEANGLE;
|
||||
|
||||
ball.speed += this.def.BALLSPEEDINCR;
|
||||
ball.vx = ball.speed * side * Math.cos(bounceAngle);
|
||||
ball.vy = ball.speed * -Math.sin(bounceAngle);
|
||||
}
|
||||
|
||||
keyUpHandler(ev) {
|
||||
const idx = this.keys.indexOf(ev.key);
|
||||
if (idx != -1)
|
||||
this.keys.splice(idx, 1);
|
||||
}
|
||||
|
||||
keyDownHandler(ev) {
|
||||
if (!this.keys.includes(ev.key))
|
||||
this.keys.push(ev.key);
|
||||
}
|
||||
}
|
||||
|
||||
class Paddle {
|
||||
constructor(context, paddleSide, def) {
|
||||
this.width = def.PADDLEWIDTH;
|
||||
this.height = def.PADDLEHEIGHT;
|
||||
this.x = paddleSide;
|
||||
this.y = def.CANVASHEIGHT / 2 - this.height / 2;
|
||||
this.ctx = context;
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.ctx.fillStyle = 'black';
|
||||
this.ctx.fillRect(this.x, this.y, this.width, this.height);
|
||||
}
|
||||
|
||||
getCenter() {
|
||||
return {
|
||||
x: this.x + this.width / 2,
|
||||
y: this.y + this.height / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Ball {
|
||||
constructor(context, def, startSide) {
|
||||
this.radius = def.BALLRADIUS;
|
||||
this.speed = def.BALLSPEED;
|
||||
this.x = def.CANVASWIDTH / 2;
|
||||
this.y = def.CANVASHEIGHT / 2;
|
||||
this.vy = 0;
|
||||
if (startSide === 0)
|
||||
this.vx = -this.speed;
|
||||
else
|
||||
this.vx = this.speed;
|
||||
this.ctx = context;
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.ctx.fillStyle = 'black';
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
|
||||
this.ctx.fill();
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
|
||||
|
||||
export default class extends AbstractAuthentificateView {
|
||||
constructor(params) {
|
||||
super(params, "Home");
|
||||
this.redirect_url = "/login"
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<h1>HOME</h1>
|
||||
<a href="/matchmaking" class="nav__link" data-link>Play a game</a>
|
||||
<a href="/game/offline" class="nav__link" data-link>Play offline</a>
|
||||
<a href="/me" class="nav__link" data-link>Me</a>
|
||||
<a href="/logout" class="nav__link" data-link>Logout</a>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import { client, navigateTo } from "../index.js";
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
|
||||
function game_found(game_id)
|
||||
{
|
||||
navigateTo(`/games/${game_id}`)
|
||||
}
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, "Dashboard");
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
await client.matchmaking.start(game_found)
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<h1>finding<h1>
|
||||
`;
|
||||
}
|
||||
|
||||
async leavePage()
|
||||
{
|
||||
await client.matchmaking.stop();
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
import { client, navigateTo } from "../index.js";
|
||||
import { clear, fill_errors } from "../utils/formUtils.js";
|
||||
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
|
||||
|
||||
export default class extends AbstractAuthentificateView
|
||||
{
|
||||
constructor(params)
|
||||
{
|
||||
super(params, "Me");
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
document.getElementById("save-account-button").onclick = this.save_account;
|
||||
document.getElementById("delete-account-button").onclick = this.delete_account;
|
||||
document.getElementById("save-profile-button").onclick = this.save_profile;
|
||||
}
|
||||
|
||||
async delete_account()
|
||||
{
|
||||
let current_password = document.getElementById("current_password-input").value;
|
||||
|
||||
let response_data = await client.account.delete(current_password);
|
||||
|
||||
console.log(await client.isAuthentificate())
|
||||
if (response_data === null || response_data === "user deleted")
|
||||
{
|
||||
navigateTo("/login");
|
||||
return;
|
||||
}
|
||||
clear("innerHTML", ["current_password-input"])
|
||||
fill_errors({"current_password-input": response_data["password"]}, "innerHTML")
|
||||
}
|
||||
|
||||
async save_account()
|
||||
{
|
||||
let username = document.getElementById("username-input").value;
|
||||
let new_password = document.getElementById("new_password-input").value;
|
||||
let current_password = document.getElementById("current_password-input").value;
|
||||
|
||||
let data = {};
|
||||
|
||||
data.username = username;
|
||||
if (new_password.length != 0)
|
||||
data.new_password = new_password;
|
||||
|
||||
let response_data = await client.account.update(data, current_password);
|
||||
|
||||
if (response_data === null)
|
||||
{
|
||||
navigateTo("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
if (response_data === "data has been alterate")
|
||||
response_data = {"save-account": "saved"}
|
||||
|
||||
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"])
|
||||
fill_errors(response_data, "innerHTML")
|
||||
}
|
||||
|
||||
async save_profile()
|
||||
{
|
||||
let avatar = document.getElementById("avatar-input");
|
||||
|
||||
if (avatar.files[0] !== undefined)
|
||||
{
|
||||
let form_data = new FormData();
|
||||
form_data.append("file", avatar.files[0]);
|
||||
await client.me.change_avatar(form_data);
|
||||
}
|
||||
document.getElementById("save-profile").innerHTML = "Saved";
|
||||
}
|
||||
|
||||
async getHtml()
|
||||
{
|
||||
return `
|
||||
<link rel="stylesheet" href="/static/css/me.css">
|
||||
<h1>ME</h1>
|
||||
<div id="main">
|
||||
<div class="account">
|
||||
<h3>Account</h3>
|
||||
<input type="text" placeholder="username" id="username-input" text=${client.me.username}>
|
||||
<span id="username"></span>
|
||||
<input type=password placeholder="new_password" id="new_password-input">
|
||||
<span id="new_password"></span>
|
||||
<input type=password placeholder="current_password" id="current_password-input">
|
||||
<span id="current_password"></span>
|
||||
|
||||
<input type="button" value="Save Credentials" id="save-account-button">
|
||||
<span id="save-account"></span>
|
||||
<input type="button" value="Delete Account" id="delete-account-button">
|
||||
<span id="delete-account"></span>
|
||||
</div>
|
||||
<div class="profile">
|
||||
<h3>Profile</h3>
|
||||
<input type="file" id="avatar-input" accept="image/png, image/jpeg">
|
||||
<input type="button" value="Save profile" id="save-profile-button">
|
||||
<span id="save-profile"></span>
|
||||
</div>
|
||||
<a href="/logout" class="nav__link" data-link>Logout</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, "Dashboard");
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<h1>404 Bozo</h1>
|
||||
<p>Git gud</p>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
import { client } from "../index.js"
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, "Profile ");
|
||||
this.user_id = params.id;
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
this.profile = await client.profiles.getProfile(this.user_id);
|
||||
this.info = document.getElementById("info");
|
||||
|
||||
// Username
|
||||
let username = document.createElement("a");
|
||||
username.id = "username";
|
||||
username.appendChild(document.createTextNode(this.profile.username));
|
||||
this.info.appendChild(username);
|
||||
|
||||
this.info.appendChild(document.createElement("br"));
|
||||
|
||||
// Avatar
|
||||
let avatar = document.createElement("img");
|
||||
avatar.id = "avatar";
|
||||
avatar.src = this.profile.avatar_url;
|
||||
this.info.appendChild(avatar);
|
||||
|
||||
await this.blockButton();
|
||||
}
|
||||
|
||||
async blockButton() {
|
||||
// Block option
|
||||
if (client.me.user_id != this.user_id) {
|
||||
let block = document.getElementById("block") || document.createElement("a");
|
||||
block.id = "block";
|
||||
block.innerText = "";
|
||||
block.onclick = async () => {
|
||||
if (!this.profile.isBlocked)
|
||||
await client.profiles.block(this.user_id);
|
||||
else
|
||||
await client.profiles.deblock(this.user_id);
|
||||
this.profile = await client.profiles.getProfile(this.user_id);
|
||||
this.blockButton();
|
||||
};
|
||||
if (this.profile.isBlocked)
|
||||
block.appendChild(document.createTextNode("Deblock"));
|
||||
else
|
||||
block.appendChild(document.createTextNode("Block"));
|
||||
this.info.appendChild(block);
|
||||
}
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<link rel="stylesheet" href="/static/css/profile.css">
|
||||
<div id="info">
|
||||
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
import AbstractView from "./abstracts/AbstractView.js";
|
||||
import {client} from "../index.js";
|
||||
import {Message} from "../api/chat/message.js"
|
||||
|
||||
export default class extends AbstractView {
|
||||
constructor(params) {
|
||||
super(params, "Search");
|
||||
}
|
||||
|
||||
async postInit() {
|
||||
|
||||
let search = document.getElementById("input_user");
|
||||
search.oninput = this.users;
|
||||
|
||||
let chat_input = document.getElementById("input_chat");
|
||||
//chat_input.addEventListener("keydown", this.chat_manager)
|
||||
|
||||
this.last_add_chat = undefined;
|
||||
this.users();
|
||||
this.chat();
|
||||
|
||||
}
|
||||
|
||||
async users() {
|
||||
|
||||
let search = document.getElementById("input_user").value.toLowerCase();
|
||||
|
||||
let logged = await client.isAuthentificate();
|
||||
|
||||
let users = await client.profiles.all();
|
||||
let list_users = document.getElementById('list_users');
|
||||
list_users.innerHTML = "";
|
||||
|
||||
users.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => {
|
||||
|
||||
if (user.user_id == null) {
|
||||
console.log("list User one with id null;");
|
||||
return;
|
||||
}
|
||||
|
||||
var new_user = document.createElement("li");
|
||||
|
||||
// username
|
||||
let username = document.createElement("a");
|
||||
username.href = `/profiles/${user.user_id}`;
|
||||
username.appendChild(document.createTextNode(user.username));
|
||||
new_user.appendChild(username);
|
||||
|
||||
// space
|
||||
new_user.appendChild(document.createTextNode(" "));
|
||||
|
||||
// chat
|
||||
if (logged && client.me.user_id != user.user_id) {
|
||||
let add_chat = document.createElement("a");
|
||||
add_chat.id = "add_chat_off";
|
||||
add_chat.onclick = async () => {
|
||||
if (client.channel != undefined) {
|
||||
client.channel.members_id.forEach((member_id) => {
|
||||
if (member_id == user.user_id)
|
||||
client.channel = undefined;
|
||||
});
|
||||
|
||||
if (client.channel == undefined) {
|
||||
add_chat.id = "add_chat_off";
|
||||
this.last_add_chat = undefined;
|
||||
return this.hideChat();
|
||||
}
|
||||
|
||||
client.channel.disconnect();
|
||||
}
|
||||
|
||||
client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat);
|
||||
this.chat();
|
||||
if (this.last_add_chat != undefined)
|
||||
this.last_add_chat.id = "add_chat_off";
|
||||
this.last_add_chat = add_chat;
|
||||
add_chat.id = "add_chat_on";
|
||||
};
|
||||
add_chat.appendChild(document.createTextNode("Chat"));
|
||||
new_user.appendChild(add_chat);
|
||||
|
||||
new_user.appendChild(document.createTextNode(" "));
|
||||
|
||||
}
|
||||
|
||||
// break line
|
||||
new_user.appendChild(document.createElement("br"));
|
||||
|
||||
// avatar
|
||||
var img = document.createElement("img");
|
||||
img.src = user.avatar_url;
|
||||
new_user.appendChild(img);
|
||||
|
||||
|
||||
list_users.appendChild(new_user);
|
||||
});
|
||||
//console.log(list_users);
|
||||
|
||||
}
|
||||
|
||||
async chat() {
|
||||
|
||||
let users = await client.profiles.all();
|
||||
let logged = await client.isAuthentificate();
|
||||
/*let reload = document.getElementById("messages");
|
||||
if (reload != null)
|
||||
reload.remove();*/
|
||||
|
||||
let reload = document.getElementById("members");
|
||||
if (reload != null)
|
||||
reload.remove();
|
||||
|
||||
if (client.channel == undefined || !logged)
|
||||
return ;
|
||||
|
||||
let chats = document.getElementById("chats");
|
||||
|
||||
if (document.getElementById("chat") == null) {
|
||||
let chat = document.createElement("div");
|
||||
chat.id = "chat";
|
||||
chats.appendChild(chat);
|
||||
}
|
||||
|
||||
|
||||
// div des messages
|
||||
let messages = document.getElementById("messages");
|
||||
if (messages == null) {
|
||||
messages = document.createElement("div");
|
||||
messages.id = "messages";
|
||||
if (document.getElementById("input_chat") == null)
|
||||
chat.appendChild(messages);
|
||||
else
|
||||
document.getElementById("input_chat").before(messages);
|
||||
}
|
||||
|
||||
// les messages, réecriture seulement du dernier
|
||||
let i = 0;
|
||||
client.channel.messages.forEach((message) => {
|
||||
if (messages.children[i] == null || message.content != messages.children[i].innerText) {
|
||||
let text = document.createElement("p");
|
||||
text.appendChild(document.createTextNode(message.content));
|
||||
if (message.author_id == client.me.user_id)
|
||||
text.id = "you";
|
||||
else
|
||||
text.id = "other";
|
||||
|
||||
messages.appendChild(text);
|
||||
}
|
||||
i++;
|
||||
});
|
||||
|
||||
// Input pour rentrer un message
|
||||
if (document.getElementById("input_chat") == null) {
|
||||
let chat_input = document.createElement("input");
|
||||
chat_input.id="input_chat";
|
||||
chat_input.type="text";
|
||||
chat_input.name="message";
|
||||
chat_input.placeholder="message bozo";
|
||||
chat_input.maxLength=255;
|
||||
chat.appendChild(chat_input);
|
||||
|
||||
chat_input.onkeydown = async () => {
|
||||
if (event.keyCode == 13 && client.channel != undefined) {
|
||||
//let chat_input = document.getElementById("input_chat");
|
||||
let chat_text = chat_input.value;
|
||||
|
||||
let receivers_id = [];
|
||||
client.channel.members_id.forEach((member_id) => {
|
||||
if (member_id != client.me.user_id)
|
||||
receivers_id.push(users.filter(user => user.user_id == member_id)[0].user_id);
|
||||
});
|
||||
await client.channel.sendMessageChannel(chat_text, receivers_id)
|
||||
|
||||
// Reset
|
||||
chat_input.value = "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// nom des membres du chat
|
||||
let members = document.createElement("h2");
|
||||
members.id = "members";
|
||||
let usernames = "";
|
||||
client.channel.members_id.forEach((member_id) => {
|
||||
if (member_id != client.me.user_id) {
|
||||
if (usernames.length > 0)
|
||||
usernames += ", ";
|
||||
usernames += (users.filter(user => user.user_id == member_id)[0].username);
|
||||
}
|
||||
});
|
||||
members.appendChild(document.createTextNode(usernames));
|
||||
messages.before(members);
|
||||
|
||||
|
||||
// Scroll to the bottom of messages
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
|
||||
}
|
||||
|
||||
async hideChat() {
|
||||
|
||||
let close = document.getElementById("chat");
|
||||
if (close != null)
|
||||
close.remove();
|
||||
|
||||
}
|
||||
|
||||
async leavePage() {
|
||||
if (client.channel != undefined)
|
||||
client.channel.disconnect();
|
||||
client.channel = undefined
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<link rel="stylesheet" href="/static/css/search.css">
|
||||
|
||||
<div id="chats">
|
||||
<div id="users">
|
||||
<input id="input_user" type="text" name="message" placeholder="userbozo"/>
|
||||
<ul id="list_users">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { client, navigateTo } from "../../index.js";
|
||||
import AbstractRedirectView from "./AbstractRedirectView.js";
|
||||
|
||||
export default class extends AbstractRedirectView{
|
||||
constructor(params, title) {
|
||||
super(params, title, "/login");
|
||||
}
|
||||
|
||||
async redirect()
|
||||
{
|
||||
if (await client.isAuthentificate() === false)
|
||||
{
|
||||
navigateTo(this.redirect_url);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import { client, navigateTo } from "../../index.js";
|
||||
import AbstractRedirectView from "./AbstractRedirectView.js";
|
||||
|
||||
export default class extends AbstractRedirectView{
|
||||
constructor(params, title, url) {
|
||||
super(params, title, url);
|
||||
}
|
||||
|
||||
async redirect()
|
||||
{
|
||||
if (await client.isAuthentificate() === false)
|
||||
return 0;
|
||||
navigateTo(this.redirect_url);
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { navigateTo } from "../../index.js";
|
||||
import AbstractView from "./AbstractView.js";
|
||||
|
||||
export default class extends AbstractView{
|
||||
constructor(params, title, url)
|
||||
{
|
||||
super(params, title);
|
||||
this.redirect_url = url;
|
||||
}
|
||||
|
||||
async redirect()
|
||||
{
|
||||
navigateTo(url);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
export default class {
|
||||
constructor(params, title) {
|
||||
this.params = params;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
async postInit() {
|
||||
}
|
||||
|
||||
async leavePage() {
|
||||
}
|
||||
|
||||
setTitle() {
|
||||
document.title = this.title;
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return "";
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import { client, navigateTo } from "../../index.js";
|
||||
import { clear, fill_errors } from "../../utils/formUtils.js";
|
||||
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
|
||||
|
||||
async function login()
|
||||
{
|
||||
let username = document.getElementById("username-input").value;
|
||||
let password = document.getElementById("password-input").value;
|
||||
|
||||
let response_data = await client.login(username, password);
|
||||
|
||||
if (response_data == null)
|
||||
{
|
||||
navigateTo("/home");
|
||||
return;
|
||||
}
|
||||
|
||||
clear("innerHTML", ["username", "user", "password"]);
|
||||
fill_errors(response_data, "innerHTML");
|
||||
}
|
||||
|
||||
export default class extends AbstractNonAuthentifiedView {
|
||||
constructor(params) {
|
||||
super(params, "Login", "/home");
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
document.getElementById("login-button").onclick = login;
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<div class=form>
|
||||
<label>Login</label>
|
||||
<link rel="stylesheet" href="/static/css/accounts/login.css">
|
||||
<input type="text" id="username-input" placeholder="username">
|
||||
<span id="username"></span>
|
||||
<input type="password" id="password-input" placeholder="password">
|
||||
<span id="password"></span>
|
||||
<input type="button" value="Login" id="login-button">
|
||||
<span id="user"></span>
|
||||
<a href="/register" class="nav__link" data-link>Register</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { client, navigateTo } from "../../index.js";
|
||||
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
|
||||
|
||||
export default class extends AbstractAuthentifiedView
|
||||
{
|
||||
constructor(params) {
|
||||
super(params, "Logout");
|
||||
client.logout();
|
||||
navigateTo("/login")
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import { client, navigateTo } from "../../index.js";
|
||||
import { clear, fill_errors } from "../../utils/formUtils.js";
|
||||
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
|
||||
|
||||
async function register()
|
||||
{
|
||||
let username = document.getElementById("username-input").value;
|
||||
let password = document.getElementById("password-input").value;
|
||||
|
||||
let response_data = await client.account.create(username, password);
|
||||
|
||||
if (response_data == null)
|
||||
{
|
||||
navigateTo("/home");
|
||||
return;
|
||||
}
|
||||
|
||||
clear("innerHTML", ["username", "user", "password"]);
|
||||
fill_errors(response_data, "innerHTML");
|
||||
}
|
||||
|
||||
export default class extends AbstractNonAuthentifiedView {
|
||||
constructor(params) {
|
||||
super(params, "Register", "/home");
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
document.getElementById("register-button").onclick = register;
|
||||
}
|
||||
|
||||
async getHtml() {
|
||||
return `
|
||||
<div class=form>
|
||||
<label>Register</label>
|
||||
<link rel="stylesheet" href="/static/css/accounts/register.css">
|
||||
<input type="text" id="username-input" placeholder="username">
|
||||
<span id="username"></span>
|
||||
<input type="password" id="password-input" placeholder="password">
|
||||
<span id="password"></span>
|
||||
<input type="button" value="Register" id="register-button">
|
||||
<span id="user"></span>
|
||||
<a href="/login" class="nav__link" data-link>Login</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Single Page App</title>
|
||||
<link rel="stylesheet" href="{% static 'css/index.css' %}">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="/" class="nav__link" data-link>Dashboard</a>
|
||||
<a href="/search" class="nav__link" data-link>Search</a>
|
||||
<a href="/home" class="nav__link" data-link>Home</a>
|
||||
<a href="/login" class="nav__link" data-link>Login</a>
|
||||
<a href="/register" class="nav__link" data-link>Register</a>
|
||||
</nav>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="{% static 'js/index.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -1,7 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from .views import index_view
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^', index_view ,name="index"),
|
||||
]
|
@ -1,7 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
def index_view(req):
|
||||
return render(req, 'index.html');
|
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GamesConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'games'
|
@ -1,14 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class GameModel(models.Model):
|
||||
|
||||
def create(self, users_id: [int]):
|
||||
self.save()
|
||||
for user_id in users_id:
|
||||
GameMembersModel(game_id=self.pk, member_id=user_id)
|
||||
return self.pk
|
||||
|
||||
class GameMembersModel(models.Model):
|
||||
game_id = models.IntegerField()
|
||||
member_id = models.IntegerField()
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -1,3 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
22
manage.py
22
manage.py
@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'trancendence.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MatchmakingConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'matchmaking'
|
@ -1,48 +0,0 @@
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from games.models import GameModel
|
||||
|
||||
import json
|
||||
|
||||
queue_id: [int] = []
|
||||
queue_ws: [WebsocketConsumer] = []
|
||||
|
||||
class MatchMaking(WebsocketConsumer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.channel_name = "matchmaking"
|
||||
self.group_name = "matchmaking"
|
||||
|
||||
def connect(self):
|
||||
|
||||
user: User = self.scope["user"]
|
||||
if (user.is_anonymous or not user.is_authenticated):
|
||||
return
|
||||
|
||||
self.channel_layer.group_add(self.group_name, self.channel_name)
|
||||
|
||||
self.accept()
|
||||
|
||||
global queue_id, queue_ws
|
||||
queue_id.append(user.pk)
|
||||
queue_ws.append(self)
|
||||
|
||||
if len(set(queue_id)) == 2:
|
||||
game_id: int = GameModel().create(set(queue_id))
|
||||
event = {"game_id": game_id}
|
||||
for ws in queue_ws:
|
||||
ws.send(text_data=json.dumps({'game_id': game_id}))
|
||||
queue_id.clear()
|
||||
queue_ws.clear()
|
||||
|
||||
|
||||
def disconnect(self, close_code):
|
||||
user: User = self.scope["user"]
|
||||
global queue_id, queue_ws
|
||||
if (user.pk in queue_id):
|
||||
queue_ws.pop(queue_id.index(user.pk))
|
||||
queue_id.remove(user.pk)
|
||||
self.channel_layer.group_discard(self.group_name, self.channel_name)
|
@ -1,3 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
@ -1,6 +0,0 @@
|
||||
from django.urls import re_path
|
||||
from . import consumers
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'ws/matchmaking/', consumers.MatchMaking.as_asgi())
|
||||
]
|
@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -1,3 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "ft_transcendence",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import ProfileModel, BlockModel
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(ProfileModel)
|
||||
admin.site.register(BlockModel)
|
@ -1,6 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ProfilesConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'profiles'
|
@ -1,28 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.db.models import IntegerField
|
||||
|
||||
def upload_to(instance, filename: str):
|
||||
return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}"
|
||||
|
||||
# Create your models here.
|
||||
class ProfileModel(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
avatar_url = models.ImageField(upload_to=upload_to, default="./profiles/static/avatars/default.avif") #blank=True, null=True)
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def on_user_created(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
profile: ProfileModel = ProfileModel.objects.create(pk = instance.pk, user = instance)
|
||||
profile.save()
|
||||
|
||||
class BlockModel(models.Model):
|
||||
blocker = IntegerField(primary_key=False)
|
||||
blocked = IntegerField(primary_key=False)
|
||||
|
||||
def __str__(self):
|
||||
return "blocker_id: " + str(self.blocker) + ", blocked_id: " + str(self.blocked)
|
@ -1,11 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ProfileModel
|
||||
|
||||
class ProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
username = serializers.ReadOnlyField(source='user.username')
|
||||
avatar_url = serializers.ImageField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = ProfileModel
|
||||
fields = ["username", "avatar_url", "user_id"]
|
Binary file not shown.
@ -1,18 +0,0 @@
|
||||
from django.test import TestCase
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
# Create your tests here.
|
||||
class ProfileTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user: User = User.objects.create(username='bozo', password='password')
|
||||
self.user.save()
|
||||
self.expected_response = {"name": "bozo",
|
||||
"title": ""}
|
||||
self.url = "/api/profiles/"
|
||||
|
||||
def test_profile_create_on_user_created(self):
|
||||
response: HttpResponse = self.client.get(self.url + str(self.user.pk))
|
||||
response_dict: dict = eval(response.content)
|
||||
self.assertDictEqual(self.expected_response, response_dict)
|
||||
|
@ -1,15 +0,0 @@
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
from . import viewsets
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("me", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'get': 'retrieve'}), name="my_profile_page"),
|
||||
path("<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
|
||||
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
|
||||
path("block", views.BlocksView.as_view(), name="block_page"),
|
||||
path("block/<int:pk>", views.BlockView.as_view(), name="block_page"),
|
||||
|
||||
] + static("/static/avatars/", document_root="./avatars")
|
@ -1,66 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import authentication, permissions, status
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from django.core import serializers
|
||||
from .models import BlockModel
|
||||
|
||||
class BlockView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request, pk):
|
||||
block = BlockModel.objects.filter(pk=pk)
|
||||
if (block):
|
||||
return Response(serializers.serialize("json", block), status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response("Not Found", status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
class BlocksView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request):
|
||||
blocks = BlockModel.objects.filter(blocker=request.user.pk)
|
||||
if (blocks):
|
||||
return Response(serializers.serialize("json", BlockModel.objects.filter(blocker=request.user.pk)), status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response({}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def post(self, request):
|
||||
data: dict = request.data
|
||||
users_id = request.data.get("users_id", None)
|
||||
|
||||
if (users_id == None):
|
||||
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if (BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])):
|
||||
return Response({"Already Exist"}, status=status.HTTP_409_CONFLICT)
|
||||
|
||||
new_block = BlockModel()
|
||||
new_block.blocker = users_id[0]
|
||||
new_block.blocked = users_id[1]
|
||||
new_block.save()
|
||||
|
||||
return Response({"block_id": new_block.pk}, status=status.HTTP_201_CREATED)
|
||||
|
||||
def delete(self, request):
|
||||
data: dict = request.data
|
||||
users_id = request.data.get("users_id", None)
|
||||
|
||||
if (users_id == None):
|
||||
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
block = BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])
|
||||
|
||||
print(list(block))
|
||||
if (block.count() > 1):
|
||||
return Response("Not normal >:[", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
if (not block):
|
||||
return Response("Don't exist", status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
block.delete()
|
||||
return Response("Deleted", status=status.HTTP_200_OK)
|
@ -1,61 +0,0 @@
|
||||
from rest_framework import permissions
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from .serializers import ProfileSerializer
|
||||
from .models import ProfileModel
|
||||
|
||||
class ProfileViewSet(viewsets.ModelViewSet):
|
||||
queryset = ProfileModel.objects.all
|
||||
serializer_class = ProfileSerializer
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||
|
||||
def retrieve(self, request: HttpRequest, pk=None):
|
||||
if (not self.queryset().filter(pk=pk).exists()):
|
||||
return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
instance = self.queryset().get(pk=pk)
|
||||
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
|
||||
return Response(self.serializer_class(instance).data,
|
||||
status=status.HTTP_200_OK)
|
||||
|
||||
def list(self, request: HttpRequest):
|
||||
serializer = ProfileSerializer(self.queryset(), many=True)
|
||||
for profile in serializer.data:
|
||||
profile["avatar_url"] = profile["avatar_url"][profile["avatar_url"].find("static") - 1:]
|
||||
return Response(serializer.data)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(user=self.request.user)
|
||||
|
||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
serializer_class = ProfileSerializer
|
||||
queryset = ProfileModel.objects.all
|
||||
|
||||
def get_object(self):
|
||||
obj = self.queryset().get(pk=self.request.user.pk)
|
||||
return obj
|
||||
|
||||
def perform_update(self, serializer, pk=None):
|
||||
profile: ProfileModel = self.get_object()
|
||||
avatar = self.request.data.get("file", None)
|
||||
if (avatar is not None):
|
||||
if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"):
|
||||
profile.avatar_url.storage.delete(profile.avatar_url.name)
|
||||
profile.avatar_url = avatar
|
||||
profile.save()
|
||||
|
||||
def retrieve(self, request: HttpRequest, pk=None):
|
||||
instance: ProfileModel = self.get_object()
|
||||
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
|
||||
return Response(self.serializer_class(instance).data,
|
||||
status=status.HTTP_200_OK)
|
@ -1,30 +0,0 @@
|
||||
asgiref==3.7.2
|
||||
attrs==23.1.0
|
||||
autobahn==23.6.2
|
||||
Automat==22.10.0
|
||||
cffi==1.16.0
|
||||
channels==4.0.0
|
||||
constantly==23.10.4
|
||||
cryptography==41.0.7
|
||||
daphne==4.0.0
|
||||
Django==4.2.6
|
||||
django-cors-headers==4.3.0
|
||||
django-restapi==0.1.3
|
||||
djangorestframework==3.14.0
|
||||
hyperlink==21.0.0
|
||||
idna==3.6
|
||||
incremental==22.10.0
|
||||
install==1.3.5
|
||||
Pillow==10.1.0
|
||||
pyasn1==0.5.1
|
||||
pyasn1-modules==0.3.0
|
||||
pycparser==2.21
|
||||
pyOpenSSL==23.3.0
|
||||
pytz==2023.3.post1
|
||||
service-identity==23.1.0
|
||||
six==1.16.0
|
||||
sqlparse==0.4.4
|
||||
Twisted==23.10.0
|
||||
txaio==23.1.1
|
||||
typing_extensions==4.8.0
|
||||
zope.interface==6.1
|
@ -1,30 +0,0 @@
|
||||
"""
|
||||
ASGI config for trancendence project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
|
||||
import chat.routing
|
||||
import matchmaking.routing
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'trancendence.settings')
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
'http':get_asgi_application(),
|
||||
'websocket':AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
chat.routing.websocket_urlpatterns +
|
||||
matchmaking.routing.websocket_urlpatterns
|
||||
)
|
||||
)
|
||||
})
|
||||
|
@ -1,154 +0,0 @@
|
||||
"""
|
||||
Django settings for trancendence project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.2.6.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-18!@88-wm-!skec9^n-85n(f$my^#mh3!#@f=_e@=*arh_yyjj'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
CORS_ORIGIN_ALLOW_ALL = False
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = ["https://django.chauvet.pro"]
|
||||
|
||||
CORS_ORIGIN_WHITELIST = (
|
||||
'http://localhost:8000',
|
||||
)
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'channels',
|
||||
'daphne',
|
||||
|
||||
'matchmaking.apps.MatchmakingConfig',
|
||||
'games.apps.GamesConfig',
|
||||
'accounts.apps.AccountsConfig',
|
||||
'profiles.apps.ProfilesConfig',
|
||||
'frontend.apps.FrontendConfig',
|
||||
'chat.apps.ChatConfig',
|
||||
|
||||
'corsheaders',
|
||||
'rest_framework',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
ASGI_APPLICATION = 'trancendence.asgi.application'
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
'default' :{
|
||||
'BACKEND':'channels.layers.InMemoryChannelLayer'
|
||||
}
|
||||
}
|
||||
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'trancendence.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'trancendence.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user