update deps
This commit is contained in:
@@ -12,7 +12,7 @@ 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 OtpAuth
|
||||
from otpauth import TOTP
|
||||
|
||||
from options.options import SysOptions
|
||||
from problem.models import Problem
|
||||
@@ -42,6 +42,22 @@ 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(APIView):
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
def get(self, request, **kwargs):
|
||||
@@ -142,9 +158,7 @@ class TwoFactorAuthAPI(APIView):
|
||||
|
||||
label = f"{SysOptions.website_name_shortcut}:{user.username}"
|
||||
image = qrcode.make(
|
||||
OtpAuth(token).to_uri(
|
||||
"totp", label, SysOptions.website_name.replace(" ", "")
|
||||
)
|
||||
_totp_uri(token, label, SysOptions.website_name.replace(" ", ""))
|
||||
)
|
||||
return self.success(img2base64(image))
|
||||
|
||||
@@ -156,7 +170,7 @@ class TwoFactorAuthAPI(APIView):
|
||||
"""
|
||||
code = request.data["code"]
|
||||
user = request.user
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
if _valid_totp(user.tfa_token, code):
|
||||
user.two_factor_auth = True
|
||||
user.save()
|
||||
return self.success("Succeeded")
|
||||
@@ -170,7 +184,7 @@ class TwoFactorAuthAPI(APIView):
|
||||
user = request.user
|
||||
if not user.two_factor_auth:
|
||||
return self.error("2FA is already turned off")
|
||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||
if _valid_totp(user.tfa_token, code):
|
||||
user.two_factor_auth = False
|
||||
user.save()
|
||||
return self.success("Succeeded")
|
||||
@@ -219,7 +233,7 @@ class UserLoginAPI(APIView):
|
||||
if user.two_factor_auth and "tfa_code" not in data:
|
||||
return self.error("tfa_required")
|
||||
|
||||
if OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
|
||||
if _valid_totp(user.tfa_token, data["tfa_code"]):
|
||||
prev_login = user.last_login
|
||||
auth.login(request, user)
|
||||
request.session["prev_login"] = (
|
||||
@@ -294,7 +308,7 @@ class UserChangeEmailAPI(APIView):
|
||||
if user.two_factor_auth:
|
||||
if "tfa_code" not in data:
|
||||
return self.error("tfa_required")
|
||||
if not OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
|
||||
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():
|
||||
@@ -320,7 +334,7 @@ class UserChangePasswordAPI(APIView):
|
||||
if user.two_factor_auth:
|
||||
if "tfa_code" not in data:
|
||||
return self.error("tfa_required")
|
||||
if not OtpAuth(user.tfa_token).valid_totp(data["tfa_code"]):
|
||||
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()
|
||||
|
||||
@@ -1,76 +1,68 @@
|
||||
annotated-types==0.7.0
|
||||
anyio==4.12.0
|
||||
asgiref==3.11.0
|
||||
attrs==25.4.0
|
||||
anyio==4.13.0
|
||||
asgiref==3.11.1
|
||||
attrs==26.1.0
|
||||
autobahn==25.12.2
|
||||
automat==25.4.16
|
||||
cbor2==5.7.1
|
||||
certifi==2025.11.12
|
||||
cbor2==6.0.1
|
||||
certifi==2026.4.22
|
||||
cffi==2.0.0
|
||||
channels==4.3.2
|
||||
channels-redis==4.3.0
|
||||
charset-normalizer==3.4.4
|
||||
charset-normalizer==3.4.7
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
constantly==23.10.4
|
||||
coverage==6.5.0
|
||||
cryptography==46.0.3
|
||||
cryptography==48.0.0
|
||||
daphne==4.2.1
|
||||
distro==1.9.0
|
||||
django==6.0
|
||||
django-cas-ng==5.0.1
|
||||
django-dbconn-retry==0.1.8
|
||||
django-dramatiq==0.13.0
|
||||
django-redis==5.4.0
|
||||
djangorestframework==3.16.0
|
||||
dramatiq==1.17.0
|
||||
entrypoints==0.4
|
||||
envelopes==0.4
|
||||
flake8==7.0.0
|
||||
flake8-coding==1.3.2
|
||||
flake8-quotes==3.3.2
|
||||
gunicorn==22.0.0
|
||||
django==6.0.4
|
||||
django-cas-ng==5.1.1
|
||||
django-dbconn-retry==0.3.1
|
||||
django-dramatiq==0.15.0
|
||||
django-redis==6.0.0
|
||||
djangorestframework==3.17.1
|
||||
dramatiq==2.1.0
|
||||
gunicorn==26.0.0
|
||||
h11==0.16.0
|
||||
httpcore==1.0.9
|
||||
httpx==0.28.1
|
||||
hyperlink==21.0.0
|
||||
idna==3.11
|
||||
idna==3.13
|
||||
incremental==24.11.0
|
||||
jiter==0.12.0
|
||||
jsonfield==3.1.0
|
||||
lxml==6.0.2
|
||||
mccabe==0.7.0
|
||||
jiter==0.14.0
|
||||
lxml==6.1.0
|
||||
msgpack==1.1.2
|
||||
openai==2.14.0
|
||||
otpauth==1.0.1
|
||||
packaging==25.0
|
||||
pillow==10.2.0
|
||||
prometheus-client==0.23.1
|
||||
psycopg==3.2.9
|
||||
psycopg-binary==3.2.9
|
||||
openai==2.34.0
|
||||
otpauth==2.2.1
|
||||
packaging==26.2
|
||||
pillow==12.2.0
|
||||
psycopg==3.3.4
|
||||
psycopg-binary==3.3.4
|
||||
py-ubjson==0.16.1
|
||||
pyasn1==0.6.1
|
||||
pyasn1==0.6.3
|
||||
pyasn1-modules==0.4.2
|
||||
pycodestyle==2.11.1
|
||||
pycparser==2.23
|
||||
pydantic==2.12.5
|
||||
pydantic-core==2.41.5
|
||||
pyflakes==3.2.0
|
||||
pyopenssl==25.3.0
|
||||
python-cas==1.7.1
|
||||
python-dateutil==2.8.2
|
||||
pycparser==3.0 ; implementation_name != 'PyPy'
|
||||
pydantic==2.13.3
|
||||
pydantic-core==2.46.3
|
||||
pyopenssl==26.2.0
|
||||
python-cas==1.7.2
|
||||
python-dateutil==2.9.0.post0
|
||||
qrcode==8.2
|
||||
raven==6.10.0
|
||||
redis==7.1.0
|
||||
requests==2.32.5
|
||||
redis==7.4.0
|
||||
requests==2.33.1
|
||||
sentry-sdk==2.59.0
|
||||
service-identity==24.2.0
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
sqlparse==0.5.5
|
||||
tqdm==4.67.1
|
||||
tqdm==4.67.3
|
||||
twisted==25.5.0
|
||||
txaio==25.12.2
|
||||
typing-extensions==4.15.0
|
||||
typing-inspection==0.4.2
|
||||
ujson==5.11.0
|
||||
urllib3==2.6.2
|
||||
xlsxwriter==3.2.0
|
||||
zope-interface==8.1.1
|
||||
tzdata==2026.2 ; sys_platform == 'win32'
|
||||
u-msgpack-python==2.8.0 ; platform_python_implementation != 'CPython'
|
||||
ujson==5.12.0
|
||||
urllib3==2.6.3
|
||||
xlsxwriter==3.2.9
|
||||
zope-interface==8.4
|
||||
|
||||
@@ -10,9 +10,14 @@ For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import raven
|
||||
from copy import deepcopy
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||
|
||||
from utils.shortcuts import get_env
|
||||
|
||||
production_env = get_env("OJ_ENV", "dev") == "production"
|
||||
@@ -40,9 +45,6 @@ VENDOR_APPS = [
|
||||
"django_dbconn_retry",
|
||||
]
|
||||
|
||||
if production_env:
|
||||
VENDOR_APPS.append("raven.contrib.django.raven_compat")
|
||||
|
||||
|
||||
LOCAL_APPS = [
|
||||
"account",
|
||||
@@ -148,7 +150,19 @@ HITOKOTO_DIR = os.path.join(DATA_DIR, "hitokoto")
|
||||
STATICFILES_DIRS = [os.path.join(DATA_DIR, "public")]
|
||||
|
||||
|
||||
LOGGING_HANDLERS = ["console", "sentry"] if production_env else ["console"]
|
||||
SENTRY_DSN = get_env("SENTRY_DSN")
|
||||
if production_env and SENTRY_DSN:
|
||||
sentry_sdk.init(
|
||||
dsn=SENTRY_DSN,
|
||||
integrations=[
|
||||
DjangoIntegration(),
|
||||
LoggingIntegration(level=logging.INFO, event_level=logging.ERROR),
|
||||
],
|
||||
send_default_pii=False,
|
||||
)
|
||||
|
||||
|
||||
LOGGING_HANDLERS = ["console"]
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
@@ -164,11 +178,6 @@ LOGGING = {
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "standard",
|
||||
},
|
||||
"sentry": {
|
||||
"level": "ERROR",
|
||||
"class": "raven.contrib.django.raven_compat.handlers.SentryHandler",
|
||||
"formatter": "standard",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"django.request": {
|
||||
@@ -256,10 +265,6 @@ DRAMATIQ_RESULT_BACKEND = {
|
||||
"MIDDLEWARE_OPTIONS": {"result_ttl": None},
|
||||
}
|
||||
|
||||
RAVEN_CONFIG = {
|
||||
"dsn": "https://b200023b8aed4d708fb593c5e0a6ad3d:1fddaba168f84fcf97e0d549faaeaff0@sentry.io/263057"
|
||||
}
|
||||
|
||||
IP_HEADER = "HTTP_X_REAL_IP"
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
@@ -5,29 +5,26 @@ description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"channels>=4.2.0",
|
||||
"channels-redis>=4.2.0",
|
||||
"daphne>=4.1.2",
|
||||
"django>=5.2.3",
|
||||
"django-cas-ng==5.0.1",
|
||||
"django-dbconn-retry==0.1.8",
|
||||
"django-dramatiq==0.13.0",
|
||||
"django-redis==5.4.0",
|
||||
"djangorestframework==3.16.0",
|
||||
"dramatiq==1.17.0",
|
||||
"entrypoints==0.4",
|
||||
"envelopes==0.4",
|
||||
"gunicorn==22.0.0",
|
||||
"jsonfield==3.1.0",
|
||||
"openai>=1.108.1",
|
||||
"otpauth==1.0.1",
|
||||
"pillow==10.2.0",
|
||||
"psycopg==3.2.9",
|
||||
"psycopg-binary==3.2.9",
|
||||
"python-dateutil==2.8.2",
|
||||
"qrcode==8.2",
|
||||
"raven==6.10.0",
|
||||
"xlsxwriter==3.2.0",
|
||||
"channels>=4.3.2,<5",
|
||||
"channels-redis>=4.3.0,<5",
|
||||
"daphne>=4.2.1,<5",
|
||||
"django>=6.0.4,<6.1",
|
||||
"django-cas-ng>=5.1.1,<6",
|
||||
"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",
|
||||
"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",
|
||||
"sentry-sdk[django]>=2.0.0,<3",
|
||||
"xlsxwriter>=3.2.9,<4",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
|
||||
@@ -2,10 +2,12 @@ import os
|
||||
import random
|
||||
import re
|
||||
from base64 import b64encode
|
||||
from email.utils import formataddr
|
||||
from io import BytesIO
|
||||
|
||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||
from django.utils.crypto import get_random_string
|
||||
from envelopes import Envelope
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
|
||||
def rand_str(length=32, type="lower_hex"):
|
||||
@@ -56,21 +58,29 @@ def datetime2str(value, format="iso-8601"):
|
||||
return value
|
||||
return value.strftime(format)
|
||||
|
||||
|
||||
def natural_sort_key(s, _nsre=re.compile(r"(\d+)")):
|
||||
return [int(text) if text.isdigit() else text.lower()
|
||||
for text in re.split(_nsre, s)]
|
||||
|
||||
|
||||
def send_email(smtp_config, from_name, to_email, to_name, subject, content):
|
||||
envelope = Envelope(from_addr=(smtp_config["email"], from_name),
|
||||
to_addr=(to_email, to_name),
|
||||
subject=subject,
|
||||
html_body=content)
|
||||
return envelope.send(smtp_config["server"],
|
||||
login=smtp_config["email"],
|
||||
password=smtp_config["password"],
|
||||
port=smtp_config["port"],
|
||||
tls=smtp_config["tls"])
|
||||
connection = get_connection(
|
||||
host=smtp_config["server"],
|
||||
port=smtp_config["port"],
|
||||
username=smtp_config["email"],
|
||||
password=smtp_config["password"],
|
||||
use_tls=smtp_config["tls"],
|
||||
)
|
||||
message = EmailMultiAlternatives(
|
||||
subject=subject,
|
||||
body=strip_tags(content),
|
||||
from_email=formataddr((from_name, smtp_config["email"])),
|
||||
to=[formataddr((to_name, to_email))],
|
||||
connection=connection,
|
||||
)
|
||||
message.attach_alternative(content, "text/html")
|
||||
return message.send()
|
||||
|
||||
|
||||
def get_env(name, default=""):
|
||||
|
||||
Reference in New Issue
Block a user