Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
e331456
[update] connect mysql
uswebk Dec 30, 2022
7f31f00
[update] custom user model
uswebk Dec 30, 2022
eceee70
[update] installed jwt
uswebk Dec 30, 2022
c4a10d6
[update] requirements.txt
uswebk Dec 30, 2022
77fdbd8
[feature] api routing
uswebk Dec 30, 2022
9387c5c
[feature] account registar API
uswebk Dec 30, 2022
d0f9b0d
[feature] jwt refresh, verify
uswebk Dec 30, 2022
e8f0141
[feature] custom permission
uswebk Dec 30, 2022
9333233
[update] user list view
uswebk Dec 30, 2022
300cedb
[feature] otp
uswebk Dec 30, 2022
e2f33bb
[feature] send email about otp
uswebk Dec 30, 2022
f92edf5
[refactor] otp send mail service
uswebk Dec 31, 2022
f6217d8
[feature] otp verify view
uswebk Dec 31, 2022
402f92b
[refactor] use exception for otp verify
uswebk Dec 31, 2022
0196057
[refactor] add OTP create service
uswebk Jan 2, 2023
4a1bd26
Create README.md
uswebk Jan 2, 2023
3945241
[refactor] add requirements.txt
uswebk Jan 2, 2023
89ffb44
[refactor] use transaction
uswebk Jan 2, 2023
6639d5f
[refactor] otp verify service
uswebk Jan 3, 2023
0af666b
[refactor] delete AccountView
uswebk Jan 4, 2023
2d65be1
[feature] add SendOtp
uswebk Jan 4, 2023
48fe101
[feature] Login View
uswebk Jan 4, 2023
3784cbf
[refactor] email -> emails
uswebk Jan 5, 2023
9ad4355
[refactor] Login Service
uswebk Jan 5, 2023
f674fa6
[refactor] delete variable
uswebk Jan 5, 2023
fb235d4
[refactor] move secret key to env
uswebk Jan 6, 2023
8203b9f
[refactor] No login without email verification
uswebk Jan 6, 2023
d599384
[feature] add quiz app
uswebk Jan 8, 2023
946e99a
[feature] add quiz tag table
uswebk Jan 8, 2023
1e0bcfc
[refactor] otp verify service
uswebk Jan 8, 2023
48f490e
[refactor] rename method
uswebk Jan 8, 2023
df73d7c
[refactor] delete not use Exception
uswebk Jan 8, 2023
4b78eb2
[refactor] init account
uswebk Jan 8, 2023
857368b
[refactor] rename View
uswebk Jan 8, 2023
f41523a
[feature] tags routing
uswebk Jan 8, 2023
6e4dae5
[fix] wrong file
uswebk Jan 8, 2023
704d5dd
[fix] change from
uswebk Jan 8, 2023
21a8bc0
[feature] tag list & create view
uswebk Jan 8, 2023
5f7d5eb
[feature] tag list & create view
uswebk Jan 8, 2023
9befee7
[feature] add quiz table
uswebk Jan 8, 2023
dcb6371
[feature] add relation table of quizzes & tags
uswebk Jan 8, 2023
ddf583e
[feature] add quiz_choices table
uswebk Jan 8, 2023
04a2748
[feature] add quiz list view
uswebk Jan 8, 2023
edcf866
[update] quiz default filter
uswebk Jan 8, 2023
de65743
[refactor] delete not use
uswebk Jan 8, 2023
bfa4801
[refactor] rename viewset
uswebk Jan 8, 2023
86f95b5
[feature] quiz create in serializer
uswebk Jan 8, 2023
0b021c6
[refactor] add create choices method
uswebk Jan 8, 2023
622f993
[refactor] add create method in QuizViewSet
uswebk Jan 9, 2023
be72e8f
[feature] add validation
uswebk Jan 9, 2023
3bde9a3
[feature] add tag,quiz relation
uswebk Jan 9, 2023
5e9b46f
[feature] get tags in quiz
uswebk Jan 9, 2023
3f8518e
[style] move Meta class
uswebk Jan 9, 2023
4e1a425
[update] auth required for otp send
uswebk Jan 9, 2023
846c9d3
[update] use get_serializer
uswebk Jan 9, 2023
c070cc6
[feature] add quiz_answers table
uswebk Jan 9, 2023
caa6640
[feature] add answer endpoint
uswebk Jan 11, 2023
1a6ce49
[doc] idea setting
uswebk Jan 11, 2023
195cd37
[update] create answer logs
uswebk Jan 11, 2023
21b8a6c
[feature] create services for AnswerView
uswebk Jan 13, 2023
0657af3
[feature] add QuizService
uswebk Jan 13, 2023
42af7c8
[update] add auto_created to QuizTagModel
uswebk Jan 13, 2023
c648179
[feature] quiz logical delete
uswebk Jan 15, 2023
1c7c43c
[feature] quiz_answer_choices table
uswebk Jan 21, 2023
94170c7
[refactor] quiz migrations
uswebk Jan 21, 2023
17dd0f9
[feature] quiz answer choices create
uswebk Jan 21, 2023
3faf08c
[update] get_answer_choices relation
uswebk Feb 19, 2023
e40aa87
[feature] get profile API
uswebk Feb 19, 2023
15e321e
Bump django from 4.1.4 to 4.1.7
dependabot[bot] Mar 2, 2023
7008f8b
Bump cryptography from 38.0.4 to 39.0.1
dependabot[bot] Mar 2, 2023
b6a41b2
Bump future from 0.18.2 to 0.18.3
dependabot[bot] Mar 2, 2023
eac9fcd
Merge pull request #3 from uswebk/dependabot/pip/django-4.1.7
uswebk Mar 2, 2023
c4c6ac5
Merge pull request #4 from uswebk/dependabot/pip/cryptography-39.0.1
uswebk Mar 2, 2023
ae6dd6c
Merge pull request #5 from uswebk/dependabot/pip/future-0.18.3
uswebk Mar 2, 2023
215808b
[update] add email_verified_at in AccountSerializer
uswebk Mar 2, 2023
9e606e7
Merge branch 'develop' of github.com:uswebk/coodig into develop
uswebk Mar 2, 2023
72d222c
[update] Can get users for email pre-authentication
uswebk Mar 2, 2023
3a31dde
[feature] Create API that randomly retrieves one quiz
uswebk Mar 5, 2023
c716faf
[refactor] otp verify service
uswebk Mar 12, 2023
21575d9
[refactor] rename columns
uswebk Mar 12, 2023
d9efd59
Update README.md
uswebk Mar 15, 2023
81f1684
[update] add permission
uswebk Mar 19, 2023
1181325
Merge branch 'develop' of github.com:uswebk/coodig into develop
uswebk Mar 19, 2023
356dbdf
[feature] add get otp API
uswebk Mar 23, 2023
3fe96bf
[fix] otp response
uswebk Mar 24, 2023
8e261a5
[update] change timezone
uswebk Mar 29, 2023
bf82183
[fix] send otp permisson
uswebk Apr 2, 2023
d2021bb
[feature] password reset endpoints
uswebk Apr 3, 2023
fe4b084
[feature] password reset view
uswebk Apr 4, 2023
f66139f
[refactor] move api directory
uswebk Apr 10, 2023
8a14926
[update] for android
uswebk Apr 13, 2023
2792ec1
[refactor] change directory
uswebk Apr 15, 2023
267b24d
[update] signature uri
uswebk Apr 20, 2023
3e62161
[update] password validation
uswebk May 1, 2023
09ae2e2
[update] Change format of login error response
uswebk May 2, 2023
c31e271
[update] Record login datetime
uswebk May 2, 2023
e1b2077
[feature] Reset Password API
uswebk May 4, 2023
9c0759e
Bump sqlparse from 0.4.3 to 0.4.4
dependabot[bot] May 4, 2023
a4be426
Merge pull request #8 from uswebk/dependabot/pip/sqlparse-0.4.4
uswebk May 4, 2023
05658e9
[refactor] Reset Password time out setting
uswebk May 4, 2023
f15f68a
Merge branch 'develop' of github.com:uswebk/coodig into develop
uswebk May 4, 2023
ddfc24d
[refactor] Reset Password Service
uswebk May 4, 2023
a4198ac
[refactor] Allow login even if unauthorized
uswebk May 6, 2023
97940d2
[refactor] change functional
uswebk May 15, 2023
c5f8e78
[refactor] change static method
uswebk Jun 2, 2023
ee318fa
[update] Readme
uswebk Jun 11, 2023
d3e19d7
[update] README.md
uswebk Jun 14, 2023
e099a03
[refactor] change to static
uswebk Jun 20, 2023
6bda34f
Merge remote-tracking branch 'origin/develop' into develop
uswebk Jun 20, 2023
210c6da
[Update] README.md
uswebk Jul 9, 2023
07821b8
Update README.md
uswebk Jul 20, 2023
251da26
[update] README.md
uswebk Jul 23, 2023
885381a
[refactor] random limit
uswebk Jul 30, 2023
1e48be7
Merge branch 'develop' of github.com:uswebk/coodig into develop
uswebk Jul 30, 2023
035673a
[update] quiz limit
uswebk Jul 30, 2023
199f09d
[refactor] fetch quiz create user
uswebk Jul 30, 2023
6126d6c
[feature] add answers endpoint
uswebk Aug 6, 2023
9224a8f
[fix] is_select
uswebk Aug 6, 2023
50ed1cd
Update README.md
uswebk Aug 20, 2023
dc639b4
[fix] add index to created_at column
uswebk Aug 20, 2023
66e027d
Merge remote-tracking branch 'origin/develop' into develop
uswebk Aug 20, 2023
90490d9
[refactor] fetch random quiz
uswebk Aug 20, 2023
fe3256e
[refactor] fetch quiz when exists answer
uswebk Aug 22, 2023
2397d22
[feature] answers api
uswebk Aug 28, 2023
6f65dd5
[update] add stats
uswebk Aug 30, 2023
598692d
[update] idea
uswebk Sep 11, 2023
ea3b03f
[refactor] delete not use import
uswebk Sep 13, 2023
47b2ee5
[fix] quiz answer
uswebk Sep 17, 2023
e3db6e6
[fix] import
uswebk Sep 17, 2023
5759ad3
[refactor] serializer
uswebk Sep 18, 2023
2a087ef
[refactor] delete api
uswebk Sep 19, 2023
86ada3c
Update README.md
uswebk Sep 20, 2023
ad76892
[refactor] delete import
uswebk Sep 25, 2023
7c265cb
Merge remote-tracking branch 'origin/develop' into develop
uswebk Sep 25, 2023
fb86bd8
[refactor] delete import
uswebk Sep 25, 2023
cdd6a98
[refactor] empty authentication_classes
uswebk Sep 30, 2023
aea2bdc
[refactor] model manager
uswebk Oct 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env_example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SECRET_KEY='secret-xxxx'
EMAIL_HOST='smtp.xxxx.xxx'
EMAIL_HOST_USER='test@example.com'
EMAIL_HOST_PASSWORD='password'
URI_SECRET_KEY='Ck5C9bBqFAxVPS5Eaj55jK6GxerywfkMbxswmzCd'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.env
1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/php.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<div align="center">
<img width="350" alt="coodig-icon" src="https://github.com/uswebk/coodig-api/assets/50518919/5e0a9c1c-ddf8-4835-ad7c-fffef407bc85">
<h1>coodig</h1>

