From 6b83774995d9592c37c86dbe5ffbf6bbcd6bf49c Mon Sep 17 00:00:00 2001 From: Recep BATTAL Date: Sun, 21 Jul 2024 02:18:45 +0300 Subject: [PATCH] last-back&profile-merge --- apigateway/Dockerfile | 1 - apigateway/apigateway/settings.py | 3 +- apigateway/requirements.txt | 2 + apigateway/src/middleware.py | 8 + apigateway/src/views.py | 20 +- authservice/Dockerfile | 1 + authservice/src/views.py | 95 ++-- bucketservice/Dockerfile | 1 + bucketservice/bucketservice/settings.py | 2 +- .../Ekran_Resmi_2024-07-20_OS_5.56.23.png | Bin 0 -> 4966 bytes bucketservice/src/views.py | 2 +- docker-compose.yml | 22 +- friendservice/Dockerfile | 1 + friendservice/friendservice/settings.py | 2 +- friendservice/srcs/repository.py | 1 + friendservice/srcs/service.py | 2 + frontend/Dockerfile | 13 + frontend/add.txt | 11 - frontend/index.html | 6 +- frontend/nginx.conf | 60 ++- ...024-03-07 03.44.57.png => Mighty-Pong.png} | Bin frontend/src/Routes.js | 14 + frontend/src/components/AoaComponent.js | 15 + frontend/src/components/AuthComponent.js | 14 + frontend/src/contants/contants.js | 61 +-- frontend/src/routes/ai/Ai.js | 37 ++ frontend/src/routes/ai/ai.html | 280 ++++++------ frontend/src/routes/aoa/Aoa.js | 4 + frontend/src/routes/aoa/aoa.html | 13 + frontend/src/routes/auth/Auth.js | 18 + frontend/src/routes/auth/auth.html | 22 + frontend/src/routes/edit/Edit.js | 13 +- frontend/src/routes/edit/edit.html | 10 +- .../routes/friendrequests/Friendrequests.js | 210 ++++----- frontend/src/routes/friends/Friends.js | 181 ++++---- frontend/src/routes/game/Game.js | 41 +- frontend/src/routes/homepage/HomePage.js | 14 - frontend/src/routes/localgame/Localgame.js | 42 +- frontend/src/routes/localgame/localgame.html | 3 +- .../routes/localtournament/Localtournament.js | 373 ++++++++-------- .../localtournament/localtournament.html | 7 +- frontend/src/routes/login/Login.js | 2 +- frontend/src/routes/login/login.html | 13 + .../src/routes/otherprofile/Otherprofile.js | 132 +++--- .../src/routes/otherprofile/otherprofile.html | 388 ++++++++--------- frontend/src/routes/profile/Profile.js | 77 ++-- frontend/src/routes/profile/profile.html | 407 +++++++++--------- frontend/src/routes/quickplay/quickplay.html | 3 +- frontend/src/routes/register/register.html | 5 +- frontend/src/routes/tournament/Tournament.js | 55 --- .../src/routes/tournament/tournament.html | 15 - frontend/src/routes/users/Users.js | 150 +++---- frontend/src/utils/navTo.js | 3 +- frontend/src/utils/utils.js | 21 +- gameplayservice/Dockerfile | 2 + gameplayservice/gameplayservice/consumers.py | 38 +- gameplayservice/gameplayservice/settings.py | 8 +- gameservice/Dockerfile | 1 + gameservice/src/serializers.py | 1 + gameservice/src/service.py | 104 +++-- gameservice/src/views.py | 12 +- statusservice/Dockerfile | 4 +- statusservice/statusservice/consumers.py | 1 + statusservice/statusservice/settings.py | 8 +- usermanagement/src/interfaces/repository.py | 2 +- usermanagement/src/interfaces/service.py | 6 +- usermanagement/src/repository.py | 5 +- usermanagement/src/serializers.py | 8 +- usermanagement/src/service.py | 73 +++- usermanagement/src/utils.py | 2 - usermanagement/src/views.py | 38 +- 71 files changed, 1812 insertions(+), 1397 deletions(-) create mode 100644 bucketservice/media/images/Ekran_Resmi_2024-07-20_OS_5.56.23.png create mode 100644 frontend/Dockerfile delete mode 100644 frontend/add.txt rename frontend/public/images/{Ekran Resmi 2024-03-07 03.44.57.png => Mighty-Pong.png} (100%) create mode 100644 frontend/src/components/AoaComponent.js create mode 100644 frontend/src/components/AuthComponent.js create mode 100644 frontend/src/routes/aoa/Aoa.js create mode 100644 frontend/src/routes/aoa/aoa.html create mode 100644 frontend/src/routes/auth/Auth.js create mode 100644 frontend/src/routes/auth/auth.html delete mode 100644 frontend/src/routes/tournament/Tournament.js delete mode 100644 frontend/src/routes/tournament/tournament.html diff --git a/apigateway/Dockerfile b/apigateway/Dockerfile index 89420d4..1d3180c 100644 --- a/apigateway/Dockerfile +++ b/apigateway/Dockerfile @@ -8,6 +8,5 @@ RUN pip3 install --upgrade pip RUN pip3 install -r requirements.txt -EXPOSE 8000 CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/apigateway/apigateway/settings.py b/apigateway/apigateway/settings.py index 74e0f2a..9b08866 100644 --- a/apigateway/apigateway/settings.py +++ b/apigateway/apigateway/settings.py @@ -26,7 +26,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.environ.get('DEBUG') -ALLOWED_HOSTS = ['35.242.209.3', 'localhost', '127.0.0.1', '0.0.0.0', 'apigateway', 'usermanagement', 'authservice', 'gameservice', 'friendservice', 'statusservice', 'gameplayservice'] +ALLOWED_HOSTS = ['35.242.209.3', 'localhost', '127.0.0.1', '0.0.0.0', 'apigateway', 'usermanagement', 'authservice', 'gameservice', 'friendservice', 'statusservice', 'gameplayservice', '[::1]'] # Application definition @@ -39,6 +39,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', ] MIDDLEWARE = [ diff --git a/apigateway/requirements.txt b/apigateway/requirements.txt index 38a53dc..ce13035 100644 --- a/apigateway/requirements.txt +++ b/apigateway/requirements.txt @@ -1,3 +1,5 @@ +django +django-core djangorestframework django-cors-headers requests diff --git a/apigateway/src/middleware.py b/apigateway/src/middleware.py index c8b586d..619e5d2 100644 --- a/apigateway/src/middleware.py +++ b/apigateway/src/middleware.py @@ -1,7 +1,10 @@ +import logging + import requests from django.conf import settings from django.http import JsonResponse + class JWTAuthenticationMiddleware: def __init__(self, get_response): self.get_response = get_response @@ -12,9 +15,14 @@ def __call__(self, request): return response def process_view(self, request, view_func, view_args, view_kwargs): + if request.path.startswith('/api'): + request.path = request.path[4:] if request.path in self.paths_to_exclude: return None + if request.path.startswith('/user/email_verify'): + return None + token = request.headers.get('Authorization') if not token: return JsonResponse({'error': 'Missing token'}, status=401) diff --git a/apigateway/src/views.py b/apigateway/src/views.py index 9bda469..cc50871 100644 --- a/apigateway/src/views.py +++ b/apigateway/src/views.py @@ -1,3 +1,5 @@ +import logging + import requests from django.conf import settings from django.http import HttpResponseRedirect @@ -10,6 +12,9 @@ class APIGatewayView(APIView): def operations(self, request, path): headers = dict(request.headers) + if request.path.startswith('/user/email_verify'): + headers['Content-Type'] = 'application/json' + return pass_request_to_destination_service(request, path, headers) if not (settings.EXCLUDED_ROUTES and request.path in settings.EXCLUDED_ROUTES): headers['id'] = str(request.user_id) return pass_request_to_destination_service(request, path, headers) @@ -40,13 +45,26 @@ def pass_request_to_destination_service(request, path, headers): if params: full_url += f"?{params.urlencode()}" method = request.method.lower() - response = requests.request(method, full_url, headers=headers, json=request.data) + try: + response = requests.request(method, full_url, headers=headers, json=request.data) + except requests.exceptions.RequestException as e: + logging.error(f"Error in request: {e}") + return Response({'error': 'Internal server error'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) if path.startswith('auth/') and response.status_code == 200 or response.status_code == 207: json_response = response.json() if 'redirect_url' in json_response: return HttpResponseRedirect(json_response['redirect_url']) + if path.startswith('user/email_verify') and response.status_code == 200: + try: + json_response = response.json() + if 'redirect_url' in json_response: + return HttpResponseRedirect(json_response['redirect_url']) + except Exception as e: + logging.error(f"Error in request: {e}") + return Response({'error': 'Internal server error'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + if response.headers.get('content-type') == 'application/json': return Response(response.json(), status=response.status_code) return Response(response.content, status=response.status_code) diff --git a/authservice/Dockerfile b/authservice/Dockerfile index 05ba701..334e0cd 100644 --- a/authservice/Dockerfile +++ b/authservice/Dockerfile @@ -8,4 +8,5 @@ RUN pip3 install --upgrade pip RUN pip3 install -r requirements.txt + CMD [ "python3", "manage.py", "runserver", "0.0.0.0:8001"] diff --git a/authservice/src/views.py b/authservice/src/views.py index 0a8c8fe..b5e2393 100644 --- a/authservice/src/views.py +++ b/authservice/src/views.py @@ -1,4 +1,5 @@ import jwt +import uuid import requests from datetime import datetime, timedelta, timezone from rest_framework import viewsets @@ -9,26 +10,25 @@ import logging -def generate_access_token(user_id): +def generate_access_token(user_id, jti): payload = { 'user_id': user_id, + 'jti': jti, # unique identifier for the token pair. 'exp': datetime.now(tz=timezone.utc) + timedelta(minutes=1), 'iat': datetime.now(tz=timezone.utc) } access_token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256') - iat_seconds = payload['iat'].timestamp() - logging.error("------------------------------->>>>>>>>> %s", iat_seconds) - return access_token, iat_seconds + return access_token -def generate_refresh_token(user_id, exp, iat, access_token_iat): +def generate_refresh_token(user_id, exp, iat, jti): if exp is None: exp = datetime.now(tz=timezone.utc) + timedelta(days=7) if iat is None: iat = datetime.now(tz=timezone.utc) payload = { 'user_id': user_id, - 'acces_token_iat': access_token_iat, + 'jti': jti, 'exp': exp, 'iat': iat, } @@ -39,15 +39,37 @@ def generate_refresh_token(user_id, exp, iat, access_token_iat): class AuthHandler(viewsets.ViewSet): SECRET_KEY = settings.SECRET_KEY - verification_options = {"require": ["exp", "iat"], "verify_exp": True, "verify_iat": True, "verify_signature": True} + verification_options = { + "require": ["exp", "iat", "jti"], + "verify_exp": True, + "verify_iat": True, + "verify_signature": True + } + + def _decode_token(self, token, verify_exp=True): + try: + options = self.verification_options.copy() + options['verify_exp'] = verify_exp + return jwt.decode(token, self.SECRET_KEY, algorithms=['HS256'], options=options), None + except jwt.ExpiredSignatureError as e: + return None, 'Token has expired' + except jwt.InvalidTokenError as e: + return None, 'Invalid token' + except Exception as e: + logging.error("Unexpected error decoding token: %s", str(e)) + return None, 'Error decoding token' + + def _error_response(self, message, status_code=status.HTTP_400_BAD_REQUEST): + return Response({'error': message}, status=status_code) def generate_tokens(self, request): req = GenerateTokenSerializer(data=request.query_params) if not req.is_valid(): return Response(req.errors, status=400) user_id = req.validated_data['user_id'] - access_token, acss_iat = generate_access_token(user_id) - refresh_token = generate_refresh_token(user_id, None, None, acss_iat) + jti = str(uuid.uuid4()) + access_token = generate_access_token(user_id, jti) + refresh_token = generate_refresh_token(user_id, None, None, jti) return Response({ 'access_token': access_token, 'refresh_token': refresh_token @@ -71,39 +93,33 @@ def validate_token(self, request): def refresh_token(self, request): access_token = request.headers.get('Authorization') if not access_token: - return Response({'error': 'Token is required'}, status=status.HTTP_400_BAD_REQUEST) + return self._error_response('Access token is required', status.HTTP_401_UNAUTHORIZED) + req = RefreshTokenSerializer(data=request.data) if not req.is_valid(): return Response(req.errors, status=status.HTTP_400_BAD_REQUEST) + refresh_token = req.validated_data['refresh_token'] + decoded_access_token, access_error = self._decode_token(access_token.split(' ')[1], verify_exp=False) + decoded_refresh_token, refresh_error = self._decode_token(refresh_token, verify_exp=True) - try: - decoded_access_token = jwt.decode(access_token.split(' ')[1], self.SECRET_KEY, algorithms=['HS256'], options={'verify_exp': False}) - except jwt.ExpiredSignatureError: - try: - accs_iat = decoded_access_token['iat'] - refresh_token = refresh_token.split(' ')[1] - decoded_token = jwt.decode(refresh_token, self.SECRET_KEY, algorithms=['HS256'], options=self.verification_options) - refresh_accs_iat = decoded_token['acces_token_iat'] - if accs_iat != refresh_accs_iat: - return Response({'error': 'Access token and refresh token do not match'}, status=status.HTTP_400_BAD_REQUEST) - user_id = decoded_token['user_id'] - new_access_token, access_iat = generate_access_token(user_id) - new_refresh_token = generate_refresh_token(user_id, decoded_token['exp'], decoded_token['iat'], access_iat) - return Response({ - 'access_token': new_access_token, - 'refresh_token': new_refresh_token - }, status=status.HTTP_200_OK) - except jwt.ExpiredSignatureError: - return Response({'error': 'Token has expired'}, status=status.HTTP_401_UNAUTHORIZED) - except jwt.InvalidTokenError: - return Response({'error': 'Invalid token'}, status=status.HTTP_401_UNAUTHORIZED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) - except jwt.InvalidTokenError: - return Response({'error': 'Invalids token'}, status=status.HTTP_401_UNAUTHORIZED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) + if access_error: + return self._error_response(access_error, status.HTTP_401_UNAUTHORIZED) + if refresh_error: + return self._error_response(refresh_error, status.HTTP_401_UNAUTHORIZED) + + if decoded_access_token['jti'] != decoded_refresh_token['jti']: + return self._error_response('Access token and refresh token do not match') + + user_id = decoded_refresh_token['user_id'] + jti = str(uuid.uuid4()) # Generate a new jti for the new token pair + new_access_token = generate_access_token(user_id, jti) + new_refresh_token = generate_refresh_token(user_id, None, None, jti) + + return Response({ + 'access_token': new_access_token, + 'refresh_token': new_refresh_token + }, status=status.HTTP_200_OK) def intra_oauth(self, request): return Response({'url': settings.INTRA_REDIRECT_URL}, status=status.HTTP_200_OK) @@ -129,9 +145,10 @@ def intra_oauth_callback(self, request): return Response(user_creation_response.json(), status=user_creation_response.status_code) response_data = user_creation_response.json().get('data') + jti = str(uuid.uuid4()) user_id = response_data[0].get('id') - token, access_iat = generate_access_token(user_id) - refresh_token = generate_refresh_token(user_id, None, None, access_iat) + token = generate_access_token(user_id, jti) + refresh_token = generate_refresh_token(user_id, None, None, jti) # 207 status code is for username already exist. # Redirect to frontend for update username. Otherwise, redirect to homepage diff --git a/bucketservice/Dockerfile b/bucketservice/Dockerfile index 5fa3009..f627c74 100644 --- a/bucketservice/Dockerfile +++ b/bucketservice/Dockerfile @@ -16,6 +16,7 @@ COPY start.sh /app/start.sh RUN chmod +x /app/start.sh + ENTRYPOINT ["sh", "./start.sh"] CMD ["python3", "manage.py", "runserver", "0.0.0.0:8014"] diff --git a/bucketservice/bucketservice/settings.py b/bucketservice/bucketservice/settings.py index 69c49d4..bbd905b 100644 --- a/bucketservice/bucketservice/settings.py +++ b/bucketservice/bucketservice/settings.py @@ -26,7 +26,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.environ.get('DEBUG') -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['*'] SERVICE_ROUTES = { '/auth': 'http://authservice:8001', diff --git a/bucketservice/media/images/Ekran_Resmi_2024-07-20_OS_5.56.23.png b/bucketservice/media/images/Ekran_Resmi_2024-07-20_OS_5.56.23.png new file mode 100644 index 0000000000000000000000000000000000000000..60050606a6956dc4e1be99204731c85631a592e0 GIT binary patch literal 4966 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 yW{vdLR%9b!$C~FC+w;u$XPW BaseResponse: @@ -30,6 +31,7 @@ def get_requests(self, user_id, page, limit) -> BaseResponse: def get_friends(self, user_id, page, limit) -> BaseResponse: pass + class FriendsService(IFriendsService): def __init__(self, repository: IFriendsRepository): self.repository = repository diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..7af4ea3 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,13 @@ +FROM nginx:alpine + +RUN apk update && \ + apk add --no-cache openssl + + +RUN mkdir -p /etc/ssl/private/ && mkdir -p /etc/ssl/certs/ && \ + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /etc/ssl/private/nginx.key \ + -out /etc/ssl/certs/nginx.crt \ + -subj "/C=TR/ST=Kocaeli/L=Kocaeli/O=Ecole42/OU=transcendence/CN=127.0.0.1:443" + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/add.txt b/frontend/add.txt deleted file mode 100644 index 1b1bc55..0000000 --- a/frontend/add.txt +++ /dev/null @@ -1,11 +0,0 @@ -serializer.py - - -register->phone -2fa->email, twofa_code -login->forget email(?) - -register.js -> /authservice/src/views.py -register formundan alınan kullanıcı bilgileri bu sayfaya gidiyor olabilir. -bunu kontrol edebilmek için uzantıdaki dosyaya yorum satırında bir kod ekledim. -eğer doğru bir şekilde formdan alınan veriler backende gelirse konsola yazdırıyors \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 0c59d44..d0f9073 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -33,12 +33,16 @@ transform: translateX(-50%); } body, html { + /* background-image: url('public/images/Mighty-Pong.png'); */ + /* background-size: 75%; + background-repeat: no-repeat; + background-position: center; */ height: 100%; margin: 0; display: flex; flex-direction: column; background-color: #e2e8f0; - + z-index: -1; } nav { width: 100%; diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 6aac9ad..42429a4 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -1,14 +1,39 @@ +#user nginx; +worker_processes auto; + +#error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + events { - worker_connections 1024; + worker_connections 1024; } http { - include /etc/nginx/mime.types; + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + gzip on; + + include /etc/nginx/conf.d/*.conf; server { - listen 80; - server_name localhost; + listen 443 ssl; + listen [::]:443 ssl; + + server_name localhost 127.0.0.1; + + ssl_certificate /etc/ssl/certs/nginx.crt; + ssl_certificate_key /etc/ssl/private/nginx.key; location / { root /usr/share/nginx/html; @@ -16,11 +41,36 @@ http { try_files $uri $uri/ /index.html; } + location /api/ { + proxy_pass http://apigateway:8000/; + } + + location /bucket/ { + proxy_pass http://bucketservice:8014; + } + + location /ws/game/ { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_http_version 1.1; + proxy_set_header Host $host; + + proxy_pass http://gameplayservice:8011; + } + + location /ws/status/ { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_http_version 1.1; + proxy_set_header Host $host; + + proxy_pass http://statusservice:8020; + } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } - } + } } diff --git a/frontend/public/images/Ekran Resmi 2024-03-07 03.44.57.png b/frontend/public/images/Mighty-Pong.png similarity index 100% rename from frontend/public/images/Ekran Resmi 2024-03-07 03.44.57.png rename to frontend/public/images/Mighty-Pong.png diff --git a/frontend/src/Routes.js b/frontend/src/Routes.js index baeee95..7da9e37 100644 --- a/frontend/src/Routes.js +++ b/frontend/src/Routes.js @@ -19,8 +19,16 @@ import ForgotpasswordComponent from "./components/ForgotpasswordComponent.js" import ResetpasswordComponent from "./components/ResetpasswordComponent.js" import ChangepasswordComponent from "./components/ChangepasswordComponent.js" import ConflictusernameComponent from "./components/ConflictusernameComponent.js" +import AoaComponent from "./components/AoaComponent.js" +import AuthComponent from "./components/AuthComponent.js" export const routes = [ + { + path:"/404", + htmlPath: "./src/routes/aoa/aoa.html", + js: "/src/routes/aoa/Aoa.js", + component: AoaComponent, + }, { path:"/", htmlPath: "./src/routes/homepage/homepage.html", @@ -146,5 +154,11 @@ export const routes = [ htmlPath: "./src/routes/conflictusername/conflictusername.html", js: "/src/routes/conflictusername/Conflictusername.js", component: ConflictusernameComponent, + }, + { + path:"/auth", + htmlPath: "./src/routes/auth/auth.html", + js: "/src/routes/auth/Auth.js", + component: AuthComponent, } ] diff --git a/frontend/src/components/AoaComponent.js b/frontend/src/components/AoaComponent.js new file mode 100644 index 0000000..11b4387 --- /dev/null +++ b/frontend/src/components/AoaComponent.js @@ -0,0 +1,15 @@ +class AoaComponent { + constructor(path) { + this.path = path; + } + async render() { + return fetch(this.path) + .then(res => { + if (!res.ok) + throw new Error("couldn't fetch route"); + return res.text(); + }) + } +} + +export default AoaComponent; diff --git a/frontend/src/components/AuthComponent.js b/frontend/src/components/AuthComponent.js new file mode 100644 index 0000000..fdbb79c --- /dev/null +++ b/frontend/src/components/AuthComponent.js @@ -0,0 +1,14 @@ +class AuthComponent { + constructor(path) { + this.path = path; + } + async render() { + return fetch(this.path) + .then(res => { + if (!res.ok) + throw new Error("couldn't fetch route"); + return res.text(); + }) + } +} +export default AuthComponent; diff --git a/frontend/src/contants/contants.js b/frontend/src/contants/contants.js index 8f3f7fc..62fbfe5 100644 --- a/frontend/src/contants/contants.js +++ b/frontend/src/contants/contants.js @@ -1,28 +1,33 @@ -export const changeUrlBase = "http://localhost:8000/user/pwd/update"; -export const changeUsernameUrl = "http://127.0.0.1:8000/user/username"; -export const userDetailUrl = "http://127.0.0.1:8000/user/details"; -export const updateUserUrl = "http://127.0.0.1:8000/user/update"; -export const pictureUrl = "http://localhost:8014/bucket/image/serve"; -export const avatarUpdateUrl = "http://localhost:8014/bucket/image"; -export const forgotpasswordUrl = "http://127.0.0.1:8000/user/pwd/forgot"; -export const userGetByIdUrl = "http://127.0.0.1:8000/user/get/id"; -export const requestsList = "http://127.0.0.1:8000/friends/request"; -export const acceptUrl = "http://127.0.0.1:8000/friends/accept"; -export const rejectUrl = "http://127.0.0.1:8000/friends/reject"; -export const friendList = "http://127.0.0.1:8000/friends/list"; -export const friendDelete = "http://127.0.0.1:8000/friends/delete"; -export const singleUserDetailUrl = "http://127.0.0.1:8000/user/get/id"; -export const gameDetailUrl = "http://127.0.0.1:8000/game/list"; -export const joinUrl = "http://127.0.0.1:8000/game/join"; -export const matchHistoryUrl = "http://127.0.0.1:8000/game/history"; -export const gameCreateUrl = "http://127.0.0.1:8000/game/room"; -export const registerUrl = "http://localhost:8000/user/register"; -export const loginUrl = "http://127.0.0.1:8000/user/login"; -export const IntraOAuthUrl = "http://127.0.0.1:8000/auth/intra" -export const resetUrlBase = "http://localhost:8004/user/pwd/change"; -export const url = "http://127.0.0.1:8000/user/2fa"; -export const twofaUrl = "http://127.0.0.1:8000/user/2fa"; -export const searchUrl = "http://127.0.0.1:8000/user/search"; -export const friendAdd = "http://127.0.0.1:8000/friends/add"; -export const ValidateAccessToken = "http://127.0.0.1:8000/auth/token/validate"; -export const ValidateRefreshToken = "http://127.0.0.1:8000/auth/token/refresh"; +const BASE_DOMAIN = "127.0.0.1:443"; +const BASE_URL = "https://" + BASE_DOMAIN; +export const changeUrlBase = BASE_URL + "/api/user/pwd/update"; +export const changeUsernameUrl = BASE_URL + "/api/user/username"; +export const userDetailUrl = BASE_URL + "/api/user/details"; +export const updateUserUrl = BASE_URL + "/api/user/update"; +export const pictureUrl = BASE_URL + "/bucket/image/serve"; +export const avatarUpdateUrl = BASE_URL + "/bucket/image"; +export const forgotpasswordUrl = BASE_URL + "/api/user/pwd/forgot"; +export const userGetByIdUrl = BASE_URL + "/api/user/get/id"; +export const requestsList = BASE_URL + "/api/friends/request"; +export const acceptUrl = BASE_URL + "/api/friends/accept"; +export const rejectUrl = BASE_URL + "/api/friends/reject"; +export const friendList = BASE_URL + "/api/friends/list"; +export const friendDelete = BASE_URL + "/api/friends/delete"; +export const singleUserDetailUrl = BASE_URL + "/api/user/get/id"; +export const gameDetailUrl = BASE_URL + "/api/game/list"; +export const joinUrl = BASE_URL + "/api/game/join"; +export const matchHistoryUrl = BASE_URL + "/api/game/history"; +export const gameCreateUrl = BASE_URL + "/api/game/room"; +export const registerUrl = BASE_URL + "/api/user/register"; +export const loginUrl = BASE_URL + "/api/user/login"; +export const IntraOAuthUrl = BASE_URL + "/api/auth/intra" +export const resetUrlBase = BASE_URL + "/api/user/pwd/change"; +export const url = BASE_URL + "/api/user/2fa"; +export const twofaUrl = BASE_URL + "/api/user/2fa"; +export const searchUrl = BASE_URL + "/api/user/search"; +export const friendAdd = BASE_URL + "/api/friends/add"; +export const ValidateAccessToken = BASE_URL + "/api/auth/token/validate"; +export const ValidateRefreshToken = BASE_URL + "/api/auth/token/refresh"; + +export const GamePlaySocketUrl = "wss://"+BASE_DOMAIN+"/ws/game/"; +export const StatusServiceSocketUrl = "wss://"+BASE_DOMAIN+"/ws/status/"; diff --git a/frontend/src/routes/ai/Ai.js b/frontend/src/routes/ai/Ai.js index 13e7258..f0646b2 100644 --- a/frontend/src/routes/ai/Ai.js +++ b/frontend/src/routes/ai/Ai.js @@ -167,6 +167,43 @@ export async function fetchAi() { } }); + function finishGame() { + gameRunning = false; + startButton.style.display = 'block'; + alert("VS AI is aborted!"); + } + + // Listen for navigation events + const originalPushState = history.pushState; + const originalReplaceState = history.replaceState; + + history.pushState = function () { + if (gameRunning) { + finishGame(); + } + return originalPushState.apply(history, arguments); + }; + + history.replaceState = function () { + if (gameRunning) { + finishGame(); + } + return originalReplaceState.apply(history, arguments); + }; + + window.addEventListener('popstate', () => { + if (gameRunning) { + finishGame(); + } + }); + + function navigateTo(url) { + if (gameRunning) { + finishGame(); + } + originalNavigateTo(url); + } + draw(); } } diff --git a/frontend/src/routes/ai/ai.html b/frontend/src/routes/ai/ai.html index 05192b9..8ad6480 100644 --- a/frontend/src/routes/ai/ai.html +++ b/frontend/src/routes/ai/ai.html @@ -1,142 +1,140 @@ - -
Player 1: 0 | AI: 0
- - + body { + margin: 0; + padding: 0; + background-color: #e2e8f0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + font-family: Arial, sans-serif; + position: relative; + } + #gameCanvas { + border: 2px solid #000000; + } + #scoreBoard { + position: absolute; + top: 365px; + font-size: 24px; + font-weight: bold; + color: #000000; + } + #startButton { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + .btn-23, + .btn-23 *, + .btn-23 :after, + .btn-23 :before, + .btn-23:after, + .btn-23:before { + border: 0 solid; + box-sizing: border-box; + } + + .btn-23 { + -webkit-tap-highlight-color: transparent; + -webkit-appearance: button; + background-color: #000; + background-image: none; + color: #fff; + cursor: pointer; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, + Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; + font-size: 100%; + font-weight: 900; + line-height: 1.5; + margin: 0; + -webkit-mask-image: -webkit-radial-gradient(#000, #fff); + padding: 0; + text-transform: uppercase; + } + + .btn-23:disabled { + cursor: default; + } + + .btn-23:-moz-focusring { + outline: auto; + } + + .btn-23 svg { + display: block; + vertical-align: middle; + } + + .btn-23 [hidden] { + display: none; + } + + .btn-23 { + border-radius: 99rem; + border-width: 2px; + overflow: hidden; + padding: 0.8rem 3rem; + position: relative; + } + + .btn-23 span { + display: grid; + inset: 0; + place-items: center; + position: absolute; + transition: opacity 0.2s ease; + } + + .btn-23 .marquee { + --spacing: 5em; + --start: 0em; + --end: 5em; + -webkit-animation: marquee 1s linear infinite; + animation: marquee 1s linear infinite; + -webkit-animation-play-state: paused; + animation-play-state: paused; + opacity: 0; + position: relative; + text-shadow: #fff var(--spacing) 0, #fff calc(var(--spacing) * -1) 0, + #fff calc(var(--spacing) * -2) 0; + } + + .btn-23:hover .marquee { + -webkit-animation-play-state: running; + animation-play-state: running; + opacity: 1; + } + + .btn-23:hover .text { + opacity: 0; + } + + @-webkit-keyframes marquee { + 0% { + transform: translateX(var(--start)); + } + + to { + transform: translateX(var(--end)); + } + } + + @keyframes marquee { + 0% { + transform: translateX(var(--start)); + } + + to { + transform: translateX(var(--end)); + } + } + +
Player 1: 0 | AI: 0
+ + diff --git a/frontend/src/routes/aoa/Aoa.js b/frontend/src/routes/aoa/Aoa.js new file mode 100644 index 0000000..9f1e1de --- /dev/null +++ b/frontend/src/routes/aoa/Aoa.js @@ -0,0 +1,4 @@ + +export async function fetchAoa() { + document.getElementById("nav-bar").style.display = "none"; +} diff --git a/frontend/src/routes/aoa/aoa.html b/frontend/src/routes/aoa/aoa.html new file mode 100644 index 0000000..badf025 --- /dev/null +++ b/frontend/src/routes/aoa/aoa.html @@ -0,0 +1,13 @@ + + +
+

404 Not Found

+

Sorry, the page you are looking for does not exist.

+ + Go to Home +
diff --git a/frontend/src/routes/auth/Auth.js b/frontend/src/routes/auth/Auth.js new file mode 100644 index 0000000..3b18246 --- /dev/null +++ b/frontend/src/routes/auth/Auth.js @@ -0,0 +1,18 @@ +import { navigateTo } from "../../utils/navTo.js"; + +export async function fetchAuth() { + console.log("fetchAuth"); + + const urlParams = new URLSearchParams(window.location.search); + const access_token = urlParams.get("access_token"); + const refresh_token = urlParams.get("refresh_token"); + + if (access_token && refresh_token) { + localStorage.setItem("access_token", access_token); + localStorage.setItem("refresh_token", refresh_token); + navigateTo("/"); + } else { + console.log("No access token found"); + navigateTo("/login"); + } +} diff --git a/frontend/src/routes/auth/auth.html b/frontend/src/routes/auth/auth.html new file mode 100644 index 0000000..f91320d --- /dev/null +++ b/frontend/src/routes/auth/auth.html @@ -0,0 +1,22 @@ + + +
+
+
+
+
+

Birazdan yönlendirileceksiniz

+
+
+
+

Lütfen bekleyin...

+
+
+
+
+
+ diff --git a/frontend/src/routes/edit/Edit.js b/frontend/src/routes/edit/Edit.js index a066c58..92e1b5c 100644 --- a/frontend/src/routes/edit/Edit.js +++ b/frontend/src/routes/edit/Edit.js @@ -24,7 +24,7 @@ export async function fetchEdit() { } const data = await response.json(); - const user = data[0].data[0]; + const user = data.data[0]; document.getElementById("profile-pic").src = pictureUrl + "?id=" + user.id; @@ -35,6 +35,7 @@ export async function fetchEdit() { document.querySelector("input[name='user-name']").value = user.username; document.querySelector("input[name='last-name']").value = user.last_name; document.querySelector("input[name='phone']").value = user.phone; + document.querySelector("input[name='email']").value = user.email; document.getElementById("save-button").addEventListener("click", async () => { const access_token = localStorage.getItem("access_token"); @@ -42,6 +43,7 @@ export async function fetchEdit() { const firstName = document.querySelector("input[name='first-name']").value; const lastName = document.querySelector("input[name='last-name']").value; const phone = document.querySelector("input[name='phone']").value; + const email = document.querySelector("input[name='email']").value; const fields_warning = document.getElementById('fields-warning'); let body = { @@ -49,7 +51,7 @@ export async function fetchEdit() { last_name: lastName, phone: phone, username: userName, - email: user.email, + email: email, }; try { @@ -66,6 +68,11 @@ export async function fetchEdit() { const errorData = await response.json(); throw errorData; } + if (response.status === 207) { + alert("Email updated successfully, please verify your email"); + document.getElementById("logout-button").click(); + return; + } alert("Profile updated successfully"); navigateTo("/profile"); @@ -80,6 +87,8 @@ export async function fetchEdit() { insertIntoElement('fields-warning', "First name error: " + err.first_name); } else if (err.last_name) { insertIntoElement('fields-warning', "Last name error: " + err.last_name); + } else if (err.email) { + insertIntoElement('fields-warning', "Email error: " + err.email); } else { insertIntoElement('fields-warning', "Error: internal server error"); console.log(err); diff --git a/frontend/src/routes/edit/edit.html b/frontend/src/routes/edit/edit.html index e879afd..57444f9 100644 --- a/frontend/src/routes/edit/edit.html +++ b/frontend/src/routes/edit/edit.html @@ -26,7 +26,7 @@
- Admin + Admin

John Doe

user-name
@@ -70,6 +70,14 @@
Phone
+
+
+
Email
+
+
+ +
+
Avatar
diff --git a/frontend/src/routes/friendrequests/Friendrequests.js b/frontend/src/routes/friendrequests/Friendrequests.js index bc6db1b..6ac6850 100644 --- a/frontend/src/routes/friendrequests/Friendrequests.js +++ b/frontend/src/routes/friendrequests/Friendrequests.js @@ -3,6 +3,7 @@ import { goPagination } from "../../utils/utils.js"; import { userGetByIdUrl, userDetailUrl, requestsList, acceptUrl, rejectUrl } from "../../contants/contants.js"; let currentPage = 1; // Current page +let total_pages = 1; export async function fetchFriendrequests() { const access_token = localStorage.getItem("access_token"); @@ -22,7 +23,7 @@ export async function fetchFriendrequests() { throw new Error(errorData.error); } const data = await response_user.json(); - const user = data[0].data[0]; + const user = data.data[0]; const response = await fetch(requestsList + "?page=" + currentPage + "&limit=5" , { method: "GET", @@ -39,115 +40,120 @@ export async function fetchFriendrequests() { const requests_res = await response.json(); const requests = requests_res.data - let pagination = requests_res.pagination; - let totalPages = pagination.total_pages; - + let paginate_data = requests_res.pagination; + if (paginate_data) { + total_pages = paginate_data.total_pages; + } console.log(requests); const tbody = document.querySelector(".table tbody"); tbody.innerHTML = ""; let sender = {}; - requests.forEach((request, index) => { - - fetch(userGetByIdUrl + "?id=" + request.id, { - method: "GET", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - } - }).then((response) => { - if (!response.ok) { - throw new Error("Failed to get user"); - } - return response.json(); - }).then((data) => { - let sender = data.data[0]; - - const row = document.createElement("tr"); - - const th = document.createElement("th"); - th.scope = "row"; - th.innerText = index + 1; - row.appendChild(th); - - const sender_td = document.createElement("td"); - sender_td.innerText = sender.username; // Adjust based on actual user object - row.appendChild(sender_td); - - const full_name = document.createElement("td"); - full_name.innerText = sender.first_name + " " + sender.last_name; // Adjust based on actual user object - row.appendChild(full_name); - - //add two buttons accept and reject button to each row - const td = document.createElement("td"); - const acceptButton = document.createElement("button"); - acceptButton.id = "accept-button"; - acceptButton.innerText = "Accept"; - acceptButton.className = "btn btn-primary"; - td.appendChild(acceptButton); - - const rejectButton = document.createElement("button"); - rejectButton.id = "reject-button"; - rejectButton.innerText = "Reject"; - rejectButton.className = "btn btn-danger"; - td.appendChild(rejectButton); - - row.appendChild(td); - - tbody.appendChild(row); - - acceptButton.addEventListener("click", async () => { - try { - const response = await fetch(acceptUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - }, - body: JSON.stringify({ - "receiver_id": request.id, - }) - }); + if (requests) + { - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error); + requests.forEach((request, index) => { + + fetch(userGetByIdUrl + "?id=" + request.id, { + method: "GET", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${access_token}`, } - navigateTo("/friendrequests"); - } catch (error) { - alert(error.message); - } - }); - - rejectButton.addEventListener("click", async () => { - try { - const response = await fetch(rejectUrl, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - }, - body: JSON.stringify({ - "receiver_id": request.id, - }) - }); + }).then((response) => { + if (!response.ok) { + throw new Error("Failed to get user"); + } + return response.json(); + }).then((data) => { + let sender = data.data[0]; + + const row = document.createElement("tr"); + + const th = document.createElement("th"); + th.scope = "row"; + th.innerText = index + 1; + row.appendChild(th); + + const sender_td = document.createElement("td"); + sender_td.innerText = sender.username; // Adjust based on actual user object + row.appendChild(sender_td); + + const full_name = document.createElement("td"); + full_name.innerText = sender.first_name + " " + sender.last_name; // Adjust based on actual user object + row.appendChild(full_name); + + //add two buttons accept and reject button to each row + const td = document.createElement("td"); + const acceptButton = document.createElement("button"); + acceptButton.id = "accept-button"; + acceptButton.innerText = "Accept"; + acceptButton.className = "btn btn-primary"; + td.appendChild(acceptButton); + + const rejectButton = document.createElement("button"); + rejectButton.id = "reject-button"; + rejectButton.innerText = "Reject"; + rejectButton.className = "btn btn-danger"; + td.appendChild(rejectButton); + + row.appendChild(td); + + tbody.appendChild(row); + + acceptButton.addEventListener("click", async () => { + try { + const response = await fetch(acceptUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${access_token}`, + }, + body: JSON.stringify({ + "receiver_id": request.id, + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error); + } + navigateTo("/friendrequests"); + } catch (error) { + alert(error.message); + } + }); - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error); + rejectButton.addEventListener("click", async () => { + try { + const response = await fetch(rejectUrl, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${access_token}`, + }, + body: JSON.stringify({ + "receiver_id": request.id, + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error); + } + navigateTo("/friendrequests"); + } catch (error) { + alert(error.message); } - navigateTo("/friendrequests"); - } catch (error) { - alert(error.message); - } - }); - - }).catch((error) => { - console.error(error); }); - }); - goPagination(totalPages, currentPage, async (newPage) => { - currentPage = newPage; - fetchFriendrequests(); - }, "pagination-container"); + + }).catch((error) => { + console.error(error); + }); + }); + goPagination(total_pages, currentPage, async (newPage) => { + currentPage = newPage; + fetchFriendrequests(); + }, "pagination-container"); + } } diff --git a/frontend/src/routes/friends/Friends.js b/frontend/src/routes/friends/Friends.js index a541c07..dfa80d6 100644 --- a/frontend/src/routes/friends/Friends.js +++ b/frontend/src/routes/friends/Friends.js @@ -3,29 +3,17 @@ import { userStatuses } from "../../utils/utils.js"; import { goPagination } from "../../utils/utils.js"; import { friendList, friendDelete, userDetailUrl, singleUserDetailUrl, pictureUrl } from "../../contants/contants.js"; -let currentPage = 1; // Current page +let currentPage = 1; +let total_pages = 1; + export async function fetchFriends() { const access_token = localStorage.getItem("access_token"); if (!access_token) { console.log("No access token found"); navigateTo("/login"); + return; } else { - const response_user = await fetch(userDetailUrl, { - method: "GET", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - } - }); - if (!response_user.ok) { - const errorData = await response_user.json(); - throw new Error(errorData.error); - } - const data_user = await response_user.json(); - const currentUser = data_user[0].data[0]; - const currentUserId = currentUser.id; - const response = await fetch(friendList + "?page=" + currentPage + "&limit=5", { method: "GET", headers: { @@ -35,97 +23,102 @@ export async function fetchFriends() { }); const data = await response.json(); - const users = data.data; - const pagination = data.pagination; - const totalPages = pagination.total_pages; + const users = data.data; + const paginate_data = data.pagination; + if (paginate_data) { + total_pages = paginate_data.total_pages; + } const tableBody = document.querySelector('.widget-26 tbody'); tableBody.innerHTML = ''; // Clear previous content - users.forEach(user => { - let user_res = {}; - fetch(singleUserDetailUrl + "?id=" + user.id, { - method: "GET", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - } - }).then(response => { - if (!response.ok) { - throw new Error("Failed to fetch user details"); - } - return response.json(); - }).then(data => { - user_res = data.data[0]; - - const userElement = document.createElement('tr'); - let image = pictureUrl + "?id=" + user.id; - const user_status = userStatuses.includes(user.id) ? true : false; - userElement.innerHTML = ` - -
- User Image -
- - - - - -
-

Email: ${user_res.email}

-

Phone: ${user_res.phone}

-
- - -
ID: ${user_res.id}
- - -
- - ${user_status === true ? 'Online' : 'Offline' } -
- - -
- -
- - `; - tableBody.appendChild(userElement); - document.getElementById(`delete-friend-button-${user_res.id}`).addEventListener("click", function(event) { - event.preventDefault(); - fetch(friendDelete,{ - method: 'DELETE', + if (users) { + users.forEach(user => { + let user_res = {}; + fetch(singleUserDetailUrl + "?id=" + user.id, { + method: "GET", headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${access_token}` - }, - body: JSON.stringify({ - receiver_id: user_res.id - }) + "Content-Type": "application/json", + "Authorization": `Bearer ${access_token}`, + } }).then(response => { if (!response.ok) { - return response.json().then(error => { - throw new Error(error.error); - }); + throw new Error("Failed to fetch user details"); } return response.json(); }).then(data => { - navigateTo("/friends"); - }).catch(error => { - alert(error.message); + user_res = data.data[0]; + + const userElement = document.createElement('tr'); + let image = pictureUrl + "?id=" + user.id; + const user_status = userStatuses.includes(user.id) ? true : false; + userElement.innerHTML = ` + +
+ User Image +
+ + + + + +
+

Email: ${user_res.email}

+

Phone: ${user_res.phone}

+
+ + +
ID: ${user_res.id}
+ + +
+ + ${user_status === true ? 'Online' : 'Offline' } +
+ + +
+ +
+ + `; + + tableBody.appendChild(userElement); + document.getElementById(`delete-friend-button-${user_res.id}`).addEventListener("click", function(event) { + event.preventDefault(); + fetch(friendDelete,{ + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${access_token}` + }, + body: JSON.stringify({ + receiver_id: user_res.id + }) + }).then(response => { + if (!response.ok) { + return response.json().then(error => { + throw new Error(error.error); + }); + } + return response.json(); + }).then(data => { + navigateTo("/friends"); + }).catch(error => { + alert(error.message); + }); }); }); - }); - }); - goPagination(totalPages, currentPage, async (newPage) => { - currentPage = newPage; - fetchFriends(); - }, "pagination-container"); + }); + goPagination(total_pages, currentPage, async (newPage) => { + currentPage = newPage; + fetchFriends(); + }, "pagination-container"); + } } } diff --git a/frontend/src/routes/game/Game.js b/frontend/src/routes/game/Game.js index 9d60544..4f8aa47 100644 --- a/frontend/src/routes/game/Game.js +++ b/frontend/src/routes/game/Game.js @@ -1,5 +1,5 @@ import { navigateTo } from "../../utils/navTo.js"; -const userDetailUrl = "http://127.0.0.1:8000/user/details"; +import { userDetailUrl, GamePlaySocketUrl } from "../../contants/contants.js"; export let ws; @@ -25,7 +25,7 @@ export async function fetchGame() { } const data = await response.json(); - const user = data[0].data[0]; + const user = data[0]; if (!game_id) { @@ -33,10 +33,11 @@ export async function fetchGame() { return; } - var connection = "ws://127.0.0.1:8011/ws/game/" + "?room=" + game_id + "?token=" + access_token; + var connection = GamePlaySocketUrl + "?room=" + game_id + "?token=" + access_token; ws = new WebSocket(connection); + ws.onopen = () => { console.log("Connected to server"); } @@ -49,10 +50,11 @@ export async function fetchGame() { ctx.fillRect(paddRight.positionX, paddRight.positionY, paddRight.sizeX, paddRight.sizeY); ctx.font = '30px Arial'; - ctx.fillText(paddRight.score, 50, 50); - ctx.fillText(paddLeft.score, canvas.width - 50, 50); - ctx.fillText(paddLeftUsername, canvas.width - 150, 50); - ctx.fillText(paddRightUsername, 75, 50); + ctx.fillText(paddRight.score, 150, 50); + ctx.fillText(paddRightUsername, 320, 50); + + ctx.fillText(paddLeft.score, canvas.width - 180, 50); + ctx.fillText(paddLeftUsername, canvas.width - 420, 50); } function drawBall(ball) { @@ -66,31 +68,6 @@ export async function fetchGame() { ws.onmessage = (message) => { let items = JSON.parse(message.data); - /* - if (items.message === "game_over") { - const requestBody = JSON.stringify(items); - const requestHeaders = { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}` - }; - fetch("http://localhost:8000/game/save", { - method: "POST", - headers: requestHeaders, - body: requestBody, - }).then((response) => { - console.log("Sunucudan gelen yanıt:", response); - if (!response.ok) { - throw new Error("Error saving game"); - } - return response.json(); - }).then((data) => { - console.log("Sunucudan gelen veri:", data); - }).catch((error) => { - console.log(error); - }); - navigateTo("/"); - }*/ - console.log(items); if (items.message === "game_over" && items.winner) { diff --git a/frontend/src/routes/homepage/HomePage.js b/frontend/src/routes/homepage/HomePage.js index 2726abb..292bf13 100644 --- a/frontend/src/routes/homepage/HomePage.js +++ b/frontend/src/routes/homepage/HomePage.js @@ -12,17 +12,3 @@ export async function fetchHomePage() { } } -export async function fetchAuth() { - const urlParams = new URLSearchParams(window.location.search); - const access_token = urlParams.get("access_token"); - const refresh_token = urlParams.get("refresh_token"); - - if (access_token && refresh_token) { - localStorage.setItem("access_token", access_token); - localStorage.setItem("refresh_token", refresh_token); - navigateTo("/"); - } else { - console.log("No access token found"); - navigateTo("/login"); - } -} diff --git a/frontend/src/routes/localgame/Localgame.js b/frontend/src/routes/localgame/Localgame.js index e9074a7..aa67bcf 100644 --- a/frontend/src/routes/localgame/Localgame.js +++ b/frontend/src/routes/localgame/Localgame.js @@ -1,4 +1,12 @@ -import { navigateTo } from "../../utils/navTo.js"; +import { navigateTo as originalNavigateTo } from "../../utils/navTo.js"; + +// Override navigateTo function to detect navigation changes +function navigateTo(url) { + if (gameRunning) { + finishGame(); + } + originalNavigateTo(url); +} export async function fetchLocalgame() { const access_token = localStorage.getItem("access_token"); @@ -159,6 +167,36 @@ export async function fetchLocalgame() { } }); + function finishGame() { + gameRunning = false; + startButton.style.display = 'block'; + alert("Local Game is aborted!"); + } + + // Listen for navigation events + const originalPushState = history.pushState; + const originalReplaceState = history.replaceState; + + history.pushState = function () { + if (gameRunning) { + finishGame(); + } + return originalPushState.apply(history, arguments); + }; + + history.replaceState = function () { + if (gameRunning) { + finishGame(); + } + return originalReplaceState.apply(history, arguments); + }; + + window.addEventListener('popstate', () => { + if (gameRunning) { + finishGame(); + } + }); + draw(); } - } +} diff --git a/frontend/src/routes/localgame/localgame.html b/frontend/src/routes/localgame/localgame.html index bc7e3cc..cd4efef 100644 --- a/frontend/src/routes/localgame/localgame.html +++ b/frontend/src/routes/localgame/localgame.html @@ -1,4 +1,3 @@ - + +
diff --git a/frontend/src/routes/otherprofile/Otherprofile.js b/frontend/src/routes/otherprofile/Otherprofile.js index a181940..1f740e0 100644 --- a/frontend/src/routes/otherprofile/Otherprofile.js +++ b/frontend/src/routes/otherprofile/Otherprofile.js @@ -1,113 +1,119 @@ - - import { navigateTo } from "../../utils/navTo.js"; -import { goPagination } from "../../utils/utils.js"; +import { userStatuses, goPagination } from "../../utils/utils.js"; import { userGetByIdUrl, matchHistoryUrl, pictureUrl } from "../../contants/contants.js"; +const access_token = localStorage.getItem("access_token"); let currentPage = 1; // Current page -const access_token = localStorage.getItem("access_token"); + export async function fetchOtherprofile() { if (!access_token) { navigateTo("/login"); return; } + try { console.log("Fetching user details"); const urlParams = new URLSearchParams(window.location.search); const id = urlParams.get('id'); - const res = await fetch(userGetByIdUrl + "?id=" + id, { + const user_status = userStatuses.includes(id) ? true : false; + const userResponse = await fetch(userGetByIdUrl + "?id=" + id, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${access_token}`, } }); - if (!res.ok) { - const errorData = await res.json(); + if (!userResponse.ok) { + const errorData = await userResponse.json(); throw new Error(errorData.error); } - const data_res = await res.json(); - const user = data_res.data[0]; - console.log("User Data:", user); + const userData = await userResponse.json(); + const user = userData.data[0]; + console.log(user); + + document.getElementById("profile-pic").src = pictureUrl + "?id=" + user.id; document.getElementById("full-name").textContent = `${user.first_name} ${user.last_name}`; document.getElementById("user-name").textContent = user.username; document.getElementById("profile-first-name").textContent = user.first_name; document.getElementById("profile-last-name").textContent = user.last_name; document.getElementById("phone").textContent = user.phone; + document.getElementById("profile-status").textContent = user_status ? "Online" : "Offline"; - - console.log("Fetching match history"); - const response = await fetch(matchHistoryUrl + `?username=${user.username}&page=${currentPage}&limit=3`, { - method: "GET", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${access_token}`, - } - }); - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error); + console.log("Fetching match history"); + const matchResponse = await fetch(`${matchHistoryUrl}?username=${user.username}&page=${currentPage}&limit=3`, { + method: "GET", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${access_token}`, } - const data = await response.json(); - let matches = data.data; - let pagination = data.pagination; - let totalPages = pagination.total_pages; - let stats = data.stats; - - - const matchTableBody = document.querySelector("#match-history-table tbody"); - matchTableBody.innerHTML = ""; + }); + if (!matchResponse.ok) { + const errorData = await matchResponse.json(); + throw new Error(errorData.error); + } + const matchData = await matchResponse.json(); + const matches = matchData.data; + const stats = matchData.stats; + const paginate_data = matchData.pagination; + const totalPages = paginate_data.total_pages; document.getElementById("profile-pic").src = pictureUrl + "?id=" + user.id; - document.getElementById('total-matches').textContent = stats.total_games; + document.getElementById('total-games').textContent = stats.total_games; document.getElementById('win-count').textContent = stats.win_count; document.getElementById('lose-count').textContent = stats.lose_count; - matches.forEach(match => { + const matchTableBody = document.querySelector("#match-history-table tbody"); + matchTableBody.innerHTML = ""; + + + + + // Render match rows + matches.forEach(match => { const date = new Date(match.date).toLocaleString(); const row = document.createElement("tr"); row.innerHTML = ` - - -
${date}
-
-
-
-
-
-
-

${match.player1}

-
+ +
${date}
+
+
+
+
+
+
+

${match.player1}

-
-
-
- ${match.player1_score}:${match.player2_score} -
+
+
+
+
+ ${match.player1_score}:${match.player2_score}
-
-
-
-
-

${match.player2}

-
+
+
+
+
+
+

${match.player2}

- - +
+ `; matchTableBody.appendChild(row); - goPagination(totalPages, currentPage, async (newPage) => { - currentPage = newPage; - fetchOtherprofile(); - }, "pagination-container"); - }); + }) + goPagination(totalPages, currentPage, async (newPage) => { + currentPage = newPage; + fetchOtherprofile(); + }, "pagination-container"); + } catch (err) { + console.log(err); + } } - diff --git a/frontend/src/routes/otherprofile/otherprofile.html b/frontend/src/routes/otherprofile/otherprofile.html index ce5869b..57e7ede 100644 --- a/frontend/src/routes/otherprofile/otherprofile.html +++ b/frontend/src/routes/otherprofile/otherprofile.html @@ -1,194 +1,194 @@ - -
-
-
-
-
-
-
- Admin -
-

John Doe

-
Dosafd
-
-
-
-
-
-
    -
  • -
    Match History
    -
  • -
  • - -
  • -
-
-
-
-
-
-
-
-
Status
-
-
- Offline -
-
-
-
-
-
First Name
-
-
- Kenneth -
-
-
-
-
-
Last Name
-
-
- Valdez -
-
-
-
-
-
Phone
-
-
- 2398169029 -
-
- -
-
-
-
-
-
-
Stats
- Total Matches -
- 0 -
- - Win Number -
- 0 -
- Lose Number -
- 0 -
-
-
-
- + +
+
+
+
+
+
+
+ Admin +
+

John Doe

+
Dosafd
+
+
+
+
+
+
    +
  • +
    Match History
    +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
Status
+
+
+ Offline +
+
+
+
+
+
First Name
+
+
+ Kenneth +
+
+
+
+
+
Last Name
+
+
+ Valdez +
+
+
+
+
+
Phone
+
+
+ 2398169029 +
+
+
+
+
+
+
+
+
Stats
+ Total Matches +
+ 0 +
+ + Win Number +
+ 0 +
+ Lose Number +
+ 0 +
+
+
+
+ diff --git a/frontend/src/routes/profile/Profile.js b/frontend/src/routes/profile/Profile.js index 62603df..a2eb4c3 100644 --- a/frontend/src/routes/profile/Profile.js +++ b/frontend/src/routes/profile/Profile.js @@ -1,11 +1,16 @@ import { navigateTo } from "../../utils/navTo.js"; -import { goPagination, CheckAuth, RefreshToken} from "../../utils/utils.js"; +import { goPagination } from "../../utils/utils.js"; import { userDetailUrl, matchHistoryUrl, pictureUrl } from "../../contants/contants.js"; let currentPage = 1; // Current page - +let total_pages = 1; export async function fetchProfile() { - + const access_token = localStorage.getItem("access_token"); + if (!access_token) { + navigateTo("/login"); + return; + } else { + try { console.log("Fetching user details"); const response_user = await fetch(userDetailUrl, { method: "GET", @@ -20,7 +25,7 @@ export async function fetchProfile() { } const data_user = await response_user.json(); - const user = data_user[0].data[0]; + const user = data_user.data[0]; console.log("User Data:", user); document.getElementById("full-name").textContent = `${user.first_name} ${user.last_name}`; @@ -28,45 +33,50 @@ export async function fetchProfile() { document.getElementById("profile-first-name").textContent = user.first_name; document.getElementById("profile-last-name").textContent = user.last_name; document.getElementById("phone").textContent = user.phone; + document.getElementById("email").textContent = user.email; if (localStorage.getItem("status")) { document.getElementById("profile-status").textContent = localStorage.getItem("status"); } console.log("Fetching match history"); - const response = await fetch(`${matchHistoryUrl}?username=${user.username}&page=${currentPage}&limit=3`, { + const matchResponse = await fetch(`${matchHistoryUrl}?username=${user.username}&page=${currentPage}&limit=3`, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${access_token}`, } }); - if (!response.ok) { - const errorData = await response.json(); + if (!matchResponse.ok) { + const errorData = await matchResponse.json(); throw new Error(errorData.error); } - const data = await response.json(); - let matches = data.data; - let pagination = data.pagination; - let totalPages = pagination.total_pages; - let stats = data.stats; + const matchData = await matchResponse.json(); + const matches = matchData.data; + const stats = matchData.stats; + const paginate_data = matchData.pagination; + + if (paginate_data) { + total_pages = paginate_data.total_pages; + } + if (stats) { + document.getElementById('total-games').textContent = stats.total_games; + document.getElementById('win-count').textContent = stats.win_count; + document.getElementById('lose-count').textContent = stats.lose_count; + } document.getElementById("profile-pic").src = pictureUrl + "?id=" + user.id; - document.getElementById('total-games').textContent = stats.total_games; - document.getElementById('win-count').textContent = stats.win_count; - document.getElementById('lose-count').textContent = stats.lose_count; - const matchTableBody = document.querySelector("#match-history-table tbody"); matchTableBody.innerHTML = ""; - matches.forEach(match => { - - const date = new Date(match.date).toLocaleString(); - const row = document.createElement("tr"); - row.innerHTML = ` - + if (matches) { + matches.forEach(match => { + if (match) { + const date = new Date(match.date).toLocaleString(); + const row = document.createElement("tr"); + row.innerHTML = `
${date}

@@ -98,12 +108,19 @@ export async function fetchProfile() {
- - `; - matchTableBody.appendChild(row); - }); - goPagination(totalPages, currentPage, async (newPage) => { - currentPage = newPage; - fetchProfile(); - }, "pagination-container"); + `; + matchTableBody.appendChild(row); + } + goPagination(total_pages, currentPage, async (newPage) => { + currentPage = newPage; + fetchProfile(); + }, "pagination-container"); + })} + + } catch (error) { + console.error(error); + + } + } } + diff --git a/frontend/src/routes/profile/profile.html b/frontend/src/routes/profile/profile.html index 373f8ef..5358f33 100644 --- a/frontend/src/routes/profile/profile.html +++ b/frontend/src/routes/profile/profile.html @@ -1,199 +1,208 @@ - -
-
-
-
-
-
-
- Admin -
-

John Doe

-
Dosafd
-
-
-
-
-
-
    -
  • -
    Match History
    -
  • -
  • - -
  • -
-
-
-
-
-
-
-
-
Status
-
-
- Offline -
-
-
-
-
-
First Name
-
-
- Kenneth -
-
-
-
-
-
Last Name
-
-
- Valdez -
-
-
-
-
-
Phone
-
-
- 2398169029 -
-
-
-
-
- -
-
-
-
-
-
-
-
-
Stats
- Total Matches -
- 0 -
- - Win Number -
- 0 -
- Lose Number -
- 0 -
-
-
-
- + +
+
+
+
+
+
+
+ Admin +
+

John Doe

+
Dosafd
+
+
+
+
+
+
    +
  • +
    Match History
    +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
Status
+
+
+ Offline +
+
+
+
+
+
First Name
+
+
+ Kenneth +
+
+
+
+
+
Last Name
+
+
+ Valdez +
+
+
+
+
+
Phone
+
+
+ 2398169029 +
+
+
+
+
+
Email
+
+
+ placeholder@gmail.com +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
Stats
+ Total Matches +
+ 0 +
+ + Win Number +
+ 0 +
+ Lose Number +
+ 0 +
+
+
+
+ diff --git a/frontend/src/routes/quickplay/quickplay.html b/frontend/src/routes/quickplay/quickplay.html index bea9f46..90fd739 100644 --- a/frontend/src/routes/quickplay/quickplay.html +++ b/frontend/src/routes/quickplay/quickplay.html @@ -12,7 +12,7 @@ html, body { height: 100%; background-color: #152733; - overflow: hidden; + overflow: auto; } .form-holder { @@ -108,7 +108,6 @@

Create Game

Enter the Username below to join a game.

-
Enter User!
diff --git a/frontend/src/routes/register/register.html b/frontend/src/routes/register/register.html index 164933b..f52b42d 100644 --- a/frontend/src/routes/register/register.html +++ b/frontend/src/routes/register/register.html @@ -1,4 +1,7 @@