refactor: 移除 2FA 功能与三个冗余依赖
2FA 早已是死代码:TOTP 相关端点标注为前端未调用,前端唯一的 two_factor_auth 是硬编码 false,没有任何入口能打开它。 - 删除 TwoFactorAuthAPI、CheckTFARequiredAPI、_totp* 辅助函数、 TwoFactorAuthCodeSerializer 及各 serializer 的 tfa_code 字段 - 删除管理端写 two_factor_auth/tfa_token 的分支。登录已不再校验 TOTP,若保留写入路径,置 True 会变成静默的安全降级 - 删除 User.two_factor_auth / tfa_token 字段(迁移 0009) - 移除 django-dbconn-retry:仅挂在 INSTALLED_APPS,代码零引用。 DATABASES 未配置 CONN_MAX_AGE(默认 0),本就没有持久连接需要 重连修复。要开持久连接用 Django 自带的 CONN_HEALTH_CHECKS - requests 换成 httpx(openai 已经引入):删除废弃的 ReleaseNotesAPI,judge dispatcher 显式传 timeout=None 以保持 requests 原本的无超时行为,判题请求是同步等结果的 依赖净减 5 个:otpauth、qrcode、requests、charset-normalizer、 django-dbconn-retry Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -40,8 +40,6 @@ class User(AbstractBaseUser):
|
|||||||
reset_password_token_expire_time = models.DateTimeField(null=True)
|
reset_password_token_expire_time = models.DateTimeField(null=True)
|
||||||
# SSO auth token
|
# SSO auth token
|
||||||
auth_token = models.TextField(null=True)
|
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()))
|
session_keys = JSONField(default=list, db_default=models.Value([], output_field=models.JSONField()))
|
||||||
# open api key
|
# open api key
|
||||||
open_api = models.BooleanField(default=False, db_default=False)
|
open_api = models.BooleanField(default=False, db_default=False)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from .models import AdminType, ProblemPermission, User, UserProfile
|
|||||||
class UserLoginSerializer(serializers.Serializer):
|
class UserLoginSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField()
|
username = serializers.CharField()
|
||||||
password = serializers.CharField()
|
password = serializers.CharField()
|
||||||
tfa_code = serializers.CharField(required=False, allow_blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class UsernameOrEmailCheckSerializer(serializers.Serializer):
|
class UsernameOrEmailCheckSerializer(serializers.Serializer):
|
||||||
@@ -26,13 +25,11 @@ class UserRegisterSerializer(serializers.Serializer):
|
|||||||
class UserChangePasswordSerializer(serializers.Serializer):
|
class UserChangePasswordSerializer(serializers.Serializer):
|
||||||
old_password = serializers.CharField()
|
old_password = serializers.CharField()
|
||||||
new_password = serializers.CharField(min_length=6)
|
new_password = serializers.CharField(min_length=6)
|
||||||
tfa_code = serializers.CharField(required=False, allow_blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class UserChangeEmailSerializer(serializers.Serializer):
|
class UserChangeEmailSerializer(serializers.Serializer):
|
||||||
password = serializers.CharField()
|
password = serializers.CharField()
|
||||||
new_email = serializers.EmailField(max_length=64)
|
new_email = serializers.EmailField(max_length=64)
|
||||||
tfa_code = serializers.CharField(required=False, allow_blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class GenerateUserSerializer(serializers.Serializer):
|
class GenerateUserSerializer(serializers.Serializer):
|
||||||
@@ -61,7 +58,6 @@ class UserAdminSerializer(serializers.ModelSerializer):
|
|||||||
"real_name",
|
"real_name",
|
||||||
"create_time",
|
"create_time",
|
||||||
"last_login",
|
"last_login",
|
||||||
"two_factor_auth",
|
|
||||||
"open_api",
|
"open_api",
|
||||||
"is_disabled",
|
"is_disabled",
|
||||||
"raw_password",
|
"raw_password",
|
||||||
@@ -89,7 +85,6 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
"problem_permission",
|
"problem_permission",
|
||||||
"create_time",
|
"create_time",
|
||||||
"last_login",
|
"last_login",
|
||||||
"two_factor_auth",
|
|
||||||
"open_api",
|
"open_api",
|
||||||
"is_disabled",
|
"is_disabled",
|
||||||
"class_name",
|
"class_name",
|
||||||
@@ -121,7 +116,6 @@ class EditUserSerializer(serializers.Serializer):
|
|||||||
admin_type = serializers.ChoiceField(choices=AdminType.choices)
|
admin_type = serializers.ChoiceField(choices=AdminType.choices)
|
||||||
problem_permission = serializers.ChoiceField(choices=ProblemPermission.choices)
|
problem_permission = serializers.ChoiceField(choices=ProblemPermission.choices)
|
||||||
open_api = serializers.BooleanField()
|
open_api = serializers.BooleanField()
|
||||||
two_factor_auth = serializers.BooleanField()
|
|
||||||
is_disabled = serializers.BooleanField()
|
is_disabled = serializers.BooleanField()
|
||||||
class_name = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
class_name = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||||||
|
|
||||||
@@ -152,10 +146,6 @@ class SSOSerializer(serializers.Serializer):
|
|||||||
token = serializers.CharField()
|
token = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class TwoFactorAuthCodeSerializer(serializers.Serializer):
|
|
||||||
code = serializers.IntegerField()
|
|
||||||
|
|
||||||
|
|
||||||
class ImageUploadForm(forms.Form):
|
class ImageUploadForm(forms.Form):
|
||||||
image = forms.FileField()
|
image = forms.FileField()
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ from ..views.oj import (
|
|||||||
SSOAPI,
|
SSOAPI,
|
||||||
ApplyResetPasswordAPI,
|
ApplyResetPasswordAPI,
|
||||||
AvatarUploadAPI,
|
AvatarUploadAPI,
|
||||||
CheckTFARequiredAPI,
|
|
||||||
Metrics,
|
Metrics,
|
||||||
OpenAPIAppkeyAPI,
|
OpenAPIAppkeyAPI,
|
||||||
ProfileProblemDisplayIDRefreshAPI,
|
ProfileProblemDisplayIDRefreshAPI,
|
||||||
ResetPasswordAPI,
|
ResetPasswordAPI,
|
||||||
SessionManagementAPI,
|
SessionManagementAPI,
|
||||||
TwoFactorAuthAPI,
|
|
||||||
UserActivityRankAPI,
|
UserActivityRankAPI,
|
||||||
UserChangeEmailAPI,
|
UserChangeEmailAPI,
|
||||||
UserChangePasswordAPI,
|
UserChangePasswordAPI,
|
||||||
@@ -39,11 +37,6 @@ urlpatterns = [
|
|||||||
path("profile/fresh_display_id", ProfileProblemDisplayIDRefreshAPI.as_view()),
|
path("profile/fresh_display_id", ProfileProblemDisplayIDRefreshAPI.as_view()),
|
||||||
path("metrics", Metrics.as_view()),
|
path("metrics", Metrics.as_view()),
|
||||||
path("upload_avatar", AvatarUploadAPI.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_rank", UserRankAPI.as_view()),
|
||||||
path("user_activity_rank", UserActivityRankAPI.as_view()),
|
path("user_activity_rank", UserActivityRankAPI.as_view()),
|
||||||
path("user_problem_rank", UserProblemRankAPI.as_view()),
|
path("user_problem_rank", UserProblemRankAPI.as_view()),
|
||||||
|
|||||||
@@ -112,15 +112,6 @@ class UserAdminAPI(APIView):
|
|||||||
user.open_api_appkey = None
|
user.open_api_appkey = None
|
||||||
user.open_api = data["open_api"]
|
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()
|
user.save()
|
||||||
if pre_username != user.username:
|
if pre_username != user.username:
|
||||||
Submission.objects.filter(username=pre_username).update(username=user.username)
|
Submission.objects.filter(username=pre_username).update(username=user.username)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import os
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
import qrcode
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.db.models import Count, Q
|
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.decorators import method_decorator
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
|
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
|
||||||
from otpauth import TOTP
|
|
||||||
|
|
||||||
from options.options import SysOptions
|
from options.options import SysOptions
|
||||||
from problem.models import Problem
|
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.async_helpers import async_cache_get, async_cache_set
|
||||||
from utils.captcha import Captcha
|
from utils.captcha import Captcha
|
||||||
from utils.constants import CacheKey
|
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 ..decorators import login_required
|
||||||
from ..models import AdminType, User, UserProfile
|
from ..models import AdminType, User, UserProfile
|
||||||
@@ -32,7 +30,6 @@ from ..serializers import (
|
|||||||
RankInfoSerializer,
|
RankInfoSerializer,
|
||||||
ResetPasswordSerializer,
|
ResetPasswordSerializer,
|
||||||
SSOSerializer,
|
SSOSerializer,
|
||||||
TwoFactorAuthCodeSerializer,
|
|
||||||
UserChangeEmailSerializer,
|
UserChangeEmailSerializer,
|
||||||
UserChangePasswordSerializer,
|
UserChangePasswordSerializer,
|
||||||
UserLoginSerializer,
|
UserLoginSerializer,
|
||||||
@@ -43,22 +40,6 @@ from ..serializers import (
|
|||||||
from ..tasks import send_email_async
|
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):
|
class UserProfileAPI(AsyncAPIView):
|
||||||
@method_decorator(ensure_csrf_cookie)
|
@method_decorator(ensure_csrf_cookie)
|
||||||
async def get(self, request, **kwargs):
|
async def get(self, request, **kwargs):
|
||||||
@@ -136,72 +117,6 @@ class AvatarUploadAPI(AsyncAPIView):
|
|||||||
return self.success("Succeeded")
|
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):
|
class UserLoginAPI(AsyncAPIView):
|
||||||
@validate_serializer(UserLoginSerializer)
|
@validate_serializer(UserLoginSerializer)
|
||||||
async def post(self, request):
|
async def post(self, request):
|
||||||
@@ -210,22 +125,10 @@ class UserLoginAPI(AsyncAPIView):
|
|||||||
if user:
|
if user:
|
||||||
if user.is_disabled:
|
if user.is_disabled:
|
||||||
return self.error("Your account has been disabled")
|
return self.error("Your account has been disabled")
|
||||||
if not user.two_factor_auth:
|
prev_login = user.last_login
|
||||||
prev_login = user.last_login
|
await auth.alogin(request, user)
|
||||||
await auth.alogin(request, user)
|
request.session["prev_login"] = datetime2str(prev_login) if prev_login else ""
|
||||||
request.session["prev_login"] = datetime2str(prev_login) if prev_login else ""
|
return self.success("Succeeded")
|
||||||
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")
|
|
||||||
else:
|
else:
|
||||||
return self.error("Invalid username or password")
|
return self.error("Invalid username or password")
|
||||||
|
|
||||||
@@ -284,11 +187,6 @@ class UserChangeEmailAPI(APIView):
|
|||||||
data = request.data
|
data = request.data
|
||||||
user = auth.authenticate(username=request.user.username, password=data["password"])
|
user = auth.authenticate(username=request.user.username, password=data["password"])
|
||||||
if user:
|
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()
|
data["new_email"] = data["new_email"].lower()
|
||||||
if User.objects.filter(email=data["new_email"]).exists():
|
if User.objects.filter(email=data["new_email"]).exists():
|
||||||
return self.error("The email is owned by other account")
|
return self.error("The email is owned by other account")
|
||||||
@@ -311,11 +209,6 @@ class UserChangePasswordAPI(APIView):
|
|||||||
username = request.user.username
|
username = request.user.username
|
||||||
user = auth.authenticate(username=username, password=data["old_password"])
|
user = auth.authenticate(username=username, password=data["old_password"])
|
||||||
if user:
|
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.set_password(data["new_password"])
|
||||||
user.save()
|
user.save()
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
@@ -373,7 +266,6 @@ class ResetPasswordAPI(APIView):
|
|||||||
if user.reset_password_token_expire_time < now():
|
if user.reset_password_token_expire_time < now():
|
||||||
return self.error("Token has expired")
|
return self.error("Token has expired")
|
||||||
user.reset_password_token = None
|
user.reset_password_token = None
|
||||||
user.two_factor_auth = False
|
|
||||||
user.set_password(data["password"])
|
user.set_password(data["password"])
|
||||||
user.save()
|
user.save()
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from ..views import (
|
|||||||
DashboardInfoAPI,
|
DashboardInfoAPI,
|
||||||
JudgeServerAPI,
|
JudgeServerAPI,
|
||||||
RandomUsernameAPI,
|
RandomUsernameAPI,
|
||||||
ReleaseNotesAPI,
|
|
||||||
SMTPTestAPI,
|
SMTPTestAPI,
|
||||||
TestCasePruneAPI,
|
TestCasePruneAPI,
|
||||||
WebsiteConfigAPI,
|
WebsiteConfigAPI,
|
||||||
@@ -18,6 +17,5 @@ urlpatterns = [
|
|||||||
path("random_user", RandomUsernameAPI.as_view()),
|
path("random_user", RandomUsernameAPI.as_view()),
|
||||||
path("judge_server", JudgeServerAPI.as_view()),
|
path("judge_server", JudgeServerAPI.as_view()),
|
||||||
path("prune_test_case", TestCasePruneAPI.as_view()),
|
path("prune_test_case", TestCasePruneAPI.as_view()),
|
||||||
path("versions", ReleaseNotesAPI.as_view()), # DEPRECATED: 前端未调用
|
|
||||||
path("dashboard_info", DashboardInfoAPI.as_view()),
|
path("dashboard_info", DashboardInfoAPI.as_view()),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import smtplib
|
import smtplib
|
||||||
import time
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import requests
|
|
||||||
from asgiref.sync import sync_to_async
|
from asgiref.sync import sync_to_async
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from requests.exceptions import RequestException
|
|
||||||
|
|
||||||
from account.decorators import super_admin_required
|
from account.decorators import super_admin_required
|
||||||
from account.models import User
|
from account.models import User
|
||||||
@@ -253,23 +249,6 @@ class TestCasePruneAPI(APIView):
|
|||||||
shutil.rmtree(test_case_dir, ignore_errors=True)
|
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):
|
class DashboardInfoAPI(AsyncAPIView):
|
||||||
async def get(self, request):
|
async def get(self, request):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# This file was autogenerated by uv via the following command:
|
# 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 \
|
annotated-types==0.7.0 \
|
||||||
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
|
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
|
||||||
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
|
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
|
||||||
@@ -25,7 +25,6 @@ certifi==2026.4.22 \
|
|||||||
# via
|
# via
|
||||||
# httpcore
|
# httpcore
|
||||||
# httpx
|
# httpx
|
||||||
# requests
|
|
||||||
# sentry-sdk
|
# sentry-sdk
|
||||||
channels==4.3.2 \
|
channels==4.3.2 \
|
||||||
--hash=sha256:f2bb6bfb73ad7fb4705041d07613c7b4e69528f01ef8cb9fb6c21d9295f15667 \
|
--hash=sha256:f2bb6bfb73ad7fb4705041d07613c7b4e69528f01ef8cb9fb6c21d9295f15667 \
|
||||||
@@ -37,62 +36,6 @@ channels-redis==4.3.0 \
|
|||||||
--hash=sha256:48f3e902ae2d5fef7080215524f3b4a1d3cea4e304150678f867a1a822c0d9f5 \
|
--hash=sha256:48f3e902ae2d5fef7080215524f3b4a1d3cea4e304150678f867a1a822c0d9f5 \
|
||||||
--hash=sha256:740ee7b54f0e28cf2264a940a24453d3f00526a96931f911fcb69228ef245dd2
|
--hash=sha256:740ee7b54f0e28cf2264a940a24453d3f00526a96931f911fcb69228ef245dd2
|
||||||
# via onlinejudge
|
# 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 \
|
click==8.4.1 \
|
||||||
--hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \
|
--hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \
|
||||||
--hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96
|
--hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96
|
||||||
@@ -102,7 +45,6 @@ colorama==0.4.6 ; sys_platform == 'win32' \
|
|||||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||||
# via
|
# via
|
||||||
# click
|
# click
|
||||||
# qrcode
|
|
||||||
# tqdm
|
# tqdm
|
||||||
# uvicorn
|
# uvicorn
|
||||||
distro==1.9.0 \
|
distro==1.9.0 \
|
||||||
@@ -119,9 +61,6 @@ django==6.0.4 \
|
|||||||
# djangorestframework
|
# djangorestframework
|
||||||
# onlinejudge
|
# onlinejudge
|
||||||
# sentry-sdk
|
# sentry-sdk
|
||||||
django-dbconn-retry==0.3.1 \
|
|
||||||
--hash=sha256:d4b64d915440c3e5902ef8edf836366a6f4c4f027d34902135d7335233d6dbba
|
|
||||||
# via onlinejudge
|
|
||||||
django-dramatiq==0.15.0 \
|
django-dramatiq==0.15.0 \
|
||||||
--hash=sha256:23f0bc418a860952adbf822c4aa3b9c46c51d3d9f50be0a8ed3d19a53380df1d \
|
--hash=sha256:23f0bc418a860952adbf822c4aa3b9c46c51d3d9f50be0a8ed3d19a53380df1d \
|
||||||
--hash=sha256:e3cf1b2ac288fe4a7aa198c9450fe242ed312df8850f3f9e18ce01b8acc78b96
|
--hash=sha256:e3cf1b2ac288fe4a7aa198c9450fe242ed312df8850f3f9e18ce01b8acc78b96
|
||||||
@@ -188,14 +127,15 @@ httptools==0.8.0 \
|
|||||||
httpx==0.28.1 \
|
httpx==0.28.1 \
|
||||||
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
|
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
|
||||||
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
|
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
|
||||||
# via openai
|
# via
|
||||||
|
# onlinejudge
|
||||||
|
# openai
|
||||||
idna==3.13 \
|
idna==3.13 \
|
||||||
--hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \
|
--hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \
|
||||||
--hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3
|
--hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3
|
||||||
# via
|
# via
|
||||||
# anyio
|
# anyio
|
||||||
# httpx
|
# httpx
|
||||||
# requests
|
|
||||||
jieba==0.42.1 \
|
jieba==0.42.1 \
|
||||||
--hash=sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2
|
--hash=sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2
|
||||||
# via onlinejudge
|
# via onlinejudge
|
||||||
@@ -309,10 +249,6 @@ openai==2.34.0 \
|
|||||||
--hash=sha256:828b4efcbb126352c2b5eb97d33ae890c92a71ab72511aefc1b7fe64aeccb07b \
|
--hash=sha256:828b4efcbb126352c2b5eb97d33ae890c92a71ab72511aefc1b7fe64aeccb07b \
|
||||||
--hash=sha256:c996a71b1a210f3569844572ad4c609307e978515fb76877cf449b72596e549e
|
--hash=sha256:c996a71b1a210f3569844572ad4c609307e978515fb76877cf449b72596e549e
|
||||||
# via onlinejudge
|
# via onlinejudge
|
||||||
otpauth==2.2.1 \
|
|
||||||
--hash=sha256:169a7adbd715fca687f6a66d02ccdbefc229fb49f8a634b958d286f908134d59 \
|
|
||||||
--hash=sha256:b7eabe0ed91cb67eb3054b7f517e4b4a7495fb30eaf2951897d41c8feef5de73
|
|
||||||
# via onlinejudge
|
|
||||||
packaging==26.2 \
|
packaging==26.2 \
|
||||||
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
||||||
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
||||||
@@ -540,20 +476,31 @@ pyyaml==6.0.3 \
|
|||||||
--hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
|
--hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
|
||||||
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
|
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
qrcode==8.2 \
|
|
||||||
--hash=sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f \
|
|
||||||
--hash=sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c
|
|
||||||
# via onlinejudge
|
|
||||||
redis==7.4.0 \
|
redis==7.4.0 \
|
||||||
--hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \
|
--hash=sha256:64a6ea7bf567ad43c964d2c30d82853f8df927c5c9017766c55a1d1ed95d18ad \
|
||||||
--hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec
|
--hash=sha256:a9c74a5c893a5ef8455a5adb793a31bb70feb821c86eccb62eebef5a19c429ec
|
||||||
# via
|
# via
|
||||||
# channels-redis
|
# channels-redis
|
||||||
# django-redis
|
# django-redis
|
||||||
requests==2.34.2 \
|
ruff==0.15.12 \
|
||||||
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
|
--hash=sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b \
|
||||||
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
|
--hash=sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33 \
|
||||||
# via onlinejudge
|
--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 \
|
sentry-sdk==2.59.0 \
|
||||||
--hash=sha256:abcf65ee9a9d9cdebf9ad369782408ecca9c1c792686ef06ba34f5ab233527fe \
|
--hash=sha256:abcf65ee9a9d9cdebf9ad369782408ecca9c1c792686ef06ba34f5ab233527fe \
|
||||||
--hash=sha256:cd265808ef8bf3f3edf69b527c0a0b2b6b1322762679e55b8987db2e9584aec1
|
--hash=sha256:cd265808ef8bf3f3edf69b527c0a0b2b6b1322762679e55b8987db2e9584aec1
|
||||||
@@ -643,9 +590,7 @@ tzdata==2026.2 ; sys_platform == 'win32' \
|
|||||||
urllib3==2.6.3 \
|
urllib3==2.6.3 \
|
||||||
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
|
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
|
||||||
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
|
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
|
||||||
# via
|
# via sentry-sdk
|
||||||
# requests
|
|
||||||
# sentry-sdk
|
|
||||||
uvicorn==0.48.0 \
|
uvicorn==0.48.0 \
|
||||||
--hash=sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad \
|
--hash=sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad \
|
||||||
--hash=sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37
|
--hash=sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import logging
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
import requests
|
import httpx
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -73,7 +73,8 @@ class DispatcherBase(object):
|
|||||||
if data:
|
if data:
|
||||||
kwargs["json"] = data
|
kwargs["json"] = data
|
||||||
try:
|
try:
|
||||||
return requests.post(url, **kwargs).json()
|
# timeout=None 保持与原 requests 实现一致:判题请求是同步等结果的,不能被默认超时打断
|
||||||
|
return httpx.post(url, timeout=None, **kwargs).json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ VENDOR_APPS = [
|
|||||||
"rest_framework",
|
"rest_framework",
|
||||||
"channels",
|
"channels",
|
||||||
"django_dramatiq",
|
"django_dramatiq",
|
||||||
"django_dbconn_retry",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,20 +9,17 @@ dependencies = [
|
|||||||
"channels-redis>=4.3.0,<5",
|
"channels-redis>=4.3.0,<5",
|
||||||
"uvicorn[standard]>=0.34.0,<1",
|
"uvicorn[standard]>=0.34.0,<1",
|
||||||
"django>=6.0.4,<6.1",
|
"django>=6.0.4,<6.1",
|
||||||
"django-dbconn-retry>=0.3.1,<0.4",
|
|
||||||
"django-dramatiq>=0.15.0,<0.16",
|
"django-dramatiq>=0.15.0,<0.16",
|
||||||
"django-redis>=6.0.0,<7",
|
"django-redis>=6.0.0,<7",
|
||||||
"djangorestframework>=3.17.1,<4",
|
"djangorestframework>=3.17.1,<4",
|
||||||
"dramatiq>=2.1.0,<3",
|
"dramatiq>=2.1.0,<3",
|
||||||
"gunicorn>=26.0.0,<27",
|
"gunicorn>=26.0.0,<27",
|
||||||
|
"httpx>=0.28.1,<1",
|
||||||
"openai>=2.34.0,<3",
|
"openai>=2.34.0,<3",
|
||||||
"otpauth>=2.2.1,<3",
|
|
||||||
"pillow>=12.2.0,<13",
|
"pillow>=12.2.0,<13",
|
||||||
"psycopg>=3.3.4,<4",
|
"psycopg>=3.3.4,<4",
|
||||||
"psycopg-binary>=3.3.4,<4",
|
"psycopg-binary>=3.3.4,<4",
|
||||||
"python-dateutil>=2.9.0.post0,<3",
|
"python-dateutil>=2.9.0.post0,<3",
|
||||||
"qrcode>=8.2,<9",
|
|
||||||
"requests>=2.33.1,<3",
|
|
||||||
"sentry-sdk[django]>=2.0.0,<3",
|
"sentry-sdk[django]>=2.0.0,<3",
|
||||||
"tree-sitter>=0.25.2",
|
"tree-sitter>=0.25.2",
|
||||||
"tree-sitter-c>=0.24.2",
|
"tree-sitter-c>=0.24.2",
|
||||||
|
|||||||
115
uv.lock
generated
115
uv.lock
generated
@@ -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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.4.1"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "django-dramatiq"
|
name = "django-dramatiq"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@@ -455,21 +386,18 @@ dependencies = [
|
|||||||
{ name = "channels" },
|
{ name = "channels" },
|
||||||
{ name = "channels-redis" },
|
{ name = "channels-redis" },
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
{ name = "django-dbconn-retry" },
|
|
||||||
{ name = "django-dramatiq" },
|
{ name = "django-dramatiq" },
|
||||||
{ name = "django-redis" },
|
{ name = "django-redis" },
|
||||||
{ name = "djangorestframework" },
|
{ name = "djangorestframework" },
|
||||||
{ name = "dramatiq" },
|
{ name = "dramatiq" },
|
||||||
{ name = "gunicorn" },
|
{ name = "gunicorn" },
|
||||||
|
{ name = "httpx" },
|
||||||
{ name = "jieba" },
|
{ name = "jieba" },
|
||||||
{ name = "openai" },
|
{ name = "openai" },
|
||||||
{ name = "otpauth" },
|
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
{ name = "psycopg" },
|
{ name = "psycopg" },
|
||||||
{ name = "psycopg-binary" },
|
{ name = "psycopg-binary" },
|
||||||
{ name = "python-dateutil" },
|
{ name = "python-dateutil" },
|
||||||
{ name = "qrcode" },
|
|
||||||
{ name = "requests" },
|
|
||||||
{ name = "sentry-sdk", extra = ["django"] },
|
{ name = "sentry-sdk", extra = ["django"] },
|
||||||
{ name = "tree-sitter" },
|
{ name = "tree-sitter" },
|
||||||
{ name = "tree-sitter-c" },
|
{ name = "tree-sitter-c" },
|
||||||
@@ -489,21 +417,18 @@ requires-dist = [
|
|||||||
{ name = "channels", specifier = ">=4.3.2,<5" },
|
{ name = "channels", specifier = ">=4.3.2,<5" },
|
||||||
{ name = "channels-redis", specifier = ">=4.3.0,<5" },
|
{ name = "channels-redis", specifier = ">=4.3.0,<5" },
|
||||||
{ name = "django", specifier = ">=6.0.4,<6.1" },
|
{ 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-dramatiq", specifier = ">=0.15.0,<0.16" },
|
||||||
{ name = "django-redis", specifier = ">=6.0.0,<7" },
|
{ name = "django-redis", specifier = ">=6.0.0,<7" },
|
||||||
{ name = "djangorestframework", specifier = ">=3.17.1,<4" },
|
{ name = "djangorestframework", specifier = ">=3.17.1,<4" },
|
||||||
{ name = "dramatiq", specifier = ">=2.1.0,<3" },
|
{ name = "dramatiq", specifier = ">=2.1.0,<3" },
|
||||||
{ name = "gunicorn", specifier = ">=26.0.0,<27" },
|
{ name = "gunicorn", specifier = ">=26.0.0,<27" },
|
||||||
|
{ name = "httpx", specifier = ">=0.28.1,<1" },
|
||||||
{ name = "jieba", specifier = ">=0.42.1" },
|
{ name = "jieba", specifier = ">=0.42.1" },
|
||||||
{ name = "openai", specifier = ">=2.34.0,<3" },
|
{ name = "openai", specifier = ">=2.34.0,<3" },
|
||||||
{ name = "otpauth", specifier = ">=2.2.1,<3" },
|
|
||||||
{ name = "pillow", specifier = ">=12.2.0,<13" },
|
{ name = "pillow", specifier = ">=12.2.0,<13" },
|
||||||
{ name = "psycopg", specifier = ">=3.3.4,<4" },
|
{ name = "psycopg", specifier = ">=3.3.4,<4" },
|
||||||
{ name = "psycopg-binary", specifier = ">=3.3.4,<4" },
|
{ name = "psycopg-binary", specifier = ">=3.3.4,<4" },
|
||||||
{ name = "python-dateutil", specifier = ">=2.9.0.post0,<3" },
|
{ 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 = "sentry-sdk", extras = ["django"], specifier = ">=2.0.0,<3" },
|
||||||
{ name = "tree-sitter", specifier = ">=0.25.2" },
|
{ name = "tree-sitter", specifier = ">=0.25.2" },
|
||||||
{ name = "tree-sitter-c", specifier = ">=0.24.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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.2"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "redis"
|
name = "redis"
|
||||||
version = "7.4.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.15.12"
|
version = "0.15.12"
|
||||||
|
|||||||
Reference in New Issue
Block a user