Skip to content

Commit ff28a21

Browse files
committed
add email verify API
1 parent 630dfe1 commit ff28a21

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed

account/serializers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class Meta:
2020
"is_superuser",
2121
"date_joined",
2222
"is_active",
23-
"last_login"
23+
"last_login",
24+
"email_verified"
2425
]
25-
read_only_fields = ["id", "solved", "submit_time", "date_joined", "last_login"]
26+
read_only_fields = ["id", "solved", "submit_time", "date_joined", "last_login", "email", "email_verified"]
2627

account/views.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from django.conf import settings
99
from django.utils import timezone
1010

11+
from datetime import timedelta
12+
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
13+
1114
from rest_framework.response import Response
1215
from rest_framework.views import APIView
1316
from rest_framework import status
@@ -20,6 +23,7 @@
2023
from .decorator import password_verify_required
2124

2225
import os.path
26+
import secrets
2327

2428
# Create your views here.
2529
class AccountSessionView(APIView):
@@ -213,3 +217,64 @@ def delete(self, request, uid):
213217

214218
os.remove(avatar_path)
215219
return Response(status=status.HTTP_200_OK)
220+
221+
class AccountEmailView(APIView):
222+
223+
@method_decorator(login_required())
224+
def get(self, request):
225+
return Response({
226+
"res": request.user.email
227+
})
228+
229+
# @method_decorator(login_required())
230+
def post(self, request, vid=None):
231+
signer = TimestampSigner()
232+
user = request.user
233+
if vid == None:
234+
# send mail
235+
signature = signer.sign(user.username)
236+
user.email_user(settings.VERIFY_EMAIL_TEMPLATE_TITLE,
237+
settings.VERIFY_EMAIL_TEMPLATE_CONTENT.format(username=user.username, signature=signature),
238+
html_message=settings.VERIFY_EMAIL_TEMPLATE_CONTENT.format(username=user.username, signature=signature),)
239+
return Response({
240+
"detail": "Email sent"
241+
}, status=status.HTTP_202_ACCEPTED)
242+
243+
try:
244+
value = signer.unsign(vid, max_age=timedelta(minutes=settings.VERIFY_EMAIL_MAX_AGE))
245+
except SignatureExpired:
246+
return Response({
247+
"detail": "Signature Expired"
248+
}, status=status.HTTP_403_FORBIDDEN)
249+
except BadSignature:
250+
return Response({
251+
"detail": "Bad Signature"
252+
}, status=status.HTTP_403_FORBIDDEN)
253+
254+
if value != user.username:
255+
return Response({
256+
"detail": "Mismatch Signature"
257+
}, status=status.HTTP_403_FORBIDDEN)
258+
259+
user.email_verified = True
260+
user.save()
261+
request.session["email_verified"] = True
262+
return Response({
263+
"detail": "Susccess"
264+
}, status=status.HTTP_204_NO_CONTENT)
265+
266+
@method_decorator(syllable_required("email", str))
267+
@method_decorator(password_verify_required())
268+
def patch(self, request):
269+
# change email
270+
271+
data = request.data
272+
user = request.user
273+
274+
user.email = data.get("email")
275+
user.email_verified = False
276+
user.save()
277+
278+
return Response({
279+
"detail": "Success"
280+
}, status.HTTP_204_NO_CONTENT)

segmentoj/settings.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,12 @@
160160
EMAIL_PORT = int(os.environ.get("BACKEND_EMAIL_PORT")) if os.environ.get("BACKEND_EMAIL_PORT") else 25
161161
EMAIL_HOST_USER = os.environ.get("BACKEND_EMAIL_USERNAME")
162162
EMAIL_HOST_PASSWORD = os.environ.get("BACKEND_EMAIL_PASSWORD")
163-
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
163+
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
164+
165+
VERIFY_EMAIL_TEMPLATE_TITLE = "[SegmentOJ] Email Verify"
166+
VERIFY_EMAIL_TEMPLATE_CONTENT = """Hi, {username}<br/>
167+
It seems that you have just requested an email verify!<br/>
168+
<strong>You code is:</strong> <code>{signature}</code><br/>
169+
Please use it in 0 minutes.<br/>
170+
"""
171+
VERIFY_EMAIL_MAX_AGE = 0

segmentoj/urls.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@
2121
from django.conf import settings
2222

2323
import problem.views
24-
from account.views import AccountView, AccountSessionView, AccountUsernameAccessibilityView, AccountAvatarView, AccountPasswordView
24+
from account.views import (
25+
AccountView,
26+
AccountSessionView,
27+
AccountUsernameAccessibilityView,
28+
AccountAvatarView,
29+
AccountPasswordView,
30+
AccountEmailView
31+
)
2532
from status.views import StatusView, StatusListCountView, StatusListView
2633
from judger.views import JudgerStatusView, JudgerStatusDetailView
2734
from captcha.views import getcaptcha
@@ -36,6 +43,8 @@
3643
path("api/account/session", AccountSessionView.as_view()),
3744
path("api/account/username/accessibility/<str:username>", AccountUsernameAccessibilityView.as_view()),
3845
path("api/account/password", AccountPasswordView.as_view()),
46+
path("api/account/email", AccountEmailView.as_view()),
47+
path("api/account/email/<str:vid>", AccountEmailView.as_view()),
3948
# Avatar
4049
path("api/account/avatar/<int:uid>", AccountAvatarView.as_view()),
4150
# Problem

0 commit comments

Comments
 (0)