<h4>Backend(API) of quiz application for engineers🧑‍💻</h4>
</div>

---

📱 Mobile: https://github.com/uswebk/coodig-mobile

## Run App 🚗
```
python3 manage.py runserver 0.0.0.0:9999
```

## Endpoints

* **Register account**

```
POST: /api/v1/accounts/register/
```

* **Login**

```
POST: /api/v1/accounts/login/
```

* **Refresh JWT**

```
POST: /api/token/refresh/
```

* **Verify JWT**

```
POST: /api/token/verify/
```

* **Fetch Me**

```
GET: /api/v1/accounts/me/
```

* **Send OTP**

```
POST: /api/v1/accounts/otp/send/
```

* **Verify OTP**

```
POST: /api/v1/accounts/otp/verify/
```

* **Fetch OTP**

```
GET: /api/v1/accounts/otp/
```

* **Send Reset Password Email**

```
POST: /api/v1/accounts/reset-password/send/
```

* **Reset Password**

```
POST: /api/v1/accounts/reset-password/
```
Empty file added account/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions account/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
84 changes: 84 additions & 0 deletions account/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from rest_framework import serializers

from account.models import Account, Otp


class AccountRegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)

class Meta:
model = Account
fields = ['name', 'email', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}

def validate(self, attrs):
password = attrs.get('password')
password2 = attrs.get('password2')
if password != password2:
raise serializers.ValidationError("Password and Confirm Password doesn't match")
if len(password) < 6:
raise serializers.ValidationError("Password six more")
return attrs

