Skip to content

Commit

Permalink
Merge pull request #24 from OVINC-CN/feat_sync
Browse files Browse the repository at this point in the history
feat(asgi): replace async to sync
  • Loading branch information
OrenZhang authored Jan 8, 2025
2 parents e58f449 + 68a6462 commit fe8ee2f
Show file tree
Hide file tree
Showing 23 changed files with 108 additions and 122 deletions.
2 changes: 1 addition & 1 deletion .cruft.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"template": "https://github.com/OVINC-CN/DevTemplateDjango.git",
"commit": "1e0028fba6cb111a2ef69038ca07ac74fc95a219",
"commit": "c57436b9cfec6091334edb5b2115ea498911d51c",
"checkout": "main",
"context": {
"cookiecutter": {
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ pre-commit:
scripts/pre-commit.sh

lint: pre-commit pylint

messages:
scripts/messages.sh
32 changes: 11 additions & 21 deletions apps/account/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import re

from adrf.serializers import ModelSerializer, Serializer
from django.contrib.auth import get_user_model
from django.utils.translation import gettext, gettext_lazy
from ovinc_client.core.async_tools import SyncRunner
from ovinc_client.tcaptcha.exceptions import TCaptchaInvalid
from ovinc_client.tcaptcha.utils import TCaptchaVerify
from rest_framework import serializers
Expand All @@ -21,7 +19,7 @@
USER_MODEL: User = get_user_model()


class UserInfoSerializer(ModelSerializer):
class UserInfoSerializer(serializers.ModelSerializer):
"""
User Info
"""
Expand All @@ -31,7 +29,7 @@ class Meta:
fields = ["username", "nick_name", "last_login", "user_type"]


class SignInSerializer(Serializer):
class SignInSerializer(serializers.Serializer):
"""
Sign in
"""
Expand All @@ -44,14 +42,12 @@ class SignInSerializer(Serializer):

def validate(self, attrs: dict) -> dict:
data = super().validate(attrs)
if not SyncRunner().run(
TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify()
):
if not TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify():
raise TCaptchaInvalid()
return data


class UserRegistrySerializer(ModelSerializer):
class UserRegistrySerializer(serializers.ModelSerializer):
"""
User Registry
"""
Expand Down Expand Up @@ -84,9 +80,7 @@ class Meta:

def validate(self, attrs: dict) -> dict:
data = super().validate(attrs)
if not SyncRunner().run(
TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify()
):
if not TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify():
raise TCaptchaInvalid()
if not USER_MODEL.check_phone_verify_code(
area=data["phone_area"], phone_number=data["phone_number"], code=data["phone_verify"]
Expand All @@ -108,15 +102,15 @@ def validate_phone_number(self, phone_number: str) -> str:
return phone_number


class VerifyCodeRequestSerializer(Serializer):
class VerifyCodeRequestSerializer(serializers.Serializer):
"""
Verify Code
"""

code = serializers.CharField(label=gettext_lazy("Code"))


class WeChatLoginReqSerializer(Serializer):
class WeChatLoginReqSerializer(serializers.Serializer):
"""
WeChat Login
"""
Expand All @@ -126,7 +120,7 @@ class WeChatLoginReqSerializer(Serializer):
is_oauth = serializers.BooleanField(label=gettext_lazy("Is OAuth"), default=False)


class ResetPasswordRequestSerializer(Serializer):
class ResetPasswordRequestSerializer(serializers.Serializer):
"""
Reset Password
"""
Expand All @@ -144,9 +138,7 @@ class ResetPasswordRequestSerializer(Serializer):

def validate(self, attrs: dict) -> dict:
data = super().validate(attrs)
if not SyncRunner().run(
TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify()
):
if not TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify():
raise TCaptchaInvalid()
if not USER_MODEL.check_phone_verify_code(
area=data["phone_area"], phone_number=data["phone_number"], code=data["phone_verify"]
Expand All @@ -155,7 +147,7 @@ def validate(self, attrs: dict) -> dict:
return data


class SendVerifyCodeRequestSerializer(Serializer):
class SendVerifyCodeRequestSerializer(serializers.Serializer):
"""
Verify Code
"""
Expand All @@ -166,8 +158,6 @@ class SendVerifyCodeRequestSerializer(Serializer):

def validate(self, attrs: dict) -> dict:
data = super().validate(attrs)
if not SyncRunner().run(
TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify()
):
if not TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify():
raise TCaptchaInvalid()
return data
71 changes: 35 additions & 36 deletions apps/account/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from json import JSONDecodeError

import httpx
from channels.db import database_sync_to_async
from django.conf import settings
from django.contrib import auth
from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -53,13 +52,13 @@ class UserInfoViewSet(MainViewSet):
queryset = USER_MODEL.get_queryset()
serializer_class = UserInfoSerializer

async def list(self, request, *args, **kwargs):
def list(self, request, *args, **kwargs):
"""
Get User Info
"""

serializer = self.serializer_class(instance=request.user)
return Response(await serializer.adata)
return Response(serializer.data)


class UserSignViewSet(MainViewSet):
Expand All @@ -70,7 +69,7 @@ class UserSignViewSet(MainViewSet):
queryset = USER_MODEL.get_queryset()

@action(methods=["POST"], detail=False, authentication_classes=[SessionAuthenticate])
async def sign_in(self, request, *args, **kwargs):
def sign_in(self, request, *args, **kwargs):
"""
Sign in
"""
Expand All @@ -81,16 +80,16 @@ async def sign_in(self, request, *args, **kwargs):
request_data = request_serializer.validated_data

# login
user: User = await database_sync_to_async(auth.authenticate)(request, **request_data)
user: User = auth.authenticate(request, **request_data)
if not user:
raise WrongSignInParam()

# bind wechat
if request_data.get("wechat_code"):
await self.update_user_by_wechat(user, request_data["wechat_code"])
self.update_user_by_wechat(user, request_data["wechat_code"])

# auth session
await database_sync_to_async(auth.login)(request, user)
auth.login(request, user)

# oauth
if request_data["is_oauth"]:
Expand All @@ -99,27 +98,27 @@ async def sign_in(self, request, *args, **kwargs):
return Response()

@action(methods=["GET"], detail=False)
async def sign_out(self, request, *args, **kwargs):
def sign_out(self, request, *args, **kwargs):
"""
Sign out
"""

await database_sync_to_async(auth.logout)(request)
auth.logout(request)
return Response()

@action(methods=["POST"], detail=False, authentication_classes=[SessionAuthenticate])
async def sign_up(self, request, *args, **kwargs):
def sign_up(self, request, *args, **kwargs):
"""
sign up
"""

# validate request
request_serializer = UserRegistrySerializer(data=request.data, context={"user_ip": get_ip(request)})
await database_sync_to_async(request_serializer.is_valid)(raise_exception=True)
request_serializer.is_valid(raise_exception=True)
request_data = request_serializer.validated_data

# save
user = await database_sync_to_async(USER_MODEL.objects.create_user)(
user = USER_MODEL.objects.create_user(
last_login=datetime.datetime.now(),
username=request_data["username"],
password=request_data["password"],
Expand All @@ -129,10 +128,10 @@ async def sign_up(self, request, *args, **kwargs):

# bind wechat
if request_data.get("wechat_code"):
await self.update_user_by_wechat(user, request_data["wechat_code"])
self.update_user_by_wechat(user, request_data["wechat_code"])

# login session
await database_sync_to_async(auth.login)(request, user)
auth.login(request, user)

# oauth
if request_data["is_oauth"]:
Expand All @@ -147,7 +146,7 @@ async def sign_up(self, request, *args, **kwargs):
authentication_classes=[SessionAuthenticate],
throttle_classes=[SMSRateThrottle, IPRateThrottle],
)
async def phone_verify_code(self, request, *args, **kwargs):
def phone_verify_code(self, request, *args, **kwargs):
"""
send verify code
"""
Expand All @@ -164,15 +163,15 @@ async def phone_verify_code(self, request, *args, **kwargs):
return Response()

@action(methods=["GET"], detail=False)
async def oauth_code(self, request, *args, **kwargs):
def oauth_code(self, request, *args, **kwargs):
"""
oauth code
"""

return Response({"code": request.user.generate_oauth_code()})

@action(methods=["POST"], detail=False, authentication_classes=[ApplicationAuthenticate])
async def verify_code(self, request, *args, **kwargs):
def verify_code(self, request, *args, **kwargs):
"""
verify oauth code
"""
Expand All @@ -183,13 +182,13 @@ async def verify_code(self, request, *args, **kwargs):
request_data = request_serializer.validated_data

# load user
is_success, user = await database_sync_to_async(USER_MODEL.check_oauth_code)(request_data["code"])
is_success, user = USER_MODEL.check_oauth_code(request_data["code"])
if is_success:
return Response(await UserInfoSerializer(instance=user).adata)
return Response(UserInfoSerializer(instance=user).data)
raise WrongToken()

@action(methods=["GET"], detail=False, authentication_classes=[SessionAuthenticate])
async def wechat_config(self, request, *args, **kwargs):
def wechat_config(self, request, *args, **kwargs):
"""
WeChat Config
"""
Expand All @@ -209,7 +208,7 @@ async def wechat_config(self, request, *args, **kwargs):

# pylint: disable=R0914
@action(methods=["POST"], detail=False, authentication_classes=[SessionAuthenticate])
async def wechat_login(self, request, *args, **kwargs):
def wechat_login(self, request, *args, **kwargs):
"""
WeChat Login
"""
Expand Down Expand Up @@ -241,15 +240,15 @@ async def wechat_login(self, request, *args, **kwargs):
f"&code={request_data['code']}"
f"&grant_type={WeChatAuthType.CODE}"
)
client = httpx.AsyncClient()
client = httpx.Client()
try:
resp = await client.get(url)
resp = client.get(url)
access_info = resp.json()
except Exception as err:
logger.exception("[CallWeChatAPIFailed] %s %s", url, err)
raise WeChatLoginFailed() from err
finally:
await client.aclose()
client.close()

if "openid" not in access_info:
logger.error("[WeChatLoginFailed] %s", access_info)
Expand All @@ -259,31 +258,31 @@ async def wechat_login(self, request, *args, **kwargs):
url = (
f"{settings.WECHAT_USER_INFO_API}?access_token={access_info['access_token']}&openid={access_info['openid']}"
)
client = httpx.AsyncClient()
client = httpx.Client()
try:
resp = await client.get(url)
resp = client.get(url)
user_info = resp.json()
except Exception as err:
logger.exception("[CallWeChatAPIFailed] %s", url, err)
raise WeChatLoginFailed() from err
finally:
await client.aclose()
client.close()

code = uniq_id()
cache_key = WECHAT_USER_INFO_KEY.format(code=code)
cache.set(cache_key, json.dumps(user_info, ensure_ascii=False), timeout=settings.WECHAT_SCOPE_TIMEOUT)

# load user
user: User = await database_sync_to_async(USER_MODEL.load_user_by_union_id)(union_id=user_info["unionid"])
user: User = USER_MODEL.load_user_by_union_id(union_id=user_info["unionid"])
if user:
await self.update_user_by_wechat(user, code)
await database_sync_to_async(auth.login)(request, user)
self.update_user_by_wechat(user, code)
auth.login(request, user)
return Response({"code": user.generate_oauth_code() if request_data["is_oauth"] else ""})

# need registry
return Response({"wechat_code": code})

async def update_user_by_wechat(self, user: User, wechat_code: str) -> None:
def update_user_by_wechat(self, user: User, wechat_code: str) -> None:
"""
Update User Info By WeChat
"""
Expand All @@ -299,10 +298,10 @@ async def update_user_by_wechat(self, user: User, wechat_code: str) -> None:
user.wechat_union_id = user_info["unionid"]
user.wechat_open_id = user_info["openid"]
user.avatar = user_info["headimgurl"]
await database_sync_to_async(user.save)(update_fields=["wechat_union_id", "wechat_open_id", "avatar"])
user.save(update_fields=["wechat_union_id", "wechat_open_id", "avatar"])

@action(methods=["POST"], detail=False, authentication_classes=[SessionAuthenticate])
async def reset_password(self, request, *args, **kwargs) -> Response:
def reset_password(self, request, *args, **kwargs) -> Response:
"""
Reset Password
"""
Expand All @@ -314,19 +313,19 @@ async def reset_password(self, request, *args, **kwargs) -> Response:

# load user
try:
user: User = await database_sync_to_async(USER_MODEL.objects.get)(
user: User = USER_MODEL.objects.get(
username=request_data["username"], phone_number=request_data["phone_number"]
)
except USER_MODEL.DoesNotExist as err:
raise UserNotExist() from err

# set new password
await database_sync_to_async(user.reset_password)(request_data["password"])
user.reset_password(request_data["password"])

return Response()

@action(methods=["GET"], detail=False, authentication_classes=[SessionAuthenticate])
async def phone_areas(self, request, *args, **kwargs) -> Response:
def phone_areas(self, request, *args, **kwargs) -> Response:
"""
Phone Number Areas
"""
Expand Down
2 changes: 1 addition & 1 deletion apps/application/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from adrf.serializers import ModelSerializer
from django.contrib.auth import get_user_model
from rest_framework.serializers import ModelSerializer

from apps.account.models import User
from apps.application.models import Application
Expand Down
4 changes: 2 additions & 2 deletions apps/application/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ApplicationViewSet(MainViewSet):
serializer_class = ApplicationSerializer

@action(methods=["GET"], detail=False, authentication_classes=[SessionAuthenticate])
async def all(self, request, *args, **kwargs):
def all(self, request, *args, **kwargs):
"""
list all applications
"""
Expand All @@ -33,4 +33,4 @@ async def all(self, request, *args, **kwargs):

# response
serializer = ApplicationListSerializer(queryset, many=True)
return Response(await serializer.adata)
return Response(serializer.data)
Loading

0 comments on commit fe8ee2f

Please sign in to comment.