- 新增 ConfirmModal 组件,为6处危险操作添加二次确认弹窗 (禁用团队/用户/成员、删除视频×3处) - 所有秒数显示统一为千位分隔符+s后缀(如 36,000s) - 修复 modal/drawer 在 input 中拖拽导致误关闭的 bug (onClick → onMouseDown + e.target === e.currentTarget) - 团队模型完善:三种角色(超管/团管/成员)、四层额度检查、 团管成员管理页、超管团队管理页 - 关闭公开注册,所有账号由管理员创建 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
from rest_framework import status
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
from django.contrib.auth import authenticate, get_user_model
|
|
from django.utils import timezone
|
|
from django.db.models import Sum
|
|
|
|
from .serializers import UserSerializer
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([AllowAny])
|
|
def register_view(request):
|
|
"""POST /api/v1/auth/register — disabled, all accounts created by admins."""
|
|
return Response(
|
|
{'error': 'registration_disabled', 'message': '公开注册已关闭,请联系管理员'},
|
|
status=status.HTTP_403_FORBIDDEN,
|
|
)
|
|
|
|
|
|
@api_view(['GET', 'POST'])
|
|
@permission_classes([AllowAny])
|
|
def login_view(request):
|
|
"""GET/POST /api/v1/auth/login"""
|
|
if request.method == 'GET':
|
|
return Response({'message': 'Use POST to login', 'required_fields': ['username', 'password']})
|
|
|
|
username = request.data.get('username', '')
|
|
password = request.data.get('password', '')
|
|
|
|
# Try authenticate with username first, then email
|
|
user = authenticate(username=username, password=password)
|
|
if user is None:
|
|
# Try email login
|
|
try:
|
|
user_by_email = User.objects.get(email=username)
|
|
user = authenticate(username=user_by_email.username, password=password)
|
|
except User.DoesNotExist:
|
|
pass
|
|
|
|
if user is None:
|
|
return Response(
|
|
{'error': 'invalid_credentials', 'message': '用户名或密码错误'},
|
|
status=status.HTTP_401_UNAUTHORIZED
|
|
)
|
|
|
|
refresh = RefreshToken.for_user(user)
|
|
return Response({
|
|
'user': UserSerializer(user).data,
|
|
'tokens': {
|
|
'access': str(refresh.access_token),
|
|
'refresh': str(refresh),
|
|
}
|
|
})
|
|
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsAuthenticated])
|
|
def me_view(request):
|
|
"""GET /api/v1/auth/me — returns role, team info, and quota."""
|
|
user = request.user
|
|
today = timezone.now().date()
|
|
first_of_month = today.replace(day=1)
|
|
|
|
daily_seconds_used = user.generation_records.filter(
|
|
created_at__date=today
|
|
).aggregate(total=Sum('seconds_consumed'))['total'] or 0
|
|
|
|
monthly_seconds_used = user.generation_records.filter(
|
|
created_at__date__gte=first_of_month
|
|
).aggregate(total=Sum('seconds_consumed'))['total'] or 0
|
|
|
|
data = UserSerializer(user).data
|
|
data['quota'] = {
|
|
'daily_seconds_limit': user.daily_seconds_limit,
|
|
'daily_seconds_used': daily_seconds_used,
|
|
'monthly_seconds_limit': user.monthly_seconds_limit,
|
|
'monthly_seconds_used': monthly_seconds_used,
|
|
}
|
|
|
|
# Team info
|
|
team = user.team
|
|
if team:
|
|
# Team monthly consumption
|
|
from apps.generation.models import GenerationRecord
|
|
team_monthly_used = GenerationRecord.objects.filter(
|
|
user__team=team,
|
|
created_at__date__gte=first_of_month,
|
|
).aggregate(total=Sum('seconds_consumed'))['total'] or 0
|
|
|
|
data['team'] = {
|
|
'id': team.id,
|
|
'name': team.name,
|
|
'total_seconds_pool': team.total_seconds_pool,
|
|
'total_seconds_used': team.total_seconds_used,
|
|
'remaining_seconds': team.remaining_seconds,
|
|
'monthly_seconds_limit': team.monthly_seconds_limit,
|
|
'monthly_seconds_used': team_monthly_used,
|
|
'is_active': team.is_active,
|
|
}
|
|
data['team_disabled'] = not team.is_active
|
|
else:
|
|
data['team'] = None
|
|
data['team_disabled'] = False
|
|
|
|
return Response(data)
|