def create(self, validate_data):
return Account.objects.create_user(**validate_data)


class UserLoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255)
password = serializers.CharField()

class Meta:
model = Account
fields = ['email', 'password']

def validate_password(self, value):
if len(value) < 6:
raise serializers.ValidationError("Passwords must be at least 6 characters long.")
return value


class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'name', 'email', 'email_verified_at']


class OtpSerializer(serializers.ModelSerializer):
account = AccountSerializer(read_only=True)

class Meta:
model = Otp
fields = '__all__'


class VerifyAccountSerializer(serializers.Serializer): # noqa
otp = serializers.CharField()


class SendPasswordResetEmailSerializer(serializers.Serializer):
email = serializers.EmailField(max_length=255)

class Meta:
fields = ['email']


class PasswordResetSerializer(serializers.Serializer):
password = serializers.CharField(max_length=255, style={'input_type': 'password'})
password2 = serializers.CharField(max_length=255, style={'input_type': 'password'}, write_only=True)
uid = serializers.CharField()
token = serializers.CharField()

class Meta:
fields = ['password', 'password2', 'uid']

def validate(self, attrs):
password = attrs.get('password')
password2 = attrs.get('password2')
if password != password2:
raise serializers.ValidationError("Password and Confirm Password doesn't match")
if len(password) < 6:
raise serializers.ValidationError("Password six more")
return attrs
15 changes: 15 additions & 0 deletions account/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.urls import path

