feat: add JWT auth foundation - /api/v1/ with admin login, refresh, me, health endpoints
- Add djangorestframework-simplejwt==5.3.1 to requirements-docker.txt - Configure REST_FRAMEWORK with JWTAuthentication and SIMPLE_JWT settings - Create admin_api Django app with AdminLoginView, MeView, HealthView - Wire /api/v1/ routes without touching existing /api/ mobile endpoints - Resolve pre-existing events migration conflict (0010_merge) - Superuser admin created for initial authentication
This commit is contained in:
0
admin_api/__init__.py
Normal file
0
admin_api/__init__.py
Normal file
4
admin_api/apps.py
Normal file
4
admin_api/apps.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from django.apps import AppConfig
|
||||
class AdminApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'admin_api'
|
||||
18
admin_api/serializers.py
Normal file
18
admin_api/serializers.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
name = serializers.SerializerMethodField()
|
||||
role = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'email', 'username', 'name', 'role']
|
||||
def get_name(self, obj):
|
||||
return f"{obj.first_name} {obj.last_name}".strip() or obj.username
|
||||
def get_role(self, obj):
|
||||
if obj.is_superuser:
|
||||
return 'superadmin'
|
||||
if obj.is_staff:
|
||||
return 'admin'
|
||||
return getattr(obj, 'role', 'user')
|
||||
10
admin_api/urls.py
Normal file
10
admin_api/urls.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.urls import path
|
||||
from rest_framework_simplejwt.views import TokenRefreshView
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/auth/login/', views.AdminLoginView.as_view(), name='admin_login'),
|
||||
path('auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('auth/me/', views.MeView.as_view(), name='auth_me'),
|
||||
path('health/', views.HealthView.as_view(), name='health'),
|
||||
]
|
||||
52
admin_api/views.py
Normal file
52
admin_api/views.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from django.contrib.auth import authenticate, get_user_model
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework import status
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework_simplejwt.views import TokenRefreshView
|
||||
from django.db import connection
|
||||
from .serializers import UserSerializer
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class AdminLoginView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
def post(self, request):
|
||||
identifier = request.data.get('username') or request.data.get('email')
|
||||
password = request.data.get('password')
|
||||
if not identifier or not password:
|
||||
return Response({'error': 'username/email and password required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
# Try username first, then email
|
||||
user = authenticate(request, username=identifier, password=password)
|
||||
if not user:
|
||||
try:
|
||||
u = User.objects.get(email=identifier)
|
||||
user = authenticate(request, username=u.username, password=password)
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
if not user:
|
||||
return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
if not user.is_active:
|
||||
return Response({'error': 'Account is disabled'}, status=status.HTTP_403_FORBIDDEN)
|
||||
refresh = RefreshToken.for_user(user)
|
||||
return Response({
|
||||
'access': str(refresh.access_token),
|
||||
'refresh': str(refresh),
|
||||
'user': UserSerializer(user).data,
|
||||
})
|
||||
|
||||
class MeView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
def get(self, request):
|
||||
return Response({'user': UserSerializer(request.user).data})
|
||||
|
||||
class HealthView(APIView):
|
||||
permission_classes = [AllowAny]
|
||||
def get(self, request):
|
||||
try:
|
||||
connection.ensure_connection()
|
||||
db_status = 'ok'
|
||||
except Exception:
|
||||
db_status = 'error'
|
||||
return Response({'status': 'ok', 'db': db_status})
|
||||
Reference in New Issue
Block a user