From d095057e9b8f94264018066735c2106c77588bbe Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sat, 5 Jun 2021 22:11:51 +0800 Subject: [PATCH 01/26] add: readme.md --- readme.md | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..bd5175f --- /dev/null +++ b/readme.md @@ -0,0 +1,195 @@ +# Saboteur Online Game Website + +It's an online game website created by the well-known board game Saboteur. All game rules are according to the original version. Besides, the website supports RWD (Responsive Web Design), so you can play on any mobile device. If you want to play, you can directly navigate to https://saboteur.ooad.tk/, or we also provide a docker version that can deploy on the local server. + +## Installation + +### Docker-compose version (Suggest) + +Export environment variable or add `.env` at repo root directory. + +```bash +export SECRET_KEY='PUT_YOUR_SECRET_KEY_HERE' +export POSTGRES_PASSWORD='SET_YOUR_OWN_DATABASE_PASSWORD_HERE' +``` + +or + +```bash +# .env +SECRET_KEY=PUT_YOUR_SECRET_KEY_HERE +POSTGRES_PASSWORD=SET_YOUR_OWN_DATABASE_PASSWORD_HERE +``` + +Please prepare the [docker-compose]() and [docker]() environment first. + + ```bash + docker-compose build --no-cache + ``` + +### No-NGINX localhost version (For development) + +Use the node.js package manager yarn to install react.js and other frontend requirements. + +```bash +# if you don't have installed yarn install it first +brew install yarn +# or +npm install yarn --g + +cd mysite/frontend/ +yarn install +``` + +Use the package manager [pip](https://pip.pypa.io/en/stable/) to install Django and other backend requirements. + +```bash +cd mysite/ +pip install -r requirements.txt +``` + +Add `local_settings.py` in `mysite/mysite/settings/` + +```python +# mysite/mysite/settings/local_settings.py +import os +os.environ['SECRET_KEY'] = 'PUT_YOUR_SECRET_KEY_HERE' +os.environ['POSTGRES_PASSWORD'] = 'PUT_YOUR_DATABASE_PASSWORD_HERE' + +from .base import * + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] +CORS_ORIGIN_ALLOW_ALL = True +``` + +Becasue of Django need database, so you need to prepare database and redis in your envrionment, you can just start docker-compose redis and Postgres service. + +```ba +docker-compose up -d db redis +``` + +Or if you have own database and redis server, you need to add your setting in Django settings file to connect you database servers. + +```python +# mysite/mysite/settings/local_settings.py + +# Channels +ASGI_APPLICATION = 'mysite.asgi.application' +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + "hosts": [('redis', 6379)], # edit your redis service host and port here + }, + }, +} + +# Database +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases +# Edit following field to fit your database setting +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'postgres', + 'USER': 'postgres', + 'PASSWORD': str(os.environ['POSTGRES_PASSWORD']), + 'HOST': 'db', + 'PORT': 5432, + } +} +``` + +## Usage + +### Docker-compose version (Suggest) + +#### Start server + +Start docker-compose services up, then open your browser to view website http://localhost/. + +```bash +docker-compose up -d +``` + +### No-NGINX localhost version (For development) + +Start react and django server then open your browser to view website http://localhost:3000/. + +#### Start Django server + +```bash +cd mysite/ + +# first run need to initial database +python manage.py makemigrations --settings=mysite.settings.local_settings +python manage.py migrate --settings=mysite.settings.local_settings + +# runserver +python manage.py runserver --settings=mysite.settings.local_settings +``` + +#### Start React server + +```bash +cd mysite/frontend/ + +yarn start +``` + +## Content of this repo + +```tree +├── docker-compose.ci.yml # docker-compose github action setting +├── docker-compose.prod.yml # docker-compose production setting +├── docker-compose.yml # docker-compose localhost setting +├── mysite/ # Django project (backend) files +│ ├── Dockerfile # Docker general setting +│ ├── Dockerfile.ci # Docker github action setting +│ ├── Dockerfile.prod # Docker production setting +│ ├── authentication/ # account django app files +│ ├── frontend/ # react project (frontend) files +│ ├── game/ # game django app files +│ ├── manage.py # django manage execution file +│ ├── mysite/ # django project and setting files +│ ├── requirements.txt # backend packages requirements +│ └── saboteur/ # saboteur package (GameController, Card, Player...) +└── nginx/ # nginx files + ├── Dockerfile # Docker general setting + ├── Dockerfile.prod # Docker production setting + ├── my_nginx.conf # nginx production configuration + ├── my_nginx_local.conf # nginx localhost configuration + └── nginx.conf # nginx configuration +``` + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. + +Please make sure to update tests as appropriate. + +## Authors and acknowledgment + +This repository is a team project work in OOAD class teached by [Prof. Chun-Han Lin](https://sites.google.com/site/aaronchlin/home_e?authuser=0). + +### Course info + ++ National Taiwan Normal University (NTNU)(國立臺灣師範大學) ++ Department of Computer Science and Information Engineering (CSIE) (資訊工程學系) ++ Course Name: Object-Oriented Analysis and Design (OOAD) (物件導向分析與設計) ++ Course Semester: 2021 Spring ++ Instructor: Chun-Han Lin (林均翰) ++ [More course information](http://courseap.itc.ntnu.edu.tw/acadmOpenCourse/SyllabusCtrl?year=109&term=2&courseCode=CSC0005&courseGroup=&deptCode=SU47&formS=&classes1=9&deptGroup=) + +### Team members + ++ [Jeffery Ho](https://github.com/chiachun2491) ++ [DannyLeee](https://github.com/DannyLeee) ++ [Striper](https://github.com/justbuyyal) ++ [YuKai Lee](https://github.com/leeyk0501) ++ [Cayden-981102](https://github.com/Cayden-981102) + +## License +[MIT](https://choosealicense.com/licenses/mit/) \ No newline at end of file From bf1acd85590b95a8eb6e88bdf8e52bb0a828f211 Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sat, 5 Jun 2021 22:30:13 +0800 Subject: [PATCH 02/26] fix(frontend): add default environment variables --- mysite/frontend/src/api/Api.js | 4 ++-- mysite/frontend/src/pages/Game.js | 2 +- mysite/frontend/src/pages/Lobby.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysite/frontend/src/api/Api.js b/mysite/frontend/src/api/Api.js index 0cdd78d..a9cc143 100644 --- a/mysite/frontend/src/api/Api.js +++ b/mysite/frontend/src/api/Api.js @@ -3,9 +3,9 @@ import axios from 'axios'; let APIbaseURL; if (process.env.NODE_ENV === 'production') { - APIbaseURL = window.location.origin + /api/; + APIbaseURL = window.location.origin + '/api/'; } else { - APIbaseURL = process.env.REACT_APP_API_URL; + APIbaseURL = process.env.REACT_APP_API_URL || 'http://localhost:8000/api/'; } const axiosInstance = axios.create({ diff --git a/mysite/frontend/src/pages/Game.js b/mysite/frontend/src/pages/Game.js index f45f113..5fd667d 100644 --- a/mysite/frontend/src/pages/Game.js +++ b/mysite/frontend/src/pages/Game.js @@ -13,7 +13,7 @@ let wsBaseURL; if (process.env.NODE_ENV === 'production') { wsBaseURL = window.location.host; } else { - wsBaseURL = process.env.REACT_APP_WS_URL; + wsBaseURL = process.env.REACT_APP_WS_URL || 'localhost:8000'; } const RoomStatus = { diff --git a/mysite/frontend/src/pages/Lobby.js b/mysite/frontend/src/pages/Lobby.js index 5d89fde..20e4a28 100644 --- a/mysite/frontend/src/pages/Lobby.js +++ b/mysite/frontend/src/pages/Lobby.js @@ -11,7 +11,7 @@ let wsBaseURL; if (process.env.NODE_ENV === 'production') { wsBaseURL = window.location.host; } else { - wsBaseURL = process.env.REACT_APP_WS_URL; + wsBaseURL = process.env.REACT_APP_WS_URL || 'localhost:8000'; } class Lobby extends Component { From a016167e9adf120dc50bef8dc0eaecb6479d847a Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sat, 5 Jun 2021 22:32:08 +0800 Subject: [PATCH 03/26] fix: add allow host in django production setting --- mysite/mysite/settings/production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysite/mysite/settings/production.py b/mysite/mysite/settings/production.py index 266641f..cf3acc6 100644 --- a/mysite/mysite/settings/production.py +++ b/mysite/mysite/settings/production.py @@ -4,4 +4,4 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False -ALLOWED_HOSTS = ['saboteur.ooad.tk', ] \ No newline at end of file +ALLOWED_HOSTS = ['saboteur.ooad.tk', 'localhost'] From cf0e6827fc4bd5629368d04e78799ee013136871 Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sat, 5 Jun 2021 22:37:58 +0800 Subject: [PATCH 04/26] fix(backend): fix account can't delete because of outstanding token delete permission --- mysite/authentication/admin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mysite/authentication/admin.py b/mysite/authentication/admin.py index 026a13c..8fea190 100644 --- a/mysite/authentication/admin.py +++ b/mysite/authentication/admin.py @@ -1,4 +1,7 @@ from django.contrib import admin +from rest_framework_simplejwt.token_blacklist.admin import OutstandingTokenAdmin +from rest_framework_simplejwt.token_blacklist import models + from .models import CustomUser @@ -6,4 +9,12 @@ class CustomUserAdmin(admin.ModelAdmin): model = CustomUser +class NewOutstandingTokenAdmin(OutstandingTokenAdmin): + + def has_delete_permission(self, *args, **kwargs): + return True + + +admin.site.unregister(models.OutstandingToken) +admin.site.register(models.OutstandingToken, NewOutstandingTokenAdmin) admin.site.register(CustomUser, CustomUserAdmin) From fb38345ad5147b9cde3e526104eb698e010ab60d Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sat, 5 Jun 2021 22:42:44 +0800 Subject: [PATCH 05/26] fix(frontend): fix Game page title room name undefined --- mysite/frontend/src/pages/Game.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysite/frontend/src/pages/Game.js b/mysite/frontend/src/pages/Game.js index 5fd667d..5b3e092 100644 --- a/mysite/frontend/src/pages/Game.js +++ b/mysite/frontend/src/pages/Game.js @@ -196,7 +196,7 @@ class Game extends Component { return ( <> - {`${title}:${this.props.roomName}`} + {`${title}:${this.state.roomName}`}
From 9d913bb84c1adeabb9b1de33770ab783bd9cdd6a Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sun, 6 Jun 2021 00:07:27 +0800 Subject: [PATCH 06/26] fix(frontend): fix bystander can view one of player handcards --- .../src/components/game/GamePlaying.js | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/mysite/frontend/src/components/game/GamePlaying.js b/mysite/frontend/src/components/game/GamePlaying.js index 89815d5..61c9b81 100644 --- a/mysite/frontend/src/components/game/GamePlaying.js +++ b/mysite/frontend/src/components/game/GamePlaying.js @@ -136,7 +136,7 @@ class GamePlaying extends Component { const popover = this.props.popover !== undefined ? this.props.popover : null; // set self_id - let self_id = 0; + let self_id = -1; const list_len = gameData.player_list.length; for (let i = 0; i < list_len; i++) { if (gameData.player_list[i].id === username) { @@ -147,8 +147,8 @@ class GamePlaying extends Component { // set other players components let other_players = []; - for (let i = 1; i < list_len; i++) { - let playerIndex = (self_id + i) % list_len; + for (let i = self_id === -1 ? 0 : 1; i < list_len; i++) { + let playerIndex = ((self_id === -1 ? 0 : self_id) + i) % list_len; let player = gameData.player_list[playerIndex]; other_players.push( { + msg = v.msg; + a[msg] = a[msg] ? { v: v, count: a[msg].count + 1 } : { v: v, count: 1 }; + return a; + }, {}) + ).reduce((a, v) => (v[1].count >= a[1].count ? v : a), [null, { count: 0 }])[1].v; + } } let variant; if (alertMessage) { @@ -228,17 +238,19 @@ class GamePlaying extends Component { {/* Rival name & status */} {other_players} - + {self_id !== -1 ? ( + + ) : null} From 4c17caff6a30bc0dd9dd9f6ff2f72e5606e1e1d0 Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sun, 6 Jun 2021 00:18:34 +0800 Subject: [PATCH 07/26] refactor(frontend): delete console log for debug --- mysite/frontend/src/api/Api.js | 1 - mysite/frontend/src/components/Logout.js | 3 +-- .../frontend/src/components/errors/NetworkError.js | 3 +-- mysite/frontend/src/components/game/GamePlaying.js | 3 +-- mysite/frontend/src/pages/Game.js | 12 ++++-------- mysite/frontend/src/pages/GameHistory.js | 2 -- mysite/frontend/src/pages/Lobby.js | 13 ++++--------- mysite/frontend/src/pages/Login.js | 5 ++--- mysite/frontend/src/pages/Signup.js | 5 ++--- mysite/frontend/src/pages/Tutorial.js | 1 - 10 files changed, 15 insertions(+), 33 deletions(-) diff --git a/mysite/frontend/src/api/Api.js b/mysite/frontend/src/api/Api.js index a9cc143..58ddffd 100644 --- a/mysite/frontend/src/api/Api.js +++ b/mysite/frontend/src/api/Api.js @@ -46,7 +46,6 @@ axiosInstance.interceptors.response.use( // exp date in token is expressed in seconds, while now() returns milliseconds: const now = Math.ceil(Date.now() / 1000); - console.log(tokenParts.exp); if (tokenParts.exp > now) { return axiosInstance diff --git a/mysite/frontend/src/components/Logout.js b/mysite/frontend/src/components/Logout.js index eb1be63..79d7b35 100644 --- a/mysite/frontend/src/components/Logout.js +++ b/mysite/frontend/src/components/Logout.js @@ -11,11 +11,10 @@ function handleLogout() { localStorage.removeItem('refresh_token'); localStorage.removeItem('username'); axiosInstance.defaults.headers['Authorization'] = null; - console.log('Logout successful'); window.location.href = '/'; }); } catch (e) { - console.log(e); + console.error(e); } } diff --git a/mysite/frontend/src/components/errors/NetworkError.js b/mysite/frontend/src/components/errors/NetworkError.js index ee5e9e8..0196172 100644 --- a/mysite/frontend/src/components/errors/NetworkError.js +++ b/mysite/frontend/src/components/errors/NetworkError.js @@ -53,11 +53,10 @@ class NetworkError extends Component { axiosInstance .get('/auth/hello/') .then((response) => { - console.log('connect server successfully', response); window.location.href = this.state.redirectURL; }) .catch((err) => { - console.log('connect error', err); + console.error('connect error', err); this.countDownMsgSet(newTimeout); }); } diff --git a/mysite/frontend/src/components/game/GamePlaying.js b/mysite/frontend/src/components/game/GamePlaying.js index 61c9b81..80b86b4 100644 --- a/mysite/frontend/src/components/game/GamePlaying.js +++ b/mysite/frontend/src/components/game/GamePlaying.js @@ -55,7 +55,6 @@ class GamePlaying extends Component { const username = localStorage.getItem('username') ? localStorage.getItem('username') : '你'; if (this.props.roomData.game_data.now_play === username) { if (this.state.selectHandCardNo !== -1) { - console.log(this.state.selectHandCardNo, this.state.selectHandCardRotate, pos, action); if (this.cardIsMulti(this.state.selectHandCardNo) && action === -1 && pos >= BOARD_BASE) { this.setState( { @@ -110,7 +109,7 @@ class GamePlaying extends Component { }) ); } catch (e) { - console.log(e); + console.error(e); } // reset select card this.setState({ diff --git a/mysite/frontend/src/pages/Game.js b/mysite/frontend/src/pages/Game.js index 5b3e092..9e05f26 100644 --- a/mysite/frontend/src/pages/Game.js +++ b/mysite/frontend/src/pages/Game.js @@ -41,7 +41,6 @@ class Game extends Component { axiosInstance .get('/game/' + roomName + '/') .then((response) => { - console.log(response.data); this.setState({ roomData: response.data }); this.connectSocket(roomName); }) @@ -60,11 +59,9 @@ class Game extends Component { // check token valid first axiosInstance .get('/auth/hello/') - .then((response) => { - console.log('obtain/refresh token successfully', response); - }) + .then((response) => {}) .catch((err) => { - console.log(err); + console.error(err); }); const token = localStorage.getItem('access_token'); @@ -74,7 +71,6 @@ class Game extends Component { ws.onopen = () => { // on connecting, do nothing but log it to the console - console.log('connected'); this.setState({ socketErrorMessage: null }); this.setState({ ws: ws }); @@ -102,7 +98,7 @@ class Game extends Component { window.location.href = `/games/${message.room_id}/`; break; default: - console.log(message); + console.error('This event did not handled', message); break; } }; @@ -126,7 +122,7 @@ class Game extends Component { countDownMsgSet = (timeout) => { const close_msg = `連線已中斷,我們將在 ${timeout / 1000} 秒後重新連線`; - console.log(close_msg); + console.error(close_msg); this.setState( { socketErrorMessage: { diff --git a/mysite/frontend/src/pages/GameHistory.js b/mysite/frontend/src/pages/GameHistory.js index c9c3c95..59f17b2 100644 --- a/mysite/frontend/src/pages/GameHistory.js +++ b/mysite/frontend/src/pages/GameHistory.js @@ -19,10 +19,8 @@ class GameHistory extends Component { axiosInstance .get('game/history/') .then((response) => { - // console.log(response); let roomList = response.data; this.setState({ roomList: roomList, loaded: true }); - console.log(roomList); }) .catch((err) => { console.error(err); diff --git a/mysite/frontend/src/pages/Lobby.js b/mysite/frontend/src/pages/Lobby.js index 20e4a28..554c68b 100644 --- a/mysite/frontend/src/pages/Lobby.js +++ b/mysite/frontend/src/pages/Lobby.js @@ -34,7 +34,6 @@ class Lobby extends Component { axiosInstance .get('game/room_list/') .then((response) => { - // console.log(response); this.setState({ roomList: response.data, loaded: true }); // TODO: Websocket connect to fetch new room status @@ -50,11 +49,9 @@ class Lobby extends Component { // check token valid first axiosInstance .get('/auth/hello/') - .then((response) => { - console.log('obtain/refresh token successfully', response); - }) + .then((response) => {}) .catch((err) => { - console.log(err); + console.error(err); }); const token = localStorage.getItem('access_token'); @@ -64,7 +61,6 @@ class Lobby extends Component { ws.onopen = () => { // on connecting, do nothing but log it to the console - console.log('connected'); this.setState({ socketErrorMessage: null }); this.setState({ ws: ws }); @@ -93,7 +89,7 @@ class Lobby extends Component { newRoomList.splice(roomIndex); break; default: - console.log(message); + console.error('This event did not handled', message); break; } this.setState({ roomList: newRoomList }); @@ -118,7 +114,7 @@ class Lobby extends Component { countDownMsgSet = (timeout) => { const close_msg = `Socket is closed. Reconnect will be attempted in ${timeout / 1000} second.`; - console.log(close_msg); + console.error(close_msg); this.setState({ socketErrorMessage: close_msg }, () => { if (timeout - 1000 < 0) { this.setState({ socketErrorMessage: 'Reconnecting...' }); @@ -139,7 +135,6 @@ class Lobby extends Component { axiosInstance .post('/game/room_create/') .then((response) => { - // console.log(response.data); window.location.href = '/games/' + response.data.permanent_url + '/'; }) .catch((err) => { diff --git a/mysite/frontend/src/pages/Login.js b/mysite/frontend/src/pages/Login.js index b46dcbd..f945ba5 100644 --- a/mysite/frontend/src/pages/Login.js +++ b/mysite/frontend/src/pages/Login.js @@ -39,7 +39,6 @@ class Login extends Component { password: this.state.password, }) .then((response) => { - console.log(response.data); axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access; localStorage.setItem('access_token', response.data.access); localStorage.setItem('refresh_token', response.data.refresh); @@ -51,11 +50,11 @@ class Login extends Component { } }) .catch((err) => { - console.log(err.response); + console.error(err.response); this.setState({ errors: err.response.data, }); - console.log(this.state.errors); + console.error(this.state.errors); }); } diff --git a/mysite/frontend/src/pages/Signup.js b/mysite/frontend/src/pages/Signup.js index b5b8b50..4a552ab 100644 --- a/mysite/frontend/src/pages/Signup.js +++ b/mysite/frontend/src/pages/Signup.js @@ -32,15 +32,14 @@ class Signup extends Component { email: this.state.email, }) .then((response) => { - console.log(response.data); window.location.href = '/account/login/'; }) .catch((err) => { - console.log(err.response); + console.error(err.response); this.setState({ errors: err.response.data, }); - console.log(this.state.errors); + console.error(this.state.errors); }); } diff --git a/mysite/frontend/src/pages/Tutorial.js b/mysite/frontend/src/pages/Tutorial.js index a62b013..527979c 100644 --- a/mysite/frontend/src/pages/Tutorial.js +++ b/mysite/frontend/src/pages/Tutorial.js @@ -22,7 +22,6 @@ class Tutorial extends Component { let newState = this.state; let gameData = newState.roomData.game_data; const username = localStorage.getItem('username') ? localStorage.getItem('username') : '你'; - // console.log(newState.nowStep, cardNo, rotate, pos, action, button); if (newState.nowStep === TutorialStep.INTRO && button === true) { newState.nowStep = TutorialStep.SELECT_ROAD_AND_ROTATE; } else if (newState.nowStep === TutorialStep.SELECT_ROAD_AND_ROTATE && cardNo === 9 && rotate === true) { From d9becf8b7adf738ff919ea7f893dac10d2197d28 Mon Sep 17 00:00:00 2001 From: Jeffery Ho Date: Sun, 6 Jun 2021 00:59:02 +0800 Subject: [PATCH 08/26] fix(auth): add username field to jwt token and get username by parse token resolve #46 --- mysite/authentication/serializers.py | 4 +-- .../frontend/src/components/game/GameEnd.js | 3 +- .../src/components/game/GameOrganize.js | 2 +- .../src/components/game/GamePlaying.js | 4 +-- mysite/frontend/src/containers/Layout.js | 9 +++--- mysite/frontend/src/pages/Game.js | 29 +++++++++---------- mysite/frontend/src/pages/GameHistory.js | 3 +- mysite/frontend/src/pages/Lobby.js | 3 +- mysite/frontend/src/pages/Login.js | 5 ++-- mysite/frontend/src/pages/Signup.js | 4 +-- mysite/frontend/src/pages/Tutorial.js | 10 ++++--- mysite/frontend/src/utils/getUserName.js | 10 +++++++ 12 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 mysite/frontend/src/utils/getUserName.js diff --git a/mysite/authentication/serializers.py b/mysite/authentication/serializers.py index f6d9b0b..a18449e 100644 --- a/mysite/authentication/serializers.py +++ b/mysite/authentication/serializers.py @@ -6,11 +6,11 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer): @classmethod - def get_token(cls, user): + def get_token(cls, user: CustomUser): token = super(MyTokenObtainPairSerializer, cls).get_token(user) # Add custom claims - token['color'] = user.color + token['username'] = user.username return token diff --git a/mysite/frontend/src/components/game/GameEnd.js b/mysite/frontend/src/components/game/GameEnd.js index 1b9f7fc..2da760a 100644 --- a/mysite/frontend/src/components/game/GameEnd.js +++ b/mysite/frontend/src/components/game/GameEnd.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { Button, Card, Col, Row } from 'react-bootstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUserCog, faUser, faCrown } from '@fortawesome/free-solid-svg-icons'; +import getUserName from '../../utils/getUserName'; class GameEnd extends Component { constructor(props) { @@ -19,7 +20,7 @@ class GameEnd extends Component { } render() { - const username = localStorage.getItem('username'); + const username = this.props.username; const adminName = this.props.roomData.admin; const admin = adminName === username; diff --git a/mysite/frontend/src/components/game/GameOrganize.js b/mysite/frontend/src/components/game/GameOrganize.js index c55efca..3342220 100644 --- a/mysite/frontend/src/components/game/GameOrganize.js +++ b/mysite/frontend/src/components/game/GameOrganize.js @@ -52,7 +52,7 @@ class GameOrganize extends Component { } render() { - const username = localStorage.getItem('username'); + const username = this.props.username; const adminName = this.props.roomData.admin; const admin = adminName === username; diff --git a/mysite/frontend/src/components/game/GamePlaying.js b/mysite/frontend/src/components/game/GamePlaying.js index 80b86b4..ca5dfb5 100644 --- a/mysite/frontend/src/components/game/GamePlaying.js +++ b/mysite/frontend/src/components/game/GamePlaying.js @@ -52,7 +52,7 @@ class GamePlaying extends Component { } actionIsLegal(pos, action) { - const username = localStorage.getItem('username') ? localStorage.getItem('username') : '你'; + const username = this.props.username || '你'; if (this.props.roomData.game_data.now_play === username) { if (this.state.selectHandCardNo !== -1) { if (this.cardIsMulti(this.state.selectHandCardNo) && action === -1 && pos >= BOARD_BASE) { @@ -120,7 +120,7 @@ class GamePlaying extends Component { } render() { - const username = localStorage.getItem('username') ? localStorage.getItem('username') : '你'; + const username = this.props.username || '你'; const gameData = this.props.roomData.game_data; const nowStep = this.props.nowStep !== undefined diff --git a/mysite/frontend/src/containers/Layout.js b/mysite/frontend/src/containers/Layout.js index de96d35..062e347 100644 --- a/mysite/frontend/src/containers/Layout.js +++ b/mysite/frontend/src/containers/Layout.js @@ -1,10 +1,11 @@ import { Container, Nav, Navbar, Button } from 'react-bootstrap'; import { withRouter } from 'react-router-dom'; import handleLogout from '../components/Logout'; +import getUserName from '../utils/getUserName'; const Header = (props) => { const { location } = props; - const username = localStorage.getItem('username'); + const username = getUserName(); return ( @@ -13,13 +14,13 @@ const Header = (props) => {