diff --git a/account/migrations/0009_remove_user_tfa_token_remove_user_two_factor_auth.py b/account/migrations/0009_remove_user_tfa_token_remove_user_two_factor_auth.py new file mode 100644 index 0000000..82ae026 --- /dev/null +++ b/account/migrations/0009_remove_user_tfa_token_remove_user_two_factor_auth.py @@ -0,0 +1,21 @@ +# Generated by Django 6.0.4 on 2026-08-06 04:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0008_remove_userprofile_oi_problems_status_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='tfa_token', + ), + migrations.RemoveField( + model_name='user', + name='two_factor_auth', + ), + ] diff --git a/account/models.py b/account/models.py index 35ecf07..ef7d3a9 100644 --- a/account/models.py +++ b/account/models.py @@ -40,8 +40,6 @@ class User(AbstractBaseUser): reset_password_token_expire_time = models.DateTimeField(null=True) # SSO auth token auth_token = models.TextField(null=True) - two_factor_auth = models.BooleanField(default=False, db_default=False) - tfa_token = models.TextField(null=True) session_keys = JSONField(default=list, db_default=models.Value([], output_field=models.JSONField())) # open api key open_api = models.BooleanField(default=False, db_default=False) diff --git a/account/serializers.py b/account/serializers.py index 05b7560..7e3a221 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -8,7 +8,6 @@ from .models import AdminType, ProblemPermission, User, UserProfile class UserLoginSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField() - tfa_code = serializers.CharField(required=False, allow_blank=True) class UsernameOrEmailCheckSerializer(serializers.Serializer): @@ -26,13 +25,11 @@ class UserRegisterSerializer(serializers.Serializer): class UserChangePasswordSerializer(serializers.Serializer): old_password = serializers.CharField() new_password = serializers.CharField(min_length=6) - tfa_code = serializers.CharField(required=False, allow_blank=True) class UserChangeEmailSerializer(serializers.Serializer): password = serializers.CharField() new_email = serializers.EmailField(max_length=64) - tfa_code = serializers.CharField(required=False, allow_blank=True) class GenerateUserSerializer(serializers.Serializer): @@ -61,7 +58,6 @@ class UserAdminSerializer(serializers.ModelSerializer): "real_name", "create_time", "last_login", - "two_factor_auth", "open_api", "is_disabled", "raw_password", @@ -89,7 +85,6 @@ class UserSerializer(serializers.ModelSerializer): "problem_permission", "create_time", "last_login", - "two_factor_auth", "open_api", "is_disabled", "class_name", @@ -121,7 +116,6 @@ class EditUserSerializer(serializers.Serializer): admin_type = serializers.ChoiceField(choices=AdminType.choices) problem_permission = serializers.ChoiceField(choices=ProblemPermission.choices) open_api = serializers.BooleanField() - two_factor_auth = serializers.BooleanField() is_disabled = serializers.BooleanField() class_name = serializers.CharField(required=False, allow_null=True, allow_blank=True) @@ -152,10 +146,6 @@ class SSOSerializer(serializers.Serializer): token = serializers.CharField() -class TwoFactorAuthCodeSerializer(serializers.Serializer): - code = serializers.IntegerField() - - class ImageUploadForm(forms.Form): image = forms.FileField() diff --git a/account/urls/oj.py b/account/urls/oj.py index f767d70..5b77b95 100644 --- a/account/urls/oj.py +++ b/account/urls/oj.py @@ -6,13 +6,11 @@ from ..views.oj import ( SSOAPI, ApplyResetPasswordAPI, AvatarUploadAPI, - CheckTFARequiredAPI, Metrics, OpenAPIAppkeyAPI, ProfileProblemDisplayIDRefreshAPI, ResetPasswordAPI, SessionManagementAPI, - TwoFactorAuthAPI, UserActivityRankAPI, UserChangeEmailAPI, UserChangePasswordAPI, @@ -39,11 +37,6 @@ urlpatterns = [ path("profile/fresh_display_id", ProfileProblemDisplayIDRefreshAPI.as_view()), path("metrics", Metrics.as_view()), path("upload_avatar", AvatarUploadAPI.as_view()), - path("tfa_required", CheckTFARequiredAPI.as_view()), # DEPRECATED: 前端未调用 - path( - "two_factor_auth", # DEPRECATED: 前端未调用 - TwoFactorAuthAPI.as_view(), - ), path("user_rank", UserRankAPI.as_view()), path("user_activity_rank", UserActivityRankAPI.as_view()), path("user_problem_rank", UserProblemRankAPI.as_view()), diff --git a/account/views/admin.py b/account/views/admin.py index 95dd756..5a4be72 100644 --- a/account/views/admin.py +++ b/account/views/admin.py @@ -112,15 +112,6 @@ class UserAdminAPI(APIView): user.open_api_appkey = None user.open_api = data["open_api"] - if data["two_factor_auth"]: - # Avoid reset user tfa_token after saving changes - if not user.two_factor_auth: - user.tfa_token = rand_str() - else: - user.tfa_token = None - - user.two_factor_auth = data["two_factor_auth"] - user.save() if pre_username != user.username: Submission.objects.filter(username=pre_username).update(username=user.username) diff --git a/account/views/oj.py b/account/views/oj.py index 4f4616b..3848a98 100644 --- a/account/views/oj.py +++ b/account/views/oj.py @@ -3,7 +3,6 @@ import os from datetime import timedelta from importlib import import_module -import qrcode from django.conf import settings from django.contrib import auth from django.db.models import Count, Q @@ -12,7 +11,6 @@ from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.timezone import now from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie -from otpauth import TOTP from options.options import SysOptions from problem.models import Problem @@ -21,7 +19,7 @@ from utils.api import APIView, AsyncAPIView, CSRFExemptAPIView, validate_seriali from utils.async_helpers import async_cache_get, async_cache_set from utils.captcha import Captcha from utils.constants import CacheKey -from utils.shortcuts import datetime2str, img2base64, rand_str +from utils.shortcuts import datetime2str, rand_str from ..decorators import login_required from ..models import AdminType, User, UserProfile @@ -32,7 +30,6 @@ from ..serializers import ( RankInfoSerializer, ResetPasswordSerializer, SSOSerializer, - TwoFactorAuthCodeSerializer, UserChangeEmailSerializer, UserChangePasswordSerializer, UserLoginSerializer, @@ -43,22 +40,6 @@ from ..serializers import ( from ..tasks import send_email_async -def _totp(token): - return TOTP(token.encode("utf-8")) - - -def _totp_uri(token, label, issuer): - return _totp(token).to_uri(label, issuer) - - -def _valid_totp(token, code): - try: - code = int(code) - except (TypeError, ValueError): - return False - return _totp(token).verify(code) - - class UserProfileAPI(AsyncAPIView): @method_decorator(ensure_csrf_cookie) async def get(self, request, **kwargs): @@ -136,72 +117,6 @@ class AvatarUploadAPI(AsyncAPIView): return self.success("Succeeded") -# DEPRECATED: 前端未调用 (2026-05-26) -class TwoFactorAuthAPI(APIView): - @login_required - def get(self, request): - """ - Get QR code - """ - user = request.user - if user.two_factor_auth: - return self.error("2FA is already turned on") - token = rand_str() - user.tfa_token = token - user.save() - - label = f"{SysOptions.website_name_shortcut}:{user.username}" - image = qrcode.make(_totp_uri(token, label, SysOptions.website_name.replace(" ", ""))) - return self.success(img2base64(image)) - - @login_required - @validate_serializer(TwoFactorAuthCodeSerializer) - def post(self, request): - """ - Open 2FA - """ - code = request.data["code"] - user = request.user - if _valid_totp(user.tfa_token, code): - user.two_factor_auth = True - user.save() - return self.success("Succeeded") - else: - return self.error("Invalid code") - - @login_required - @validate_serializer(TwoFactorAuthCodeSerializer) - def put(self, request): - code = request.data["code"] - user = request.user - if not user.two_factor_auth: - return self.error("2FA is already turned off") - if _valid_totp(user.tfa_token, code): - user.two_factor_auth = False - user.save() - return self.success("Succeeded") - else: - return self.error("Invalid code") - - -# DEPRECATED: 前端未调用 (2026-05-26) -class CheckTFARequiredAPI(APIView): - @validate_serializer(UsernameOrEmailCheckSerializer) - def post(self, request): - """ - Check TFA is required - """ - data = request.data - result = False - if data.get("username"): - try: - user = User.objects.get(username=data["username"]) - result = user.two_factor_auth - except User.DoesNotExist: - pass - return self.success({"result": result}) - - class UserLoginAPI(AsyncAPIView): @validate_serializer(UserLoginSerializer) async def post(self, request): @@ -210,22 +125,10 @@ class UserLoginAPI(AsyncAPIView): if user: if user.is_disabled: return self.error("Your account has been disabled") - if not user.two_factor_auth: - prev_login = user.last_login - await auth.alogin(request, user) - request.session["prev_login"] = datetime2str(prev_login) if prev_login else "" - return self.success("Succeeded") - - if user.two_factor_auth and "tfa_code" not in data: - return self.error("tfa_required") - - if _valid_totp(user.tfa_token, data["tfa_code"]): - prev_login = user.last_login - await auth.alogin(request, user) - request.session["prev_login"] = datetime2str(prev_login) if prev_login else "" - return self.success("Succeeded") - else: - return self.error("Invalid two factor verification code") + prev_login = user.last_login + await auth.alogin(request, user) + request.session["prev_login"] = datetime2str(prev_login) if prev_login else "" + return self.success("Succeeded") else: return self.error("Invalid username or password") @@ -284,11 +187,6 @@ class UserChangeEmailAPI(APIView): data = request.data user = auth.authenticate(username=request.user.username, password=data["password"]) if user: - if user.two_factor_auth: - if "tfa_code" not in data: - return self.error("tfa_required") - if not _valid_totp(user.tfa_token, data["tfa_code"]): - return self.error("Invalid two factor verification code") data["new_email"] = data["new_email"].lower() if User.objects.filter(email=data["new_email"]).exists(): return self.error("The email is owned by other account") @@ -311,11 +209,6 @@ class UserChangePasswordAPI(APIView): username = request.user.username user = auth.authenticate(username=username, password=data["old_password"]) if user: - if user.two_factor_auth: - if "tfa_code" not in data: - return self.error("tfa_required") - if not _valid_totp(user.tfa_token, data["tfa_code"]): - return self.error("Invalid two factor verification code") user.set_password(data["new_password"]) user.save() return self.success("Succeeded") @@ -373,7 +266,6 @@ class ResetPasswordAPI(APIView): if user.reset_password_token_expire_time < now(): return self.error("Token has expired") user.reset_password_token = None - user.two_factor_auth = False user.set_password(data["password"]) user.save() return self.success("Succeeded") diff --git a/conf/urls/admin.py b/conf/urls/admin.py index a9b5f6c..d0985ab 100644 --- a/conf/urls/admin.py +++ b/conf/urls/admin.py @@ -5,7 +5,6 @@ from ..views import ( DashboardInfoAPI, JudgeServerAPI, RandomUsernameAPI, - ReleaseNotesAPI, SMTPTestAPI, TestCasePruneAPI, WebsiteConfigAPI, @@ -18,6 +17,5 @@ urlpatterns = [ path("random_user", RandomUsernameAPI.as_view()), path("judge_server", JudgeServerAPI.as_view()), path("prune_test_case", TestCasePruneAPI.as_view()), - path("versions", ReleaseNotesAPI.as_view()), # DEPRECATED: 前端未调用 path("dashboard_info", DashboardInfoAPI.as_view()), ] diff --git a/conf/views.py b/conf/views.py index 2429ca2..5732e99 100644 --- a/conf/views.py +++ b/conf/views.py @@ -1,19 +1,15 @@ import asyncio import hashlib -import json import os import random import re import shutil import smtplib -import time from datetime import timedelta -import requests from asgiref.sync import sync_to_async from django.conf import settings from django.utils import timezone -from requests.exceptions import RequestException from account.decorators import super_admin_required from account.models import User @@ -253,23 +249,6 @@ class TestCasePruneAPI(APIView): shutil.rmtree(test_case_dir, ignore_errors=True) -# DEPRECATED: 前端未调用 (2026-05-26) -class ReleaseNotesAPI(APIView): - def get(self, request): - try: - resp = requests.get( - "https://raw.githubusercontent.com/QingdaoU/OnlineJudge/master/docs/data.json?_=" + str(time.time()), - timeout=3, - ) - releases = resp.json() - except (RequestException, ValueError): - return self.success() - with open("docs/data.json", "r") as f: - local_version = json.load(f)["update"][0]["version"] - releases["local_version"] = local_version - return self.success(releases) - - class DashboardInfoAPI(AsyncAPIView): async def get(self, request): now = timezone.now() diff --git a/deploy/requirements.txt b/deploy/requirements.txt index f5ef107..cf1aa1f 100644 --- a/deploy/requirements.txt +++ b/deploy/requirements.txt @@ -1,5 +1,5 @@ # This file was autogenerated by uv via the following command: -# uv export --format requirements.txt --no-dev -o deploy/requirements.txt +# uv export --format requirements.txt annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 @@ -25,7 +25,6 @@ certifi==2026.4.22 \ # via # httpcore # httpx - # requests # sentry-sdk channels==4.3.2 \ --hash=sha256:f2bb6bfb73ad7fb4705041d07613c7b4e69528f01ef8cb9fb6c21d9295f15667 \ @@ -37,62 +36,6 @@ channels-redis==4.3.0 \ --hash=sha256:48f3e902ae2d5fef7080215524f3b4a1d3cea4e304150678f867a1a822c0d9f5 \ --hash=sha256:740ee7b54f0e28cf2264a940a24453d3f00526a96931f911fcb69228ef245dd2 # via onlinejudge -charset-normalizer==3.4.9 \ - --hash=sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380 \ - --hash=sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62 \ - --hash=sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226 \ - --hash=sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b \ - --hash=sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501 \ - --hash=sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698 \ - --hash=sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e \ - --hash=sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4 \ - --hash=sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519 \ - --hash=sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe \ - --hash=sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614 \ - --hash=sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0 \ - --hash=sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0 \ - --hash=sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917 \ - --hash=sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9 \ - --hash=sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32 \ - --hash=sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63 \ - --hash=sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd \ - --hash=sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198 \ - --hash=sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde \ - --hash=sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012 \ - --hash=sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b \ - --hash=sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5 \ - --hash=sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8 \ - --hash=sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35 \ - --hash=sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642 \ - --hash=sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2 \ - --hash=sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9 \ - --hash=sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c \ - --hash=sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33 \ - --hash=sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf \ - --hash=sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9 \ - --hash=sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84 \ - --hash=sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44 \ - --hash=sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9 \ - --hash=sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177 \ - --hash=sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b \ - --hash=sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39 \ - --hash=sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41 \ - --hash=sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0 \ - --hash=sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9 \ - --hash=sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48 \ - --hash=sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046 \ - --hash=sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632 \ - --hash=sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2 \ - --hash=sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1 \ - --hash=sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990 \ - --hash=sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b \ - --hash=sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534 \ - --hash=sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81 \ - --hash=sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a \ - --hash=sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d \ - --hash=sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf \ - --hash=sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115 - # via requests click==8.4.1 \ --hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \ --hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96 @@ -102,7 +45,6 @@ colorama==0.4.6 ; sys_platform == 'win32' \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via # click - # qrcode # tqdm # uvicorn distro==1.9.0 \ @@ -119,9 +61,6 @@ django==6.0.4 \ # djangorestframework # onlinejudge # sentry-sdk -django-dbconn-retry==0.3.1 \ - --hash=sha256:d4b64d915440c3e5902ef8edf836366a6f4c4f027d34902135d7335233d6dbba - # via onlinejudge django-dramatiq==0.15.0 \ --hash=sha256:23f0bc418a860952adbf822c4aa3b9c46c51d3d9f50be0a8ed3d19a53380df1d \ --hash=sha256:e3cf1b2ac288fe4a7aa198c9450fe242ed312df8850f3f9e18ce01b8acc78b96 @@ -188,14 +127,15 @@ httptools==0.8.0 \ httpx==0.28.1 \ --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad - # via openai + # via + # onlinejudge + # openai idna==3.13 \ --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \ --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3 # via # anyio # httpx - # requests jieba==0.42.1 \ --hash=sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2 # via onlinejudge @@ -309,10 +249,6 @@ openai==2.34.0 \ --hash=sha256:828b4efcbb126352c2b5eb97d33ae890c92a71ab72511aefc1b7fe64aeccb07b \ --hash=sha256:c996a71b1a210f3569844572ad4c609307e978515fb76877cf449b72596e549e # via onlinejudge -otpauth==2.2.1 \ - --hash=sha256:169a7adbd715fca687f6a66d02ccdbefc229fb49f8a634b958d286f908134d59 \ - --hash=sha256:b7eabe0ed91cb67eb3054b7f517e4b4a7495fb30eaf2951897d41c8feef5de73 - # via onlinejudge packaging==26.2 \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 @@ -540,20 +476,31 @@ pyyaml==6.0.3 \ --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 # via uvicorn -qrcode==8.2 \ - --hash=sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f \ - --hash=sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c - # via onlinejudge redis==7.4.0 \ --hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \ --hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec # via # channels-redis # django-redis -requests==2.34.2 \ - --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ - --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed - # via onlinejudge +ruff==0.15.12 \ + --hash=sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b \ + --hash=sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33 \ + --hash=sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0 \ + --hash=sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002 \ + --hash=sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339 \ + --hash=sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e \ + --hash=sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847 \ + --hash=sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f \ + --hash=sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6 \ + --hash=sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d \ + --hash=sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20 \ + --hash=sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd \ + --hash=sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c \ + --hash=sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5 \ + --hash=sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6 \ + --hash=sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c \ + --hash=sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5 \ + --hash=sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5 sentry-sdk==2.59.0 \ --hash=sha256:abcf65ee9a9d9cdebf9ad369782408ecca9c1c792686ef06ba34f5ab233527fe \ --hash=sha256:cd265808ef8bf3f3edf69b527c0a0b2b6b1322762679e55b8987db2e9584aec1 @@ -643,9 +590,7 @@ tzdata==2026.2 ; sys_platform == 'win32' \ urllib3==2.6.3 \ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 - # via - # requests - # sentry-sdk + # via sentry-sdk uvicorn==0.48.0 \ --hash=sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad \ --hash=sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37 diff --git a/judge/dispatcher.py b/judge/dispatcher.py index c21c4aa..d8c2448 100644 --- a/judge/dispatcher.py +++ b/judge/dispatcher.py @@ -4,7 +4,7 @@ import logging from datetime import timedelta from urllib.parse import urljoin -import requests +import httpx from django.db import IntegrityError, transaction from django.db.models import F from django.utils import timezone @@ -73,7 +73,8 @@ class DispatcherBase(object): if data: kwargs["json"] = data try: - return requests.post(url, **kwargs).json() + # timeout=None 保持与原 requests 实现一致:判题请求是同步等结果的,不能被默认超时打断 + return httpx.post(url, timeout=None, **kwargs).json() except Exception as e: logger.exception(e) diff --git a/oj/settings.py b/oj/settings.py index 3185e75..a4ec429 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -40,7 +40,6 @@ VENDOR_APPS = [ "rest_framework", "channels", "django_dramatiq", - "django_dbconn_retry", ] diff --git a/pyproject.toml b/pyproject.toml index 6955919..e210c7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,20 +9,17 @@ dependencies = [ "channels-redis>=4.3.0,<5", "uvicorn[standard]>=0.34.0,<1", "django>=6.0.4,<6.1", - "django-dbconn-retry>=0.3.1,<0.4", "django-dramatiq>=0.15.0,<0.16", "django-redis>=6.0.0,<7", "djangorestframework>=3.17.1,<4", "dramatiq>=2.1.0,<3", "gunicorn>=26.0.0,<27", + "httpx>=0.28.1,<1", "openai>=2.34.0,<3", - "otpauth>=2.2.1,<3", "pillow>=12.2.0,<13", "psycopg>=3.3.4,<4", "psycopg-binary>=3.3.4,<4", "python-dateutil>=2.9.0.post0,<3", - "qrcode>=8.2,<9", - "requests>=2.33.1,<3", "sentry-sdk[django]>=2.0.0,<3", "tree-sitter>=0.25.2", "tree-sitter-c>=0.24.2", diff --git a/uv.lock b/uv.lock index 22804c9..3c36c52 100644 --- a/uv.lock +++ b/uv.lock @@ -70,67 +70,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/fe/b7224a401ad227b263e5ba84753ffb5a88df048f3b15efd2797903543ce4/channels_redis-4.3.0-py3-none-any.whl", hash = "sha256:48f3e902ae2d5fef7080215524f3b4a1d3cea4e304150678f867a1a822c0d9f5", size = 20641, upload-time = "2025-07-22T13:48:44.545Z" }, ] -[[package]] -name = "charset-normalizer" -version = "3.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", size = 319300, upload-time = "2026-07-07T14:33:15.666Z" }, - { url = "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", size = 215802, upload-time = "2026-07-07T14:33:17.031Z" }, - { url = "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", size = 237171, upload-time = "2026-07-07T14:33:18.576Z" }, - { url = "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", size = 233075, upload-time = "2026-07-07T14:33:20.084Z" }, - { url = "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", size = 224256, upload-time = "2026-07-07T14:33:21.747Z" }, - { url = "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", size = 208784, upload-time = "2026-07-07T14:33:23.313Z" }, - { url = "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", size = 219928, upload-time = "2026-07-07T14:33:24.814Z" }, - { url = "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", size = 218489, upload-time = "2026-07-07T14:33:26.42Z" }, - { url = "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", size = 210267, upload-time = "2026-07-07T14:33:27.952Z" }, - { url = "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", size = 226030, upload-time = "2026-07-07T14:33:29.397Z" }, - { url = "https://files.pythonhosted.org/packages/0e/42/6dbc00b8cd16011691203e33570fa42ed5746599a2e878112d16eab403a3/charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642", size = 151185, upload-time = "2026-07-07T14:33:30.781Z" }, - { url = "https://files.pythonhosted.org/packages/80/cc/f920afd1a23c58ccd53c1d36085a71893a4737ff5e66e0371efab6809850/charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0", size = 162557, upload-time = "2026-07-07T14:33:32.176Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e6/0386d43a261ff4e4b30c5857af7df877254b46bec7b9d1b74b6bf969a90b/charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2", size = 152665, upload-time = "2026-07-07T14:33:33.711Z" }, - { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, - { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, - { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, - { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, - { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, - { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, - { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, - { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, - { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, - { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, - { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, - { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, - { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, - { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, - { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, - { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, - { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, - { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, - { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, - { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, - { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, - { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, - { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, - { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, - { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, - { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, -] - [[package]] name = "click" version = "8.4.1" @@ -175,14 +114,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/47/3d61d611609764aa71a37f7037b870e7bfb22937366974c4fd46cada7bab/django-6.0.4-py3-none-any.whl", hash = "sha256:14359c809fc16e8f81fd2b59d7d348e4d2d799da6840b10522b6edf7b8afc1da", size = 8368342, upload-time = "2026-04-07T13:55:37.999Z" }, ] -[[package]] -name = "django-dbconn-retry" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/2a/4ac8cf6fa6ae3f127ac7ecfa4c2c712129b3803978d3fecc4d127024e016/django_dbconn_retry-0.3.1-py3-none-any.whl", hash = "sha256:d4b64d915440c3e5902ef8edf836366a6f4c4f027d34902135d7335233d6dbba", size = 8999, upload-time = "2026-05-02T15:56:58.862Z" }, -] - [[package]] name = "django-dramatiq" version = "0.15.0" @@ -455,21 +386,18 @@ dependencies = [ { name = "channels" }, { name = "channels-redis" }, { name = "django" }, - { name = "django-dbconn-retry" }, { name = "django-dramatiq" }, { name = "django-redis" }, { name = "djangorestframework" }, { name = "dramatiq" }, { name = "gunicorn" }, + { name = "httpx" }, { name = "jieba" }, { name = "openai" }, - { name = "otpauth" }, { name = "pillow" }, { name = "psycopg" }, { name = "psycopg-binary" }, { name = "python-dateutil" }, - { name = "qrcode" }, - { name = "requests" }, { name = "sentry-sdk", extra = ["django"] }, { name = "tree-sitter" }, { name = "tree-sitter-c" }, @@ -489,21 +417,18 @@ requires-dist = [ { name = "channels", specifier = ">=4.3.2,<5" }, { name = "channels-redis", specifier = ">=4.3.0,<5" }, { name = "django", specifier = ">=6.0.4,<6.1" }, - { name = "django-dbconn-retry", specifier = ">=0.3.1,<0.4" }, { name = "django-dramatiq", specifier = ">=0.15.0,<0.16" }, { name = "django-redis", specifier = ">=6.0.0,<7" }, { name = "djangorestframework", specifier = ">=3.17.1,<4" }, { name = "dramatiq", specifier = ">=2.1.0,<3" }, { name = "gunicorn", specifier = ">=26.0.0,<27" }, + { name = "httpx", specifier = ">=0.28.1,<1" }, { name = "jieba", specifier = ">=0.42.1" }, { name = "openai", specifier = ">=2.34.0,<3" }, - { name = "otpauth", specifier = ">=2.2.1,<3" }, { name = "pillow", specifier = ">=12.2.0,<13" }, { name = "psycopg", specifier = ">=3.3.4,<4" }, { name = "psycopg-binary", specifier = ">=3.3.4,<4" }, { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, - { name = "qrcode", specifier = ">=8.2,<9" }, - { name = "requests", specifier = ">=2.33.1,<3" }, { name = "sentry-sdk", extras = ["django"], specifier = ">=2.0.0,<3" }, { name = "tree-sitter", specifier = ">=0.25.2" }, { name = "tree-sitter-c", specifier = ">=0.24.2" }, @@ -534,15 +459,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f2/40/f090499f10514515081d09cb9da09f25b821eb20497e9423afe4f07b4ecf/openai-2.34.0-py3-none-any.whl", hash = "sha256:c996a71b1a210f3569844572ad4c609307e978515fb76877cf449b72596e549e", size = 1316535, upload-time = "2026-05-04T17:34:06.773Z" }, ] -[[package]] -name = "otpauth" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/99/e6c2adda32cc969b0b7c762e17c53575db5a31e4d55e08dee36c0f801573/otpauth-2.2.1.tar.gz", hash = "sha256:169a7adbd715fca687f6a66d02ccdbefc229fb49f8a634b958d286f908134d59", size = 26444, upload-time = "2025-05-09T11:12:01.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/2c/bc109eade0af850d38837b9835f812d8a8bcad1269bbcbdc136deb9fed30/otpauth-2.2.1-py3-none-any.whl", hash = "sha256:b7eabe0ed91cb67eb3054b7f517e4b4a7495fb30eaf2951897d41c8feef5de73", size = 6412, upload-time = "2025-05-09T11:12:00.485Z" }, -] - [[package]] name = "packaging" version = "26.2" @@ -831,18 +747,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] -[[package]] -name = "qrcode" -version = "8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" }, -] - [[package]] name = "redis" version = "7.4.0" @@ -852,21 +756,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/74/3a/95deec7db1eb53979973ebd156f3369a72732208d1391cd2e5d127062a32/redis-7.4.0-py3-none-any.whl", hash = "sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec", size = 409772, upload-time = "2026-03-24T09:14:35.968Z" }, ] -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - [[package]] name = "ruff" version = "0.15.12"