Add JWT auth with per-user data isolation and account settings.
Users can register, log in, and manage profile/password in a personal account page; server data is scoped by owner across contacts, maps, tags, and import. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -196,7 +196,7 @@ def parse_upload_file(file):
|
||||
return None, 'Поддерживаются только CSV, JSON и vCard (.vcf) файлы.'
|
||||
|
||||
|
||||
def import_contacts_from_rows(rows):
|
||||
def import_contacts_from_rows(rows, owner=None):
|
||||
created = 0
|
||||
skipped = 0
|
||||
errors = []
|
||||
@@ -212,16 +212,18 @@ def import_contacts_from_rows(rows):
|
||||
errors.append(f'Строка {i + 1}: отсутствует поле "name"')
|
||||
skipped += 1
|
||||
continue
|
||||
Contact.objects.get_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'email': str(row.get('email') or '').strip(),
|
||||
'phone': str(row.get('phone') or '').strip(),
|
||||
'organization': str(row.get('organization') or '').strip(),
|
||||
'position': str(row.get('position') or '').strip(),
|
||||
'notes': str(row.get('notes') or '').strip(),
|
||||
},
|
||||
)
|
||||
lookup = {'name': name}
|
||||
defaults = {
|
||||
'email': str(row.get('email') or '').strip(),
|
||||
'phone': str(row.get('phone') or '').strip(),
|
||||
'organization': str(row.get('organization') or '').strip(),
|
||||
'position': str(row.get('position') or '').strip(),
|
||||
'notes': str(row.get('notes') or '').strip(),
|
||||
}
|
||||
if owner is not None:
|
||||
lookup['owner'] = owner
|
||||
defaults['owner'] = owner
|
||||
Contact.objects.get_or_create(**lookup, defaults=defaults)
|
||||
created += 1
|
||||
return {
|
||||
'total': len(rows),
|
||||
|
||||
@@ -3,5 +3,5 @@ from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('import/', views.import_contacts, name='import-contacts'),
|
||||
path('import/', views.ImportContactsView.as_view(), name='import-contacts'),
|
||||
]
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from core.access import use_jwt_auth
|
||||
from core.drf_mixins import JwtAuthMixin
|
||||
from .services import parse_upload_file, import_contacts_from_rows
|
||||
|
||||
|
||||
@api_view(['POST'])
|
||||
def import_contacts(request):
|
||||
file = request.FILES.get('file')
|
||||
if not file:
|
||||
return Response({'error': 'Файл не передан.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
rows, error = parse_upload_file(file)
|
||||
if error:
|
||||
return Response({'error': error}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(import_contacts_from_rows(rows))
|
||||
except Exception as e:
|
||||
return Response({'error': f'Ошибка разбора файла: {e}'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
class ImportContactsView(JwtAuthMixin, APIView):
|
||||
def post(self, request):
|
||||
file = request.FILES.get('file')
|
||||
if not file:
|
||||
return Response({'error': 'Файл не передан.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
rows, error = parse_upload_file(file)
|
||||
if error:
|
||||
return Response({'error': error}, status=status.HTTP_400_BAD_REQUEST)
|
||||
owner = request.user if use_jwt_auth() and request.user.is_authenticated else None
|
||||
return Response(import_contacts_from_rows(rows, owner=owner))
|
||||
except Exception as e:
|
||||
return Response(
|
||||
{'error': f'Ошибка разбора файла: {e}'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user