from .views import RegistrationView, VerifyOtpView, SendOtpView, UserLoginView, MeView, OtpView, SendResetPasswordView, \
ResetPasswordView

urlpatterns = [
path('me/', MeView.as_view(), name='account'),
path('register/', RegistrationView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('otp/', OtpView.as_view(), name='otp'),
path('otp/verify/', VerifyOtpView.as_view(), name='verify_otp'),
path('otp/send/', SendOtpView.as_view(), name='send_otp'),
path('reset-password/send/', SendResetPasswordView.as_view(), name='send_reset_password'),
path('reset-password/', ResetPasswordView.as_view(), name='reset_password'),
]
126 changes: 126 additions & 0 deletions account/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from django.core.exceptions import ValidationError
from django.db import transaction
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from account.api.serializers import AccountRegistrationSerializer, VerifyAccountSerializer, UserLoginSerializer, \
AccountSerializer, OtpSerializer, SendPasswordResetEmailSerializer, PasswordResetSerializer
from account.emails import send_opt
from account.exceptions import OtpVerifyError, LoginError
from account.models import Account
from account.permissions import ActiveAccount
from account.services import LoginService, OtpService, OtpVerifyService, get_tokens_for_user, SendResetPasswordService, \
ResetPasswordService
from rest_framework.authentication import BasicAuthentication


class RegistrationView(APIView):
authentication_classes = []
permission_classes = [permissions.AllowAny, ]
serializer_class = AccountRegistrationSerializer

def post(self, request):
with transaction.atomic():
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
account = serializer.save()
otp = OtpService().create(account)
send_opt(otp)
return Response({'token': get_tokens_for_user(account)}, status=status.HTTP_201_CREATED)


class UserLoginView(APIView):
permission_classes = [permissions.AllowAny, ]
serializer_class = UserLoginSerializer

def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
try:
token = LoginService().login(serializer.data['email'], serializer.data['password'])
return Response({'token': token, 'message': 'Login Success'}, status=status.HTTP_200_OK)
except LoginError as e:
return Response({'non_field_errors': ['Email or Password is not Valid']},
status=status.HTTP_404_NOT_FOUND)


class VerifyOtpView(APIView):
permission_classes = [permissions.IsAuthenticated, ActiveAccount]
serializer_class = VerifyAccountSerializer

def post(self, request):
serializer = self.serializer_class(data=request.data)
account = self.request.user
if serializer.is_valid(raise_exception=True):
try:
OtpVerifyService().done(account, serializer.data['otp'])
return Response({"messages": "otp verify success"}, status=status.HTTP_200_OK)
except OtpVerifyError as e:
return Response({"messages": str(e)}, status=status.HTTP_400_BAD_REQUEST)


class OtpView(APIView):
permission_classes = [permissions.IsAuthenticated, ActiveAccount]
serializer_class = OtpSerializer

def get(self, request):
account = self.request.user
otps = account.otps
if otps is None:
return Response({'message': 'Not Found Otp'}, status=status.HTTP_404_NOT_FOUND)

return Response(self.serializer_class(otps.last()).data, status=status.HTTP_200_OK)


class SendOtpView(APIView):
permission_classes = [permissions.IsAuthenticated, ActiveAccount]

def post(self, request):
account = self.request.user
otp = OtpService().create(account)
send_opt(otp)
return Response({'message': 'Send otp success'}, status=status.HTTP_200_OK)


class MeView(APIView):
permission_classes = [permissions.IsAuthenticated, ActiveAccount]

def get(self, request):
account = self.request.user
return Response(AccountSerializer(instance=account).data, status=status.HTTP_200_OK)


class SendResetPasswordView(APIView):
permission_classes = [permissions.AllowAny, ]
serializer_class = SendPasswordResetEmailSerializer

def post(self, request):
serializer = self.serializer_class(data=request.data)

if serializer.is_valid(raise_exception=True):
account = Account.objects.filter(email=serializer.data['email']).first()
if account is not None:
SendResetPasswordService.execute(account)

return Response({'message': 'Send Reset Password Mail'}, status=status.HTTP_200_OK)


class ResetPasswordView(APIView):
permission_classes = [permissions.AllowAny, ]
serializers_class = PasswordResetSerializer

def post(self, request):
serializer = self.serializers_class(data=request.data)

if serializer.is_valid(raise_exception=True):
try:
ResetPasswordService.execute(
serializer.data['uid'],
serializer.data['token'],
serializer.data['password']
)
return Response({}, status=status.HTTP_200_OK)

except ValidationError as e:
return Response({'message': {e.messages[0]}}, status=status.HTTP_400_BAD_REQUEST)
6 changes: 6 additions & 0 deletions account/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AccountConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'account'
2 changes: 2 additions & 0 deletions account/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
OTP_VALID_MINUTES = 10
OTP_CODE_NUMBER_OF_DIGITS = 6
Loading